@tea-agent/loop-agent 0.38.0 → 0.39.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -118,6 +118,17 @@ export const STRUCTURED_REQUIRED_PI_RETRY_POLICY = {
118
118
  ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
119
119
  retryCategories: [...STRUCTURED_REQUIRED_DAG_RETRY_CATEGORIES],
120
120
  };
121
+ /**
122
+ * Planner read-only nodes (standard DAG contract-pi / plan-pi) may retry with
123
+ * a compact-output instruction when the model produced an oversized assistant
124
+ * response. Only `output-too-large` is added on top of the default set;
125
+ * report/review and other read-only roles keep the default set so they do not
126
+ * silently learn new output semantics.
127
+ */
128
+ export const PLANNER_OUTPUT_LIMIT_RETRY_POLICY = {
129
+ ...DEFAULT_READ_ONLY_PI_RETRY_POLICY,
130
+ retryCategories: [...DEFAULT_DAG_RETRY_CATEGORIES, STRUCTURED_OUTPUT_RETRY_CATEGORY],
131
+ };
121
132
  /**
122
133
  * Default retry for reviewer / recovery nodes that declare outputProtocol.
123
134
  * Includes protocol-invalid so missing VERDICT lines are corrected in-node.
@@ -171,6 +171,9 @@ export const dagJsonArtifactGateSchema = z.object({
171
171
  const dagFrontendNodeIdSchema = z
172
172
  .string()
173
173
  .regex(/^[a-z][a-z0-9-]*$/, "frontend node id must be kebab-case");
174
+ const openspecCandidatePathSchema = z
175
+ .string()
176
+ .refine((candidate) => !candidate.includes("\\") && isOpenspecSpecFilePath(candidate), "openspec candidate must be a repo-relative supported file under openspec/schemas/, openspec/project-specs/, or ai_workspace/");
174
177
  export const dagFrontendPrewriteGateSchema = z.object({
175
178
  schemaVersion: z.literal(1),
176
179
  planFromNodeId: dagFrontendNodeIdSchema,
@@ -189,11 +192,25 @@ export const dagFrontendPrewriteGateSchema = z.object({
189
192
  outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
190
193
  requireSourceFreshness: z.literal(true),
191
194
  implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
192
- openspecCandidatePaths: z
193
- .array(z
194
- .string()
195
- .refine((candidate) => !candidate.includes("\\") && isOpenspecSpecFilePath(candidate), "openspec candidate must be a repo-relative supported file under openspec/schemas/ or openspec/project-specs/"))
196
- .default([]),
195
+ openspecCandidatePaths: z.array(openspecCandidatePathSchema).default([]),
196
+ /**
197
+ * OpenSpec gate policy. Omitted on legacy DAGs: the runtime treats
198
+ * undefined as scan-strict so pre-existing full-read semantics are
199
+ * preserved. Generation always writes it explicitly for new DAGs.
200
+ */
201
+ openspecPolicy: z.enum(["cited", "scan-strict"]).optional(),
202
+ /**
203
+ * Generation-frozen candidate source metadata (diagnostic). cited mode
204
+ * candidates are declared ∪ taskSourceCited; scan-strict candidates are
205
+ * scanStrict (auto-discovered designEvidence.normativePaths).
206
+ */
207
+ openspecCandidateSources: z
208
+ .object({
209
+ declared: z.array(openspecCandidatePathSchema).default([]),
210
+ taskSourceCited: z.array(openspecCandidatePathSchema).default([]),
211
+ scanStrict: z.array(openspecCandidatePathSchema).default([]),
212
+ })
213
+ .optional(),
197
214
  });
198
215
  export const dagFrontendLintBaselineSchema = z
199
216
  .object({
@@ -822,6 +839,8 @@ export const dagSpecSchema = z
822
839
  defaults: dagDefaultsSchema,
823
840
  skillsByRole: z.record(z.string(), z.array(z.string())).optional(),
824
841
  executorModels: dagExecutorModelsSchema.optional(),
842
+ /** Generation-time advisories surfaced as machine-visible warnings. */
843
+ advisories: z.array(z.string()).optional(),
825
844
  tasks: z.array(dagTaskSchema).min(1),
826
845
  })
