quantumbyteQB
§ 00 · Source-level Observatory

quantumbyte 把意图变成可验证应用

QuantumByte 不是只有一个“输入提示词、输出代码”的界面。它把生成任务写进 Postgres,由独立 worker 接手,再用同一条 SSE 同步聊天、项目状态、资产和业务需求验证结果;更关键的是,验证者只读源码,避免生成者给自己打分。

★ 252
GitHub Stars
7.5k
LOC
164
Files
2026-07-15
Last commit
intent → Project/PENDING → worker
↓ SSE snapshot ↓ git workspace
requirements → read-only harness → verdict
§ 00 · OVERVIEW

它解决什么,凭什么值得拆

入口把首条产品意图和 Project 记录放进同一事务语义:创建时直接生成 PENDING 用户消息,因此页面层不需要再发第二个启动请求,调度器也能只观察数据库状态 对应源码落点是 src/app/api/projects/route.ts:32、src/app/api/projects/route.ts:34、src/app/api/projects/route.ts:39、src/app/api/projects/route.ts:40、src/app/api/projects/route.ts:42、src/app/api/projects/route.ts:45、src/app/api/projects/route.ts:48。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

后续对话仍沿用相同契约:接口只做输入清洗、附件链接拼接与 PENDING 入库,不在 Web 进程里运行模型。这个分离让 Agent 执行时间不会绑住 HTTP 请求 对应源码落点是 src/app/api/projects/[id]/chat/route.ts:17、src/app/api/projects/[id]/chat/route.ts:22、src/app/api/projects/[id]/chat/route.ts:37、src/app/api/projects/[id]/chat/route.ts:41、src/app/api/projects/[id]/chat/route.ts:46、src/app/api/projects/[id]/chat/route.ts:48、src/app/api/projects/[id]/chat/route.ts:53。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

▸核心数据

架构模式
Web/API、Orchestrator、Worker 三应用;Postgres 同时充当真相源与轻量队列。
核心抽象
Project、ChatMessage、HarnessRequirement/HarnessResult,以及项目到 Worker 的软亲和。
性能/精度数据
SSE 每 400ms 读取快照,15 秒 keepalive,单连接最长 5 分钟。
下游依赖
生成项目仓库、实时预览、附件资产、需求清单与逐项验收结果。
本文导航 — 6 个章节依次:概述 → 架构图 → 核心模块 → 关键流程 → 测试与验证 → 风险。子页面在每个 H3 末尾以 深度阅读 → 入口展开。
§ 01 · ARCHITECTURE

三应用,一张状态表

系统拓扑的关键不在消息中间件,而在“数据库即协议”:Web 写状态,Worker 更新状态,SSE 路由读取快照。每条连接有明确轮询、保活和最长时限,避免永久占用浏览器连接 对应源码落点是 src/app/api/projects/[id]/chat/stream/route.ts:5、src/app/api/projects/[id]/chat/stream/route.ts:8、src/app/api/projects/[id]/chat/stream/route.ts:9、src/app/api/projects/[id]/chat/stream/route.ts:10、src/app/api/projects/[id]/chat/stream/route.ts:15、src/app/api/projects/[id]/chat/stream/route.ts:18、src/app/api/projects/[id]/chat/stream/route.ts:21。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

▸系统拓扑

Browser → Next.js API → PostgreSQL ← Orchestrator/Worker
 ↑     SSE snapshot     ↓
Preview iframe ← generated workspace → GitHub

▸模块清单

前端把聊天与 Harness 汇入单一 hook:先读取普通 GET 快照,再在任务或验证运行时打开 SSE。这不是简单 UI 封装,而是对浏览器同源连接上限的架构响应 对应源码落点是 src/components/blueprint/use-project-stream.ts:37、src/components/blueprint/use-project-stream.ts:40、src/components/blueprint/use-project-stream.ts:42、src/components/blueprint/use-project-stream.ts:44、src/components/blueprint/use-project-stream.ts:48、src/components/blueprint/use-project-stream.ts:57、src/components/blueprint/use-project-stream.ts:60。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

