~/progetti/donumai/README.md
~/progetti/donumai/README.md

donumAI

{ 日期: “2026-08-08”, 状态: “开发中”, 技术栈模块: 14 }

软件交付的阶段引擎 —— 智能体负责撰写,但在你点头之前什么都不会往前走

donumAI 源于一个非常具体的不适:那些承诺「用 AI 写项目文档」的工具,产出的是看上去合理的文字,却没有任何办法判断它是否仍然成立。三月生成的架构文档就那样躺着,和第一天一样令人信服,而到了五月需求已经变了,却没有人察觉。donumAI 从相反的假设出发:难的不是生成,而是在项目一旦推进时,把六份彼此矛盾的文档维系在一起。产品是一台引擎,带着一个软件项目走过六个阶段——需求发现、估算、领域设计、架构、实施计划、测试计划——并在每一次交接时,把一个人放在智能体所写内容的面前。

## 六个阶段与关卡

每个阶段产出一份带版本的产物,由若干章节组成。智能体负责撰写,但并不负责收尾:评审是逐章节进行的,对每一节都可以批准、评论或要求修改。只有当某个阶段的关卡获得批准,下一个阶段才会解锁——这不是走形式,而是一把真正的锁。这正是「一个会产出的助手」与「一个会推进的流程」之间的区别。

·需求发现:最初的简报,通过分阶段进行的访谈收集
·估算:数字,以及这些数字所依赖的假设
·领域设计:业务领域的模型
·架构:已锁定的决策,以及被舍弃的备选方案
·实施计划:史诗、排序、里程碑、完成的定义
·测试计划:测试的策略与分布

## 当上游发生变化时

donumAI 背后的赌注既狭窄又机械:为每份文档留版本,记录其中每一部分来自哪个决策,并在某个输入发生变化的那一刻,把下游所有依赖它的内容标记为「已过期」。偏移并不会因此消失——它只是不再隐形,而这恰恰是问题的绝大部分。追溯链接不是由模型推断出来的:它们由数据投影而来,因为推断出的链接自带错误率,而从那一刻起整条链路都会背上这个错误率。

## 确定性规则先于评分量表

每份产物在抵达关卡之前都要经过一层校验。原则是:任何能够以类型化字段捕获的属性,都要脱离模型的判断,进入结构性规则——是否存在、基数、类型、是否属于某个封闭集合。结构性规则不消耗任何一次调用,不会在两次运行之间发生变化,也不会被写得漂亮的文字说服。只有剩下的部分——那些无法再化约的语义判断——才交给评分量表。

·仅架构阶段一项:154 条确定性规则,逐行从方法文档中提取而来
·三种严重级别,绝不合并为一种:block 会拦下关卡,repair 会触发一次自动修正循环,warn 会出现在关卡上但不阻断
·封闭枚举逐字照抄自原始文档,因为模型的既有知识常常已经过时
·对那些确实需要判断、但可以强制评审者列出依据的属性,给出强制性裁定

## Markdown 永远不是输入

这是支撑其余一切的不变式,也是我最看重的一项技术决定。Worker 不产出文本:它通过一次强制的工具调用产出类型化对象。规则运行在对象之上。Markdown 只是一个投影,由一个纯渲染器生成,下游没有任何组件会去读它。如果在这个项目里我发现自己在为散文写解析器或正则,那就说明方向走错了。抽取层是一种带错误率的推断,下游所有规则都会继承这个错误,也就不再是确定性的。甚至还有一个测试在 AST 上验证这条不变式——依据返回类型而不是函数名——并且通过识别出仍留在旧模块中的历史解析器,证明它并不是瞎的。

## 方法指南

每个阶段背后都有方法文档,而这些文档可以在产品内部直接阅读:一个学习板块,解释某份产物为何是现在这个样子、什么样的决策才谈得上可锁定、以及它将依据哪些标准被评判。它们用意大利语写成,而界面其余部分是英语,这让排版成了一个实实在在的问题:长篇意大利语散文的行宽和节奏,和一个高密度界面并不相同。

## 设计与视觉识别

界面是为那些一待就是几个小时的技术人员设计的:密集的表格、冗长的文档、关卡上的决策。密度和易读性优先于装饰。这套系统叫「Slate & Ferrous」,接近单色,只有一个蓝色强调色:近黑色标记主要操作,蓝色标记状态——这样一块摆满卡片的看板依然可读,而不必让颜色同时干两份活。

·OKLCH 配色,浅色与深色主题都是各自设计的,而不是彼此的反相
·字体:标题用 Bricolage Grotesque,界面用 Public Sans,标识符、版本号和指标用 JetBrains Mono——凡是等宽的,按定义就是机器产出的值
·自托管字体:公开页面上没有任何第三方请求,这也是能够诚实地写出一份无需横幅的 Cookie 政策的唯一方式
·标志是一个双层六边形,内环的六个顶点上各有一个实心节点:每个阶段一个

## 技术栈

项目是一个 TypeScript 单仓库,其中控制面与执行面被刻意分开:API 接收请求并持有状态,而由一个独立的 worker 执行可能持续数分钟的运行任务。

·控制面与引擎:NestJS 配合 Prisma 和 PostgreSQL
·执行面:基于 BullMQ 和 Redis 的独立 worker
·Web:React 19、Vite、TanStack Router、HeroUI、Tailwind CSS v4
·共享契约:独立包中的 Zod 模式,API 与客户端共用
·与供应商无关的大模型客户端,隔离在自己的包里
·使用 pnpm 管理的单仓库

donumAI 是一个个人项目,由我独自开发,并按照我给自己定下的顺序推进:先完成类型化的接线,再调用评审者并测量它的方差,然后才是其余部分。到目前为止写下的 154 条规则,在真正跑到实际生成的产物上之前,都还只是假设;而我真正在意的那张表有两列:从不触发的规则,和总是触发的规则。它们的诊断截然相反,必须放在一起看——一条总是触发的规则并不是校验层的成功,而是更上游某处需要修正的症状。

// 技术栈
[nestjs”, prisma”, postgres”, redis”, bullmq”, react”, vite”, tanstack-router”, typescript”, tailwind”, zod”, llm”, monorepo”, sdlc”, ]