827
846
  .superRefine((spec, ctx) => {
@@ -14,6 +14,7 @@ const GOVERNANCE_WARNING_TYPES = new Set([
14
14
  "shell-verdict-gate-multi-command-state",
15
15
  "shell-verdict-gate-handwritten-inline",
16
16
  "orphan-writer-source-binding",
17
+ "advisory",
17
18
  ]);
18
19
  const ROOT_ARTIFACT_PATH_PROBES = [
19
20
  "artifacts/修改记录.md",
@@ -32,10 +33,11 @@ export function collectGovernanceWarnings(issues) {
32
33
  return issues.filter(isGovernanceWarning);
33
34
  }
34
35
  function collectBlockingIssues(issues, options = {}) {
36
+ const nonAdvisory = issues.filter((issue) => issue.type !== "advisory");
35
37
  if (options.strictGovernance) {
36
- return [...issues];
38
+ return nonAdvisory;
37
39
  }
38
- return issues.filter((issue) => !isGovernanceWarning(issue));
40
+ return nonAdvisory.filter((issue) => !isGovernanceWarning(issue));
39
41
  }
40
42
  function normalizePath(value) {
41
43
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
@@ -956,6 +958,9 @@ export function validateDagSpec(spec) {
956
958
  validateSameRankWriteSetConflicts(spec, ranks, issues);
957
959
  validateSameRankAgentAttributionRisks(spec, ranks, issues);
958
960
  validateWriterSourceBinding(spec, issues);
961
+ for (const advisory of spec.advisories ?? []) {
962
+ issues.push({ type: "advisory", message: advisory });
963
+ }
959
964
  return issues;
960
965
  }
961
966
  function validateProjectGovernanceTaskConfig(task, spec, issues) {
@@ -1,42 +1,68 @@
1
1
  # 前端设计契约模板
2
2
 
3
+ 本模板只承载**通用编写协议 + 三要素 + fail-closed 规则**,不包含任何具体业务组件名、项目专属路径或项目专属命令。设计契约在进入实现前由 `frontend-plan-pi` 落入结构化 implementation contract,`frontend-design-review-pi` 按本模板逐节审查;每一节都要给出「**要写什么** / **DAG 如何消费** / **缺失/冲突时 fail-closed**」三要素。
4
+
3
5
  ## 页面目标
4
6
 
5
- TODO
7
+ - **要写什么**:写清每个目标页面的职责、用户到达该页面的目的与成功结果,逐页列出,与需求.md 的用户目标/目标页面一一对应。
8
+ - **DAG 如何消费**:`frontend-plan-pi` 据它确定 `targets.routes` 与每页的实现步骤,`frontend-design-review-pi` 据它核对范围与需求一致性。
9
+ - **缺失/冲突时 fail-closed**:页面目标与需求不一致、缺页或目标含糊(无成功结果)时阻塞,不得自行补页或改目标。
6
10
 
7
11
  ## 信息结构
8
12
 
9
- TODO
13
+ - **要写什么**:写清每页展示的信息层级与数据来源(字段、分组、优先级、空/错误占位),接口数据与静态文案分开写。
14
+ - **DAG 如何消费**:`frontend-plan-pi` 据它安排组件数据流与状态展示,`frontend-design-review-pi` 据它核对接口字段映射。
15
+ - **缺失/冲突时 fail-closed**:信息层级缺失、数据来源不可追溯、与接口字段冲突时阻塞,不得臆造字段或层级。
10
16
 
11
17
  ## 组件拆分
12
18
 
13
- TODO
19
+ - **要写什么**:把页面拆成组件树,逐组件写职责、复用边界、props/状态归属与可替换点;复用现有设计系统组件时引用其来源路径。
20
+ - **DAG 如何消费**:`frontend-plan-pi` 据它确定 `targets.files` 与实现顺序,`frontend-design-review-pi` 据它审查拆分是否越界/重复。
21
+ - **缺失/冲突时 fail-closed**:组件边界不清、同一职责被拆到多处、或与设计规范冲突时阻塞,不得按邻近代码自行决定复用。
14
22
 
15
23
  ## 交互规则
16
24
 
17
- TODO
25
+ - **要写什么**:逐条写清每个交互的触发、预期行为、状态变化与可恢复性(对应需求.md 的交互要求)。
26
+ - **DAG 如何消费**:`frontend-plan-pi` 据它填充 `interactions[]`(`trigger` + `expectedBehavior`),verify 用行为命令断言。
27
+ - **缺失/冲突时 fail-closed**:交互无预期行为、与 UI 状态冲突、或不可自动化断言时阻塞。
18
28
 
19
29
  ## UI 状态
20
30
 
21
- TODO
31
+ - **要写什么**:逐状态(loading/empty/error/success/disabled)写清触发条件、展示内容与转移;不适用状态写 `N/A` + 非空理由。
32
+ - **DAG 如何消费**:进入 implementation contract 的 `uiStates[]`,verify 逐状态断言,`frontend-review-pi` 据它核对实现是否遗漏状态。
33
+ - **缺失/冲突时 fail-closed**:适用状态缺失预期、N/A 无理由、或状态展示与交互/接口状态冲突时阻塞。
22
34
 
23
35
  ## Mock / API 策略
24
36
 
25
- - 接口文档或 schema:TODO
26
- - 策略:`native | browser-intercept | request-adapter | not-needed | blocked`
27
- - endpoint / fixture / UI 状态映射:TODO
28
- - 显式启用方式与 production 默认关闭边界:TODO
29
- - DAG 已固化的验证入口:TODO
30
- - Real Integration Gap 与后端就绪后的复验:TODO
37
+ 策略枚举固定为 `native | browser-intercept | request-adapter | not-needed | blocked`,与 runtime Mock 语义一致:`blocked` 永远不通过(缺少/冲突接口契约、路径或依赖未授权、无法证明生产默认关闭、固定验证入口无法覆盖、或唯一方案是注释真实请求时必须选它);`not-needed` 必须有后端可用或任务不涉及远程接口的真实/无远程证据,且固定 behavior 入口能覆盖相应行为;显式 `policy=required` 不接受 `not-needed`。契约只接受当前 policy 允许的非 `blocked` 首行。
38
+
39
+ - **接口文档或 schema — 要写什么**:引用接口文档/schema 路径与版本,作为 fixture 与字段映射的唯一来源。**DAG 如何消费**:`frontend-plan-pi` 据它冻结 endpoint/fixture 映射,prewrite gate 校验 fixture 可追溯。**缺失/冲突时 fail-closed**:无文档/schema、字段冲突时选 `blocked`,不得自行发明接口。
40
+ - **策略**:只写枚举中的一个值:`native | browser-intercept | request-adapter | not-needed | blocked`。
41
+ - **endpoint / fixture / UI 状态映射 — 要写什么**:逐 endpoint 列 method、path、fixture 路径与消费组件,并映射到 UI 状态。**DAG 如何消费**:写入 `mockApi.endpoints[]`(method/path/fixture/consumer),非 `not-needed` 策略要求每个 endpoint 都有 fixture 与 consumer。**缺失/冲突时 fail-closed**:非 `not-needed` 却缺 fixture/consumer、endpoint 与 UI 状态映射不一致时阻塞。
42
+ - **显式启用方式与 production 默认关闭边界 — 要写什么**:写清真实请求为默认路径、Mock 仅通过显式开关(环境变量/构建开关)启用的具体边界。**DAG 如何消费**:`productionDefaultOff` 恒为 `true`,`frontend-review-pi` 检查是否注释真实请求或默认开启 Mock。**缺失/冲突时 fail-closed**:无法证明生产默认关闭、或 Mock 会进入生产入口时选 `blocked`。
43
+ - **DAG 已固化的验证入口 — 要写什么**:引用 DAG 生成时已冻结的 static/behavior/Mock 命令 label,不发明新命令。**DAG 如何消费**:`verificationTarget.commandLabel` 必须逐字落在冻结命令集合内,否则 contract 物化 `invalid-output`。**缺失/冲突时 fail-closed**:策略需要 Mock 验证命令却没有冻结命令时生成期 fail-closed(`no authorized Mock verification commands`)。
44
+ - **Real Integration Gap 与后端就绪后的复验 — 要写什么**:写清当前未联通的真实集成缺口,以及后端就绪后的复验路径。**DAG 如何消费**:进入 `evidenceGaps[]`,closeout 报告 `Real integration: pending`,复验任务 `<task-id>-real-api-integration-verify` 由操作者显式触发。**缺失/冲突时 fail-closed**:把 Mock 证据当真实联调证据时 review 拒绝。
31
45
 
32
46
  ## 样式与设计系统映射
33
47
 
34
- TODO
48
+ - **要写什么**:写清页面用到的主题/Token、设计系统组件与样式来源(引用具体来源路径),自定义样式与复用样式的边界。
49
+ - **DAG 如何消费**:`frontend-plan-pi` 据它安排样式实现,`frontend-design-review-pi` 据它核对是否与设计系统冲突。
50
+ - **缺失/冲突时 fail-closed**:样式来源缺失、Token/组件冲突、或自定义样式会破坏设计系统时阻塞。
35
51
 
36
52
  ## 响应式范围
37
53
 
38
- TODO
54
+ - **要写什么**:写清支持的视口范围(desktop/mobile/tablet)与断点、每个断点下的布局差异。
55
+ - **DAG 如何消费**:进入 contract 的 target runtime environment,`frontend-plan-pi` 据它决定响应式策略。
56
+ - **缺失/冲突时 fail-closed**:视口范围与需求运行环境不一致、或断点未覆盖声明环境时阻塞。
39
57
 
40
58
  ## 风险与非目标
41
59
 
42
- TODO
60
+ - **要写什么**:写清设计上的已知风险、依赖缺口与明确不做/排除的设计范围。
61
+ - **DAG 如何消费**:进入 `evidenceGaps[]` 与 Non-goals,`frontend-review-pi`/closeout 据它保留风险与后续项。
62
+ - **缺失/冲突时 fail-closed**:范围与需求非目标冲突、或风险被隐藏时阻塞。
63
+
64
+ ## OpenSpec 引用块(openspec-citations)
65
+
66
+ - **要写什么**:在 fenced `json` 契约块之后追加**恰好一个** fenced `openspec-citations` 块,每行一个 JSON `{"path","section","line"}`(`section` 可空串、`line` 为 int 或 null),逐条列出本计划实际读取并应用的每个 openspec 规范文件。
67
+ - **DAG 如何消费**:`frontend-prewrite-gate-shell` 按 fence 语言标签解析该块,并与生效 plan/review 节点的成功 read 事件核验;`cited` 模式下候选未引用 → `openspec-not-cited`,引用无 read 背书 → `openspec-citation-not-read`,块缺失/不可解析 → `openspec-citation-block-unparseable`。
68
+ - **缺失/冲突时 fail-closed**:候选非空而引用块缺失/不可解析、候选未引用、或引用未真实读取时,prewrite gate 以 `retryable-invalid` fail-closed;不要引用未读取的路径,也不要遗漏已读取的 openspec 文件。
@@ -1,35 +1,62 @@
1
1
  # 前端任务执行约束模板
2
2
 
3
+ 本模板只承载**通用探索协议 + 结果 schema + 缺失/冲突时 fail-closed 规则**,不包含任何具体业务组件名、项目专属路径或项目专属命令。项目事实由生成期能力探测(`openspec/schemas/**`、`openspec/project-specs/**`、`ai_workspace/**`)产出并经语义归类注入任务契约。
4
+
3
5
  ## 技术约束
4
6
 
5
- TODO
7
+ - **要探索什么**:项目 `package.json` 的直接依赖与 scripts、框架/路由/状态/数据获取/组件库/样式/测试/构建相关配置与引导文件,以及 `openspec/schemas/**` 与 `openspec/project-specs/rules/**` 中声明技术边界的规范。
8
+ - **结果必须包含**:每一条适用技术规则的源路径、命中章节与行号;被排除依赖的明确理由;与既有技术栈一致的运行时要求(编译、构建、入口、环境变量)。
9
+ - **缺失/冲突时如何 fail-closed**:无法找到技术规范来源时,显式标注 `source: unavailable` 并只采用任务源与现有代码的可验证事实;发现冲突规则时不臆造取舍,先记录冲突并返回 `request-revision`/blocked,而不是静默采用邻近代码惯例。
6
10
 
7
11
  ## 代码风格约束
8
12
 
9
- TODO
13
+ - **要探索什么**:`openspec/project-specs/templates/**`(代码模板)与 `openspec/project-specs/rules/**` 中组件/钩子/工具等代码风格规则,以及现有组件、主题/Token、stories 与测试作为较弱 repository fallback。
14
+ - **结果必须包含**:命中模板/规则的源路径、章节与行号;命名、目录、导出、类型、样式组织等可执行约定;冲突字段逐条列出。
15
+ - **缺失/冲突时如何 fail-closed**:没有模板/规则命中时明确说明并记录 `repository fallback` 证据;冲突未解决时不得进入实现,必须返回阻塞信息。
10
16
 
11
17
  ## 设计约束
12
18
 
13
- TODO
19
+ - **要探索什么**:`openspec/project-specs/ui/**`(主题/组件目录)与 `openspec/project-specs/rules/**` 中页面/组件拆分、交互、UI 状态与响应式相关规则。
20
+ - **结果必须包含**:命中设计规范/组件目录的源路径、章节与行号;组件拆分与复用边界、样式与设计系统映射、UI 状态与响应式范围。
21
+ - **缺失/冲突时如何 fail-closed**:设计规范缺失时明确记录来源为 `unavailable` 并保留为已知风险;组件/主题冲突时必须阻断,不能按邻近代码自行决定。
14
22
 
15
23
  ## 验证约束
16
24
 
17
- TODO
25
+ - **要探索什么**:项目 `package.json` scripts 中真实存在的 lint/typecheck/build/test 命令,以及任务源 `需求.md`/`执行约束.md` 中声明的验证命令。
26
+ - **结果必须包含**:命令的 label、原始命令文本与来源(scripts/任务源);每条命令可自终止(启动→断言→退出)的说明。
27
+ - **缺失/冲突时如何 fail-closed**:只在确定命令真实存在时写入冻结命令集;不发明 shell 命令。命令不可执行或漂移时 fail closed,不宣称验证通过。
18
28
 
19
29
  ## Mock 约束(数据型任务)
20
30
 
21
- - Mock/API/schema 规范路径:TODO
22
- - 既有 Mock service root、handler/fixture/bootstrap:TODO
23
- - 既有 browser/e2e interception request adapter/DI seam:TODO
24
- - 启动、健康检查和专项验证命令:TODO
25
- - production 禁用边界:TODO
26
- - 真实请求默认路径与 Mock 显式启用方式:TODO
27
- - `task.json.frontendMock.policy`:`auto | required | disabled`
31
+ 本段决定生成期 `allowedMockStrategies` 与冻结的 Mock 验证命令。只有当生成期**能证明任务确实包含 Mock 且有确定性验证命令**时,DAG 才会放行 `native` / `browser-intercept` / `request-adapter`;否则自动收敛为 `not-needed`,design review 若按项目规范改选 `native`,会在 prewrite 被 `mock-strategy-outside-allowed` 拦截、`implement` 被跳过。
32
+
33
+ - **何时必须填**:项目规范(`openspec/project-specs/**`、`ai_workspace/**`、mock 规则或类似 DEC-* 决策)要求/建议 Mock;或需求涉及远端接口且后端未就绪。规范要求 Mock 时,优先 `task.json.frontendMock.policy: "required"`。
34
+ - **命令要探索、不要硬编码**:到项目 `package.json` scripts 里找实际存在的 Mock 相关脚本(如 `mock`、`mock:*`、`dev:mock`,或名字含 mock 的脚本),结合既有 service root 的 handler/fixture/bootstrap 启动方式,确定一条**真实存在、确定、可自终止**(启动→断言→退出 0)的验证命令。常驻 dev server 必须包装成自终止脚本(start→assert→stop),否则 verify shell 会超时 fail-closed。
35
+ - **写入 task.json**:把探索到的命令原样写进 `frontendMock.verifyCommands`(`label` 唯一、`command` 与项目脚本一致)。`label` 会进入冻结命令集,implementation contract 的 `verificationTarget.commandLabel` 必须逐字引用它。
36
+ - **命令来源白名单**:只允许项目 `package.json` 已有脚本、Mock capability seed、或本段声明的 `verifyCommands`;plan/design 阶段不能发明 shell 命令。
37
+ - **Mock/API/schema 规范路径**:到 `openspec/project-specs/**`(rules 中的 mock/api 规则、templates 中的接口模板)与 `ai_workspace/**` 检索;结果必须列出源路径、章节与行号。缺失或冲突时 fail closed,不自行发明接口契约。
38
+ - **既有 Mock service root、handler/fixture/bootstrap**:检索项目现有 Mock 服务根目录、handler、fixture 与 bootstrap 启动方式;结果必须列出发现路径,未发现时明确记录 `unavailable`。
39
+ - **既有 browser/e2e interception 或 request adapter/DI seam**:检索项目现有浏览器/e2e 拦截或请求适配层/依赖注入 seam;结果必须列出发现路径或明确记录缺失。
40
+ - **production 禁用边界**:结果必须说明真实请求为默认路径、Mock 仅通过显式测试/开发开关启用的具体边界;无法证明生产默认关闭时返回 `blocked`。
41
+ - **真实请求默认路径与 Mock 显式启用方式**:结果必须写出真实请求默认路径与 Mock 显式启用方式(如环境变量/构建开关),不得通过注释真实请求或在生产组件内硬编码假数据实现。
42
+ - **`task.json.frontendMock.policy`**:`auto | required | disabled`(规范强制 Mock 用 `required`)
43
+ - **被拦截时怎么修**:`mock-strategy-outside-allowed` / `no authorized Mock verification commands` 是**生成期契约问题,不是 plan 问题**——补 `frontendMock.verifyCommands`(或 `policy: "required"` + 命令)后**重新生成 DAG** 再跑,plan-revision 无法修复它。
28
44
 
29
45
  ## allowedPaths
30
46
 
31
- TODO
47
+ - **要探索什么**:任务源 `需求.md`/`执行约束.md`/`task.json` 中声明的写入边界,以及本次交付实际涉及的文件/目录。
48
+ - **结果必须包含**:与 `task.json.allowedPaths` 一致且窄化的路径列表;任何越界路径都必须在 plan 中记为阻断性 scope conflict。
49
+ - **缺失/冲突时如何 fail-closed**:缺失允许路径或发现计划目标在允许路径之外时,不得擅自扩大边界,返回 blocked。
32
50
 
33
51
  ## forbiddenPaths
34
52
 
35
- TODO
53
+ - **要探索什么**:任务源与 `task.json` 中声明的禁止写入路径(运行时产物、依赖目录、构建输出、私有路径等)。
54
+ - **结果必须包含**:禁止写入的路径清单,以及实现/验证步骤不触碰这些路径的确认。
55
+ - **缺失/冲突时如何 fail-closed**:任何写入落入禁止路径立即阻断;禁止路径语义不变,不得新增读取权限字段或扩大写入边界。
56
+
57
+ ## OpenSpec 必读与引用核验(frontendOpenspec)
58
+
59
+ - **要探索什么**:`task.json.frontendOpenspec.requiredReadPaths`(契约显式声明的必读 openspec 路径)与任务源 `需求.md`/`执行约束.md` 中显式引用的 openspec 路径。
60
+ - **结果必须包含**:cited 模式候选集 = `requiredReadPaths` ∪ 任务源显式引用(去重排序);每个候选在 plan 的 fenced `openspec-citations` 引用块中逐条出现(每行一个 JSON `{"path","section","line"}`),且每条引用都能在真实 read 事件中背书。
61
+ - **缺失/冲突时如何 fail-closed**:候选非空而引用块缺失/不可解析 → `openspec-citation-block-unparseable`;候选未引用 → `openspec-not-cited`;引用存在但无成功 read 事件 → `openspec-citation-not-read`。三个 code 均为 `retryable-invalid`。候选为空时不强制并写 advisory(可声明 `requiredReadPaths` 增强)。`scan-strict` 模式保留全量必读语义与 `openspec-not-read`。
62
+ - **`task.json.frontendOpenspec`**:`policy` 为 `cited | scan-strict`(缺省 `cited`);`requiredReadPaths` 为 repo 相对路径数组,经 `isOpenspecSpecFilePath` 校验,非法路径生成期确定性失败。
@@ -1,70 +1,104 @@
1
1
  # 前端任务需求模板
2
2
 
3
+ 本模板只承载**通用编写协议 + 三要素 + fail-closed 规则**,不包含任何具体业务组件名、项目专属路径或项目专属命令。填写时每一节都要给出「**要写什么** / **DAG 如何消费** / **缺失/冲突时 fail-closed**」三要素;缺节、空占位或与验收标准冲突时必须阻塞,不得由模型猜测补写。
4
+
3
5
  ## 用户目标
4
6
 
5
- TODO
7
+ - **要写什么**:以用户视角写清本次要交付的前端行为与用户价值——用户能完成什么、为什么需要它、成功后的可观察结果。必须可验证(可被界面状态或交互断言覆盖),不得只写技术动作或孤立关键词。
8
+ - **DAG 如何消费**:`frontend-contract-pi` 把它映射为 Scope 与 `requirements[].expectedOutcome`,`frontend-plan-pi` 据它安排实现步骤并映射验收标准。
9
+ - **缺失/冲突时 fail-closed**:缺失用户目标,或用户目标与验收标准互相矛盾时阻塞(`request-revision`/blocked),不得猜测补写或改写为技术实现描述。
6
10
 
7
11
  ## 目标页面/组件/路由
8
12
 
9
- TODO
13
+ - **要写什么**:明确本次交付涉及的目标路由、页面入口与组件范围,逐条列出路径/名称与职责边界;相关但超出范围的页面写清「不做」。
14
+ - **DAG 如何消费**:对齐 `docs/runtime/frontend-implementation-workflow.md` §「需求.md 应包含」的「目标路由/页面/组件」,`frontend-contract-pi` 据它确定 `targets.files` / `targets.routes`,`frontend-plan-pi` 据它划定 writeSet 与实现步骤。
15
+ - **缺失/冲突时 fail-closed**:无法确定目标路由/页面/组件,或范围与验收标准不一致时阻塞;只写泛化名词(如「页面」「组件」)而不给具体范围视为缺失。
10
16
 
11
17
  ## 用户流程
12
18
 
13
- TODO
19
+ - **要写什么**:写清端到端用户路径,从入口到结果,逐步列出触发动作、顺序与关键分支;涉及多页面/多状态时把每条路径分开写。
20
+ - **DAG 如何消费**:`frontend-contract-pi` 据它抽取 `interactions[]`(每条带 `trigger` 与 `expectedBehavior`),`frontend-plan-pi` 据它安排组件/状态/交互实现步骤。
21
+ - **缺失/冲突时 fail-closed**:缺少可执行的端到端路径、流程与验收标准冲突或关键分支未写时阻塞,不得只写「用户进入页面即可」。
14
22
 
15
23
  ## 必须状态
16
24
 
25
+ 每个适用状态必须写清该状态的触发条件与预期界面表现;不适用状态必须写 `N/A` 并给出非空理由(对齐 runtime 的 `uiStates` / N/A state 规则)。
26
+
17
27
  ### loading
18
28
 
19
- TODO
29
+ - **要写什么**:loading 状态出现与结束的触发条件、展示内容(骨架/文案/禁用交互)与超时/失败转移。
30
+ - **DAG 如何消费**:进入 implementation contract 的 `uiStates[]`(`name: "loading"`),`frontend-implement-pi` 按它实现,verify 用它的 `verificationTargetIds` 断言。
31
+ - **缺失/冲突时 fail-closed**:适用但缺失预期行为时阻塞;不适用必须写 `N/A` 并给非空理由,只写 `N/A` 不带理由同样阻塞。
20
32
 
21
33
  ### empty
22
34
 
23
- TODO
35
+ - **要写什么**:无数据/无结果状态的触发条件与展示(空态文案/引导动作/是否可刷新)。
36
+ - **DAG 如何消费**:进入 `uiStates[]`(`name: "empty"`),verify 断言空态展示与引导。
37
+ - **缺失/冲突时 fail-closed**:适用但缺失预期行为、或与 success/error 展示冲突时阻塞;不适用写 `N/A` + 非空理由。
24
38
 
25
39
  ### error
26
40
 
27
- TODO
41
+ - **要写什么**:失败状态(请求失败/校验失败/权限失败)的触发条件、错误展示与可恢复动作。
42
+ - **DAG 如何消费**:进入 `uiStates[]`(`name: "error"`),verify 断言错误展示与重试/回退行为。
43
+ - **缺失/冲突时 fail-closed**:适用但缺失错误预期、或错误态会覆盖用户数据时阻塞;不适用写 `N/A` + 非空理由。
28
44
 
29
45
  ### success
30
46
 
31
- TODO
47
+ - **要写什么**:成功状态的触发条件与展示(数据呈现/确认反馈/后续入口)。
48
+ - **DAG 如何消费**:进入 `uiStates[]`(`name: "success"`),verify 断言成功展示与验收标准对应。
49
+ - **缺失/冲突时 fail-closed**:成功态与验收标准不一致、或缺失成功结果时阻塞;不适用写 `N/A` + 非空理由。
32
50
 
33
51
  ### disabled
34
52
 
35
- TODO
53
+ - **要写什么**:禁用/不可交互状态的触发条件(权限/前置未满足/进行中)与视觉/交互表现。
54
+ - **DAG 如何消费**:进入 `uiStates[]`(`name: "disabled"`),verify 断言禁用态不会被误触。
55
+ - **缺失/冲突时 fail-closed**:适用但缺失禁用条件、或禁用态与交互要求冲突时阻塞;不适用写 `N/A` + 非空理由。
36
56
 
37
57
  ## 目标运行环境
38
58
 
59
+ 每个环境必须声明适用性;不适用环境写 `N/A` 并给非空理由。
60
+
39
61
  ### desktop
40
62
 
41
- TODO
63
+ - **要写什么**:desktop 视口下的适用性声明与关键布局/交互差异(断点、栅格、悬停等)。
64
+ - **DAG 如何消费**:进入 contract 的 target runtime environment,`frontend-plan-pi` 据它决定响应式范围与样式策略。
65
+ - **缺失/冲突时 fail-closed**:适用但缺失断点/布局说明,或与响应式要求冲突时阻塞;不适用写 `N/A` + 非空理由。
42
66
 
43
67
  ### mobile
44
68
 
45
- TODO
69
+ - **要写什么**:mobile 视口下的适用性声明与触控/布局/安全区差异。
70
+ - **DAG 如何消费**:进入 target runtime environment,`frontend-plan-pi` 据它决定响应式范围与组件策略。
71
+ - **缺失/冲突时 fail-closed**:适用但缺失移动端表现,或与交互要求冲突时阻塞;不适用写 `N/A` + 非空理由。
46
72
 
47
73
  ### tablet
48
74
 
49
- TODO
75
+ - **要写什么**:tablet 视口下的适用性声明与中间断点表现。
76
+ - **DAG 如何消费**:进入 target runtime environment,`frontend-plan-pi` 据它决定响应式策略。
77
+ - **缺失/冲突时 fail-closed**:适用但缺失 tablet 表现时阻塞;不适用写 `N/A` + 非空理由。
50
78
 
51
79
  ## 交互要求
52
80
 
53
- TODO
81
+ - **要写什么**:逐条写清每个可交互元素的触发动作与预期行为(点击/输入/提交/滚动/键盘等),可被自动化断言覆盖。
82
+ - **DAG 如何消费**:对齐 §「需求.md 应包含」的「交互要求」,`frontend-contract-pi` 抽取 `interactions[]`(`trigger` + `expectedBehavior`),verify 用行为命令断言。
83
+ - **缺失/冲突时 fail-closed**:交互触发或预期行为缺失、与 UI 状态冲突、或不可自动化断言时阻塞,不得用「用户可正常操作」这类空话。
54
84
 
55
85
  ## 接口与 Mock 输入
56
86
 
57
- - 接口文档/schema:TODO
58
- - endpoint、method、关键请求/响应字段:TODO
59
- - 是否允许依赖真实后端:TODO
60
- - 是否要求离线或独立行为验证:TODO
61
- - success/empty/error/permission 状态:TODO
62
- - 后端当前就绪状态与 Real Integration Gap:TODO
87
+ - **接口文档/schema — 要写什么**:引用接口文档或任务附件路径与版本,声明请求/响应 schema 来源。**DAG 如何消费**:`frontend-plan-pi` 据此选择 Mock 策略并冻结 endpoint/fixture 映射。**缺失/冲突时 fail-closed**:涉及接口但未引用文档或 schema 时阻塞,不得自行发明字段。
88
+ - **endpoint、method、关键请求/响应字段 — 要写什么**:逐条列出 method、path 与关键字段及含义。**DAG 如何消费**:写入 `mockApi.endpoints[]`(method/path/fixture/consumer)。**缺失/冲突时 fail-closed**:字段缺失、与接口文档冲突或路径不合法时阻塞。
89
+ - **是否允许依赖真实后端 — 要写什么**:明确声明真实后端当前是否可调用、是否允许生产/预览依赖真实请求。**DAG 如何消费**:决定 `mockApi.strategy` 是否可选 `not-needed`(需要真实/无远程证据)。**缺失/冲突时 fail-closed**:未声明后端就绪度却要求真实数据时阻塞,选 `not-needed` 却没有真实/无远程证据会被拦截。
90
+ - **是否要求离线或独立行为验证 — 要写什么**:声明是否需要离线、本地预览或自动化行为验证(或两者都要)。**DAG 如何消费**:决定生成期 `frontendMock.verifyCommands` 与冻结命令集。**缺失/冲突时 fail-closed**:要求离线/自动化却未声明确定性验证命令时,生成期收敛为 `not-needed` 或直接 blocked,plan-revision 无法修复。
91
+ - **success/empty/error/permission 状态 — 要写什么**:逐条声明接口各响应状态对应的 UI 表现。**DAG 如何消费**:与「必须状态」交叉校验,写入 `uiStates[]` 与 fixture 映射。**缺失/冲突时 fail-closed**:接口状态与 UI 状态不匹配或缺失 permission 分支时阻塞。
92
+ - **后端当前就绪状态与 Real Integration Gap — 要写什么**:写清后端是否就绪、未就绪时保留的 Real Integration Gap 与后端就绪后的复验路径。**DAG 如何消费**:进入 `evidenceGaps[]` 与 closeout 的 `Real integration: pending`。**缺失/冲突时 fail-closed**:声称已联通真实接口却无真实证据时阻塞;未就绪却把 Mock 结果写成真实联调会被 review 拒绝。
63
93
 
64
94
  ## 验收标准
65
95
 
66
- TODO
96
+ - **要写什么**:逐条编号(如 `AC-001`)写出可独立断言的前端验收标准,每条都能映射到需求与一个 verification target(可逐条追踪)。
97
+ - **DAG 如何消费**:`frontend-contract-pi` 生成 `requirements[].verificationTargetIds` 映射,`frontend-plan-pi` 把每条 AC 落到有序步骤与验证命令,verify 逐条断言。
98
+ - **缺失/冲突时 fail-closed**:无验收标准、AC 无法映射到需求/验证目标、或 AC 与其它节冲突时阻塞,不得把「看起来能用」当验收。
67
99
 
68
100
  ## 非目标
69
101
 
70
- TODO
102
+ - **要写什么**:明确列出本次不做的前端范围/排除项(页面、组件、交互、浏览器/视觉/无障碍检查等)。
103
+ - **DAG 如何消费**:`frontend-contract-pi` 记录为 Non-goals,`frontend-review-pi` 据它拒绝范围扩散。
104
+ - **缺失/冲突时 fail-closed**:非目标与目标/验收标准冲突、或范围被悄悄扩大时阻塞;明确不做前端时也必须写入非目标。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.38.0",
3
+ "version": "0.39.0-next.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -4,24 +4,25 @@ Pre-write nodes are read-only. Preserve IDs, labels, commands, language, require
4
4
 
5
5
  ## Core nodes
6
6
 
7
- - **`frontend-contract-pi`**: `Scope`, `Non-goals`, `Acceptance Criteria`, `UI States`, `Target Runtime Environment`, `Risks`, `Verification Expectations`. No guessed requirements.
8
- - **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets. Fact vs inference vs gap. Query the knowledge base when available and always search+read `openspec/schemas/`, `openspec/project-specs/`, and `ai_workspace/` before repo fallback. Output stack, routes, components, styling, conventions, state/data, test entry points, reuse, risks.
9
- - **`frontend-plan-pi` + conditional design loop**: AC → explicit observable `expectedOutcome`, interactions → explicit `trigger` + `expectedBehavior`, then steps, in-bound files, applicable UI states, reuse, deps, Mock/API strategy, activation/rollback, frozen verify entrypoints, and real-integration gap. These nodes output Markdown plans only; each plan ends with **exactly one** fenced `json` block carrying the single authoritative `frontend-implementation-contract-v1` object. Do not emit raw JSON, JSON in prose, or a second fenced block. `frontend-contract-json-pi` and `frontend-contract-json-validate-shell` do not exist; the prewrite gate materializes the contract from the effective plan node. IDs plus file paths are not sufficient behavior semantics. Use `uiStates: []` for logic-only changes with no user-visible UI state; do not invent UI states. Applicable states require behavior/implementation/verification, while non-applicable states require a reason and omit empty behavior placeholders. Prefer native Mock; browser intercept only with existing e2e; request-adapter only for a reversible seam. A non-`not-needed` strategy requires frozen Mock verify commands (`frontendMock.verifyCommands` / `package.json` mock script / capability seed); `auto` with absent/ambiguous capability or no command selects `not-needed` (real requests stay default, gap recorded); `required` without a command is generation-time blocked (no writer is generated). Initial design pass uses the original plan; only exact `request-revision` runs read-only revision plus final review. Small-risk runs one design review only.
10
- - **`frontend-prewrite-gate-shell`**: the sole write authorization. Resolve the effective plan (revised plan when the revision branch ran, otherwise the original plan) and its review, require exact pass, retain every REQ/BR/AC id, enforce Mock policy, validate schema/source binding and writeSet containment, and materialize `contracts/frontend-implementation-contract.json` from the **single** fenced contract block in the effective plan node (multiple candidates fail closed with `invalid-output`). A planned fixture or consumer may be a future writer output and need not exist before authorization. Fallback is allowed only when a conditional primary is absent; an existing malformed primary fails closed. Generation-time blocked Mock produces one deterministic blocking shell node and no writer.
11
- - Every `verificationTarget.commandLabel` must be one of the **frozen command labels** derived from the DAG run spec's `verifyEvidence.commandLabels`; any other value is rejected fail-closed at materialization (`invalid-output`). An empty frozen set (e.g. no `run.json`) skips the check.
12
- - `mockApi.strategy !== "not-needed"` fails closed at the gate when `mockCommandLabels` is empty (`no authorized Mock verification commands`).
13
- - **`frontend-implement-pi`**: sole regular exclusive writer and consumer of `frontend-bounded-implement`, not this discovery skill. Stay in `writeSet`; real requests default-on; Mock reversible, dev/test-only, production-off. Atomic handler/intercept/adapter with consumer+tests. Stop on forbidden paths or guesses. First line must be `IMPLEMENTATION_OUTCOME: changed|already-satisfied|blocked`; runtime checks it against the attributed diff. Optional mock-verify when frozen; static+behavior always; behavior must prove page consumption. Skipped-Mock `not-needed` keeps real integration pending unless the real backend path has fresh evidence.
7
+ - **`frontend-contract-pi`**: `Scope`, `Non-goals`, `Acceptance Criteria`, `UI States`, `Target Runtime Environment`, `Risks`, `Verification Expectations`; no guessed requirements.
8
+ - **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets; fact vs inference vs gap. Search+read `openspec/schemas/`, `openspec/project-specs/`, `ai_workspace/` first. Output stack, routes, components, styling, conventions, state/data, test entry points, reuse, risks.
9
+ - **`frontend-plan-pi` + conditional design loop**: AC → observable `expectedOutcome`; interactions → `trigger`+`expectedBehavior`; steps, in-bound files, applicable UI states, reuse, deps, Mock/API strategy, activation/rollback, frozen verify entrypoints, real-integration gap. Markdown plans only; each ends with **exactly one** fenced `json` block (authoritative `frontend-implementation-contract-v1`), then exactly one fenced `openspec-citations` block listing each read openspec file. No raw JSON, JSON in prose, or 2nd fenced block. `frontend-contract-json-pi`/`frontend-contract-json-validate-shell` don't exist; prewrite gate materializes the contract from effective plan. IDs+paths aren't sufficient behavior semantics. Use `uiStates: []` for logic-only changes; don't invent UI states. Applicable states need behavior/implementation/verification; non-applicable need a reason, no empty placeholders. Non-`not-needed` needs frozen Mock verify commands (`frontendMock.verifyCommands`/`package.json` mock script/capability seed); `auto` with absent/ambiguous capability or no command `not-needed` (real requests default, gap recorded); `required` without a command generation-time blocked (no writer). Initial pass uses original plan; only exact `request-revision` runs read-only revision + final review. Small-risk: one design review.
10
+ - **`frontend-prewrite-gate-shell`**: sole write authorization. Resolve effective plan (revised if revision ran, else original) + review; require exact pass; retain every REQ/BR/AC id; enforce Mock policy; validate schema/source binding+writeSet containment; materialize `contracts/frontend-implementation-contract.json` from the **single** fenced contract block (multiple candidates fail closed `invalid-output`). Fallback only when conditional primary absent; existing malformed primary fails closed. Generation-time blocked Mock yields one deterministic blocking shell node, no writer.
11
+ - `verificationTarget.commandLabel` must be a **frozen command label** from `verifyEvidence.commandLabels`; others fail closed (`invalid-output`). Empty frozen set (no `run.json`) skips check.
12
+ - `mockApi.strategy !== "not-needed"` fails closed when `mockCommandLabels` empty (`no authorized Mock verification commands`).
13
+ - OpenSpec policy: `task.json.frontendOpenspec.policy` (default `cited`); candidates = `requiredReadPaths` task-source citations; effective plan must append one fenced `openspec-citations` block (one JSON `{"path","section","line"}` per line). Candidates non-empty but block missing/unparseable `openspec-citation-block-unparseable`; candidate not cited `openspec-not-cited`; cited path without successful read event in plan/review → `openspec-citation-not-read` (anti-fabrication). Empty candidates skip enforcement (generation-time advisory). `scan-strict` keeps auto-discovered full-read semantics (`openspec-not-read`); both modes keep `candidate-missing-drift`.
14
+ - **`frontend-implement-pi`**: sole regular exclusive writer; uses `frontend-bounded-implement` (not this skill). Stay in `writeSet`; real requests default-on. Atomic handler/intercept/adapter with consumer+tests. Stop on forbidden paths/guesses. First line `IMPLEMENTATION_OUTCOME: changed|already-satisfied|blocked`; runtime checks against attributed diff. Optional mock-verify when frozen; static+behavior always; behavior must prove page consumption. `not-needed` keeps real integration pending unless real backend has fresh evidence.
14
15
 
15
16
  ## Contract / trace / stages (M1–M2)
16
17
 
17
- - `frontend-verify-assess-shell` runs Mock/static/behavior command groups, binds contract `verificationTargets` to command labels and file/symbol evidence, then writes `frontend-verification-trace.json` and `frontend-repair-assessment.json`. Browser/visual remain `not-run`.
18
- - `frontend-review-context-shell` captures the real diff and combines contract, effective trace, repair assessment, and diff into `contracts/frontend-review-context.json`.
18
+ - `frontend-verify-assess-shell` runs Mock/static/behavior groups, binds `verificationTargets` to command labels + file/symbol evidence, writes `frontend-verification-trace.json` + `frontend-repair-assessment.json`; browser/visual stay `not-run`.
19
+ - `frontend-review-context-shell` captures real diff; combines contract, effective trace, repair assessment, diff into `contracts/frontend-review-context.json`.
19
20
  - Implement stages: (1) contract confirm (2) tests sync (3) component/UI (4) API/Mock (5) frozen checks (6) diff cleanup. Summary: Contract Ref, Changed Files, Requirements, UI States, Tests, Verification Attempts, Deviations, Residual Risks.
20
21
 
21
22
  ## Repair (M3)
22
23
 
23
- Only `eligible=true` runs `frontend-repair-pi` (same writeSet as implement; no re-spec; max 1 attempt) and `frontend-reverify-shell`. No failure condition-skips both. Non-repairable contract/path/dependency/credential/deploy/spec-unclear failures fail closed.
24
+ Only `eligible=true` runs `frontend-repair-pi` (same writeSet; no re-spec; max 1 attempt) + `frontend-reverify-shell`. No-failure skips both. Non-repairable contract/path/dependency/credential/deploy/spec-unclear failures fail closed.
24
25
 
25
26
  ## Risk & capability (M4–M6)
26
27
 
27
- Deterministic risk (no model); high-risk beats small; supervised never small. Standard/high-risk contain 15 top-level nodes; small contains 13 by omitting revision and final review. Capability seed injects adapters; openspec specs and task sources outrank adapter guidance. A11y: static/component tools only when present; Browser a11y always not-run.
28
+ Deterministic risk, no model; high-risk beats small; supervised never small. Standard/high-risk: 15 top-level nodes; small: 13 (omits revision + final review). Capability seed injects adapters; openspec specs and task sources outrank adapter guidance. A11y: static/component tools only when present; Browser a11y always not-run.