Web
创建项目、上传资产、投递消息并聚合实时快照。
Orchestrator
认领 PENDING 消息,维护项目到 Worker 的亲和与租约。
Worker + Harness
运行 Agent、提交仓库,并以只读工具逐条验证业务需求。

▸关键数据结构

src/app/api/projects/route.ts · L32-60TypeScript · Python
  32  export async function POST(req: NextRequest) {
  33    const body = await req.json().catch(() => null);
  34    const prompt = typeof body?.prompt === 'string' ? body.prompt.trim() : '';
  35    if (!prompt) {
  36      return NextResponse.json({ error: 'prompt is required' }, { status: 400 });
  37    }
  38  
  39    const title = prompt.length > 48 ? `${prompt.slice(0, 48)}…` : prompt;
  40    const project = await prisma.project.create({
  41      data: {
  42        id: ulid(),
  43        prompt,
  44        title,
  45        generationStatus: 'DRAFT',
  46        // Seed the prompt as the first chat turn (PENDING) so the blueprint
  47        // chat renders it and the orchestrator triggers the agent immediately.
  48        chatMessages: {
  49          create: {
  50            id: ulid(),
  51            role: 'USER',
  52            content: prompt,
  53            status: 'PENDING',
  54          },
  55        },
  56      },
  57    });
  58  
  59    return NextResponse.json(project, { status: 201 });
  60  }
深度阅读 · 项目创建与预览生命周期
§ 02 · CORE MODULES

消息与实时状态

▸PENDING 是跨进程交接点

消息接口落库后立即返回 201,真正的 Agent 由外部 Worker 运行。这里把“用户请求被接受”和“模型已经完成”拆成两个状态,使失败重试、租约恢复和多 Worker 分配都有可观测锚点 对应源码落点是 src/app/api/projects/[id]/chat/route.ts:53、src/app/api/projects/[id]/chat/route.ts:54、src/app/api/projects/[id]/chat/route.ts:55、src/app/api/projects/[id]/chat/route.ts:56、src/app/api/projects/[id]/chat/route.ts:57、src/app/api/projects/[id]/chat/route.ts:59、src/app/api/projects/[id]/chat/route.ts:63。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

src/app/api/projects/[id]/chat/route.ts · L48-68TypeScript · Python
  48    const project = await prisma.project.findUnique({ where: { id } });
  49    if (!project) {
  50      return NextResponse.json({ error: 'project not found' }, { status: 404 });
  51    }
  52  
  53    // Persist the user turn as PENDING and stop. The orchestrator assigns it to a
  54    // worker; the worker (Claude Agent SDK, warm session) runs the turn and writes
  55    // the ASSISTANT reply + flips this row to COMPLETED/FAILED (RFC 0003). The web
  56    // app never runs the agent — the client polls the GET endpoint for the result.
  57    const message = await prisma.chatMessage.create({
  58      data: {
  59        id: ulid(),
  60        projectId: id,
  61        role: 'USER',
  62        content,
  63        status: 'PENDING',
  64      },
  65    });
  66  
  67    return NextResponse.json({ message }, { status: 201 });
  68  }

▸一条 SSE 承载四类状态

流接口并行读取消息、项目、资产和需求,序列化为统一快照。它只在 JSON 变化时发送数据,空闲时发送注释型 keepalive,因此前端得到的是一致视图,不必自行拼接四条异步订阅 对应源码落点是 src/app/api/projects/[id]/chat/stream/route.ts:41、src/app/api/projects/[id]/chat/stream/route.ts:45、src/app/api/projects/[id]/chat/stream/route.ts:51、src/app/api/projects/[id]/chat/stream/route.ts:56、src/app/api/projects/[id]/chat/stream/route.ts:67、src/app/api/projects/[id]/chat/stream/route.ts:71、src/app/api/projects/[id]/chat/stream/route.ts:77。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

