Archived
docs(plan): 发车前扫描的三条裁定入计划(R1 测试桩前移 / R2 去类型逃逸 / R3 推帧改条件)
R1:TurnRunnerDeps 加 analyze 会让 turn-runner 测试脚手架的 deps 字面量少一个必填属性,
typecheck 在 Task 4 提交时就红 —— 桩前移到 Task 4,Task 7 那一步退化成验证。
R2:auto-source 的 matches() 在生产域代码里用了 as never,形参直接标 AutoExpr。
R3:showView 第二参不可选,传 undefined 编不过 —— 改成「声明了画面才推」。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -702,16 +702,21 @@ Expected: FAIL —— `Failed to resolve import "../internal/domain/auto-source.
|
||||
* 嵌套**两层封顶**:两层(某几颗料 × 该类型的参数字段)是已知业务的上限;
|
||||
* 超出就别 auto —— 退回模型,或者把那一段切出去。
|
||||
*/
|
||||
import type { AutoSource } from '@contracts';
|
||||
import type { AutoExpr, AutoSource } from '@contracts';
|
||||
import { evalExpr, rowsAt, type ExprCtx, type Row } from './auto-expr.ts';
|
||||
|
||||
export type ExpandedRow = { readonly row: Row; readonly outer?: Row };
|
||||
|
||||
/** where 是等值过滤;比值按字符串(台账里数字与文本混着存) */
|
||||
function matches(row: Row, where: Readonly<Record<string, unknown>> | undefined, ctx: ExprCtx, outer?: Row): boolean {
|
||||
function matches(
|
||||
row: Row,
|
||||
where: Readonly<Record<string, AutoExpr>> | undefined,
|
||||
ctx: ExprCtx,
|
||||
outer?: Row,
|
||||
): boolean {
|
||||
if (where === undefined) return true;
|
||||
return Object.entries(where).every(([column, valueExpr]) => {
|
||||
const want = evalExpr(valueExpr as never, { ...ctx, row, ...(outer === undefined ? {} : { outer }) });
|
||||
const want = evalExpr(valueExpr, { ...ctx, row, ...(outer === undefined ? {} : { outer }) });
|
||||
const got = row[column];
|
||||
return (got === null || got === undefined ? '' : String(got)) === (want === null || want === undefined ? '' : String(want));
|
||||
});
|
||||
@@ -816,11 +821,23 @@ export type AnalyzeGateway = {
|
||||
|
||||
以及在该文件顶部的 import 里补上 `AnalyzeGateway`(与 `AgentPort` 同一行 import)。
|
||||
|
||||
> 这一步会让 `apps/api/src/app/wiring/engines.ts` 装配处**类型报错**(少传一个依赖)——
|
||||
> 这是对的,Task 5 会补上。为了让本任务能独立跑测,先在 `engines.ts` 里传一个**当场抛错**的占位:
|
||||
> ```ts
|
||||
> analyze: { run: () => Promise.reject(new Error('analyze 尚未装配(Task 5)')) },
|
||||
> ```
|
||||
这一步会让**两个地方**少一个必填属性,两处都要在本任务里补上,否则 `typecheck` 当场红:
|
||||
|
||||
**① 装配层占位**(`apps/api/src/app/wiring/engines.ts`,Task 5 换成真实现):
|
||||
|
||||
```ts
|
||||
analyze: { run: () => Promise.reject(new Error('analyze 尚未装配(Task 5)')) },
|
||||
```
|
||||
|
||||
**② 测试脚手架的桩**(`apps/api/src/modules/loop-engine/__tests__/turn-runner-acceptance.test.ts`,
|
||||
在 `const deps: TurnRunnerDeps = {` 里、`agent: { … } as never,` 之后加一行):
|
||||
|
||||
```ts
|
||||
analyze: { run: async () => ({ object: {} }) } as never,
|
||||
```
|
||||
|
||||
补完先跑一遍确认既有用例没被碰坏:
|
||||
`pnpm exec vitest run apps/api/src/modules/loop-engine/__tests__/turn-runner-acceptance.test.ts`
|
||||
|
||||
- [ ] **Step 2: 写失败的测试**
|
||||
|
||||
@@ -2029,7 +2046,10 @@ Expected: 全绿(这一步只是提纯,行为一个字不变)
|
||||
/* 卡发出去了 = 这一步真动过世界 —— 标 worldTouched,自动收步那道门才诚实 */
|
||||
const touched = markWorldTouched(stateRuntime, current.item);
|
||||
await deps.state.save(taskId, model, parsed, touched);
|
||||
await showView(taskId, undefined, touched, current.view);
|
||||
/* 这一步声明了画面才推 —— `showView` 的第二参不可选,没声明就沿用上一帧(与既有行为一致) */
|
||||
if (current.view !== undefined && current.view !== '') {
|
||||
await showView(taskId, current.view, touched, current.view);
|
||||
}
|
||||
return { runtime: touched, enabled: stateEnabled, navigation: { kind: 'converged' }, status: 'awaiting' };
|
||||
}
|
||||
if (auto.wrote > 0 || auto.dropped > 0) {
|
||||
@@ -2056,7 +2076,7 @@ Expected: 全绿(这一步只是提纯,行为一个字不变)
|
||||
|
||||
> 三个你要照该文件既有写法对齐的名字:
|
||||
> - `markWorldTouched` —— 用 `grep -n "worldTouched" apps/api/src/modules/loop-engine/internal/domain/lifecycle.ts` 找那个把项标成「动过世界」的函数;它已存在(`human`/`decide`/`entry` 三条路都在用)。
|
||||
> - `showView` —— 该文件里推帧那一段用的函数,签名照它。
|
||||
> - `showView` —— 第 559 行定义,签名 `(taskId, template: string, runtime, preferred?)`;**第二参不可选**。
|
||||
> - `raiseAcceptance` —— 第 1500 行定义,签名 `(taskId, runtime, rationale, fields?)`。
|
||||
|
||||
- [ ] **Step 9: 在 `createTurnRunner` 里造出 `autoRunner`**
|
||||
@@ -2079,17 +2099,16 @@ Expected: 全绿(这一步只是提纯,行为一个字不变)
|
||||
import { createAutoRunner } from './auto-runner.ts';
|
||||
```
|
||||
|
||||
- [ ] **Step 10: 给 turn-runner 的测试脚手架补 `analyze` 端口**
|
||||
- [ ] **Step 10: 确认测试脚手架的 `analyze` 桩已经在**
|
||||
|
||||
`TurnRunnerDeps` 多了一个依赖,`turn-runner-acceptance.test.ts` 的 `harness()` 要跟上。
|
||||
在 `const deps: TurnRunnerDeps = {` 里,`agent: { … } as never,` 之后插入:
|
||||
Task 4 已经在 `turn-runner-acceptance.test.ts` 的 `const deps: TurnRunnerDeps = {` 里加过这一行:
|
||||
|
||||
```ts
|
||||
analyze: { run: async () => ({ object: {} }) } as never,
|
||||
```
|
||||
|
||||
Run: `pnpm exec vitest run apps/api/src/modules/loop-engine/__tests__/turn-runner-acceptance.test.ts`
|
||||
Expected: 现有用例全绿(这一步只是补一个没人调的端口)
|
||||
Run: `grep -n "analyze:" apps/api/src/modules/loop-engine/__tests__/turn-runner-acceptance.test.ts`
|
||||
Expected: 有这一行。**没有就现在补上** —— 少了它下面的用例连编译都过不去。
|
||||
|
||||
- [ ] **Step 11: 写接线的五个失败用例**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user