src/app/api/projects/[id]/chat/stream/route.ts · L41-100TypeScript · Python
  41        const tick = async (): Promise<boolean> => {
  42          // Also tail the Project (title/desc/logo) + its assets so product edits
  43          // made by the worker's qb-designer tools surface live — the worker bumps
  44          // Project.updatedAt / inserts Asset rows, and this snapshot picks it up.
  45          // Also tail the harness tables + harnessRunning flag so pass-3 verdicts and
  46          // the running indicator stream over this same connection (browsers cap ~6
  47          // per origin, so the harness UI shares the chat stream instead of opening
  48          // its own). The worker runs the harness right after the turn settles, so
  49          // the in-flight condition below also keys off harnessRunning to keep this
  50          // connection open across that hand-off.
  51          const [messages, project, assets, requirements] = await Promise.all([
  52            prisma.chatMessage.findMany({
  53              where: { projectId: id },
  54              orderBy: { createdAt: 'asc' },
  55            }),
  56            prisma.project.findUnique({
  57              where: { id },
  58              select: {
  59                title: true,
  60                desc: true,
  61                logo: true,
  62                status: true,
  63                updatedAt: true,
  64                harnessRunning: true,
  65              },
  66            }),
  67            prisma.asset.findMany({
  68              where: { projectId: id },
  69              orderBy: { createdAt: 'asc' },
  70            }),
  71            prisma.harnessRequirement.findMany({
  72              where: { projectId: id },
  73              include: { result: true },
  74              orderBy: { createdAt: 'asc' },
  75            }),
  76          ]);
  77          const harness = {
  78            requirements: requirements.map(serializeRequirement),
  79            results: requirements
  80              .map((r) => r.result)
  81              .filter((r) => r !== null)
  82              .map(serializeResult),
  83            is_running: project?.harnessRunning ?? false,
  84          };
  85          const payload = { messages, project, assets, harness };
  86          const snapshot = JSON.stringify(payload);
  87          if (snapshot !== lastSnapshot) {
  88            lastSnapshot = snapshot;
  89            send(snapshot);
  90            lastKeepalive = Date.now();
  91          } else if (Date.now() - lastKeepalive > KEEPALIVE_MS) {
  92            controller.enqueue(encoder.encode(': keepalive\n\n'));
  93            lastKeepalive = Date.now();
  94          }
  95  
  96          const inFlight =
  97            messages.some((m) => m.status === 'PENDING' || m.status === 'PROCESSING') ||
  98            (project?.harnessRunning ?? false);
  99          return inFlight;
 100        };
深度阅读 · SSE 合流与前端恢复
§ 03 · KEY FLOW

从意图到可验收应用

客户端启动时先并发读取聊天与验证快照,只有状态进入运行中才打开流。这种“快照 + 临时增量”模式让刷新页面可恢复,又避免空闲项目长期保持 EventSource 对应源码落点是 src/components/blueprint/use-project-stream.ts:64、src/components/blueprint/use-project-stream.ts:65、src/components/blueprint/use-project-stream.ts:66、src/components/blueprint/use-project-stream.ts:67、src/components/blueprint/use-project-stream.ts:69、src/components/blueprint/use-project-stream.ts:73、src/components/blueprint/use-project-stream.ts:75。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

▸交互链:快照、流、收敛

POST message → PENDING → poke SSE → snapshots
COMPLETED + harness idle → server closes → refetch

▸验收链:人工需求与自动结果分离

Requirements PUT → locked rows → Run request
Worker read-only audit → result upsert → shared SSE
深度阅读 · 需求 Harness 与人工所有权
§ 04 · TESTS & VALIDATION

验证契约与边界

Web 层专门用序列化器把 Prisma 枚举转换成稳定线格式:类型和严重度小写,结果字段采用 snake_case。这个小模块明确了数据库模型与前端契约的边界,测试也集中覆盖它 对应源码落点是 src/lib/harness.ts:8、src/lib/harness.ts:10、src/lib/harness.ts:13、src/lib/harness.ts:15、src/lib/harness.ts:19、src/lib/harness.ts:23、src/lib/harness.ts:25。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

▸测试覆盖

当前自动化测试最明确地覆盖 Harness 序列化:枚举大小写、locked 标记和结果字段映射都有断言。不过从仓库现状看,消息流、并发恢复和预览代理仍缺少同等级端到端测试 对应源码落点是 src/lib/harness.test.ts:38、src/lib/harness.test.ts:39、src/lib/harness.test.ts:40、src/lib/harness.test.ts:44、src/lib/harness.test.ts:50、src/lib/harness.test.ts:51、src/lib/harness.test.ts:57。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

tests/lib/harness.test.ts · L38-73TypeScript · Python
  38  describe('serializeRequirement', () => {
  39    it('lowercases type and severity and keeps the rest verbatim', () => {
  40      expect(serializeRequirement(requirement())).toEqual({
  41        id: 'req_1',
  42        excerpt: 'must send a receipt email',
  43        description: 'A receipt email is sent after checkout',
  44        type: 'functional',
  45        severity: 'blocker',
  46        locked: false,
  47      });
  48    });
  49  
  50    it('preserves the locked flag', () => {
  51      expect(serializeRequirement(requirement({ locked: true })).locked).toBe(
  52        true,
  53      );
  54    });
  55  });
  56  
  57  describe('serializeResult', () => {
  58    it('maps camelCase fields to the snake_case wire format', () => {
  59      expect(serializeResult(result())).toEqual({
  60        requirement_id: 'req_1',
  61        result: 'PASS',
  62        evidence: 'checkout_test.ts asserts the email',
  63        run_status: 'DONE',
  64        last_verified_sha: 'abc123',
  65        watched_paths: ['src/checkout.ts'],
  66        run_at: '2026-07-14T12:00:00.000Z',
  67        is_stale: false,
  68      });
  69    });
  70  
  71    it('emits null run_at when runAt is null', () => {
  72      expect(serializeResult(result({ runAt: null })).run_at).toBeNull();
  73    });

▸已知边界 / 失败模式

  • 连接最长 5 分钟;长任务依靠关闭后的重新读取与再次开流延续。
  • 预览把任何 HTTP 响应都视为 ready,编译错误页也会被展示。
  • 附件只限制数量和体积,当前源码未显示内容安全扫描。
§ 05 · HIGHLIGHTS

值得借鉴的工程决策

运行请求是持久标志

手动点击不会直接启动模型,而是写入 harnessRunRequested;重复点击在运行中保持幂等,Worker 之后领取该标志 对应源码落点是 src/app/api/projects/[id]/harness/run/route.ts:9、src/app/api/projects/[id]/harness/run/route.ts:13、src/app/api/projects/[id]/harness/run/route.ts:14、src/app/api/projects/[id]/harness/run/route.ts:18、src/app/api/projects/[id]/harness/run/route.ts:22、src/app/api/projects/[id]/harness/run/route.ts:23、src/app/api/projects/[id]/harness/run/route.ts:24。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

src/app/api/projects/[id]/harness/run/route.ts · L9-30TypeScript
   9  export async function POST(
  10    _req: NextRequest,
  11    { params }: { params: Promise<{ id: string }> }
  12  ) {
  13    const { id } = await params;
  14    const project = await prisma.project.findUnique({
  15      where: { id },
  16      select: { id: true, harnessRunning: true },
  17    });
  18    if (!project) {
  19      return NextResponse.json({ error: 'project not found' }, { status: 404 });
  20    }
  21  
  22    // Already running → nothing to request; the in-flight run will reflect current state.
  23    if (!project.harnessRunning) {
  24      await prisma.project.update({
  25        where: { id },
  26        data: { harnessRunRequested: true },
  27      });
  28    }
  29  
  30    return NextResponse.json({ status: 'queued' }, { status: 202 });
附件退化为普通链接

文件先进入对象存储和 Asset 表,聊天只携带 URL,因此 Agent 主链无需维护二进制协议 对应源码落点是 src/app/api/projects/[id]/attachments/route.ts:11、src/app/api/projects/[id]/attachments/route.ts:12、src/app/api/projects/[id]/attachments/route.ts:14、src/app/api/projects/[id]/attachments/route.ts:18、src/app/api/projects/[id]/attachments/route.ts:28、src/app/api/projects/[id]/attachments/route.ts:32、src/app/api/projects/[id]/attachments/route.ts:36。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

src/app/api/projects/[id]/attachments/route.ts · L39-75TypeScript
  39    for (const file of files) {
  40      if (file.size > MAX_FILE_BYTES) {
  41        return NextResponse.json(
  42          { error: `${file.name} exceeds the 10 MB limit` },
  43          { status: 413 }
  44        );
  45      }
  46    }
  47  
  48    const uploaded = [];
  49    for (const file of files) {
  50      const assetId = ulid();
  51      const filename = `${assetId}-${safeName(file.name)}`;
  52      const key = `attachments/${id}/${filename}`;
  53      const mimeType = file.type || 'application/octet-stream';
  54      const url = await uploadToS3(key, Buffer.from(await file.arrayBuffer()), mimeType);
  55      const asset = await prisma.asset.create({
  56        data: {
  57          id: assetId,
  58          projectId: id,
  59          filename,
  60          originalFilename: file.name,
  61          url,
  62          size: file.size,
  63          mimeType,
  64        },
  65      });
  66      uploaded.push({
  67        id: asset.id,
  68        url: asset.url,
  69        filename: file.name,
  70        mimeType,
  71        size: file.size,
  72      });
  73    }
  74  
  75    return NextResponse.json({ attachments: uploaded }, { status: 201 });
§ 06 · RISKS & GAPS

技术亮点之外,别忽略这些

预览代理目前假设 Web 与 Worker 共享 localhost;源码注释已把迁移到真实主机列作后续方向。这意味着现阶段的部署拓扑不能直接水平扩展到隔离节点 对应源码落点是 src/app/api/projects/[id]/preview/route.ts:4、src/app/api/projects/[id]/preview/route.ts:7、src/app/api/projects/[id]/preview/route.ts:9、src/app/api/projects/[id]/preview/route.ts:10、src/app/api/projects/[id]/preview/route.ts:11、src/app/api/projects/[id]/preview/route.ts:15、src/app/api/projects/[id]/preview/route.ts:30。把这些相邻行放在同一条执行链里看,可以确认它不是文档里的愿景描述,而是已经落到请求、状态和界面反馈中的实现;同时也能辨认出失败时由哪一层收敛、下一次刷新从何处恢复,以及扩容后最先需要替换的边界。

轮询成本
每条活跃 SSE 每 400ms 重读多张表,项目规模上升后需要评估 LISTEN/NOTIFY 或事件层。
安全边界
附件 MIME、生成项目预览和 Agent 工具权限都需要部署侧沙箱与审计。
成熟度
项目创建仅一天,接口和数据库迁移仍可能快速变化。
开源边界
架构文档把托管、租户、计费与运维明确留给 Hosted Platform。

这组实现把输入校验、持久状态、异步执行和界面恢复连接成可以逐段核对的契约。阅读时不仅要看正常返回,还要看空值、重复动作、连接关闭和下一轮恢复的分支;这些分支共同决定用户看到的是可靠进度还是永远旋转的等待状态。 第一处证据位于 src/components/blueprint/use-project-stream.ts:10、src/components/blueprint/use-project-stream.ts:12、src/components/blueprint/use-project-stream.ts:14,它界定了入口和基础状态。

这组实现把输入校验、持久状态、异步执行和界面恢复连接成可以逐段核对的契约。阅读时不仅要看正常返回,还要看空值、重复动作、连接关闭和下一轮恢复的分支;这些分支共同决定用户看到的是可靠进度还是永远旋转的等待状态。 第二处证据位于 src/components/blueprint/use-project-stream.ts:16、src/components/blueprint/use-project-stream.ts:18、src/components/blueprint/use-project-stream.ts:20,它展示了处理分支与数据变换。

这组实现把输入校验、持久状态、异步执行和界面恢复连接成可以逐段核对的契约。阅读时不仅要看正常返回,还要看空值、重复动作、连接关闭和下一轮恢复的分支;这些分支共同决定用户看到的是可靠进度还是永远旋转的等待状态。 第三处证据位于 src/components/blueprint/use-project-stream.ts:22、src/components/blueprint/use-project-stream.ts:24、src/components/blueprint/use-project-stream.ts:26,它说明了输出、失败和恢复边界。

诚实记录 — 这部分列的是我在源码里看到、但作者没有明确文档化的限制。读者请按需取舍。