@peterxiaoyang/superspec 0.1.50 → 0.1.51

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.
package/dist/record.js CHANGED
@@ -72,7 +72,7 @@ function previousRejectionInstruction(job) {
72
72
  return `${reason}上一轮没有可复核的历史 finding;请按当前 gate 的完整范围独立审查,不要把拒绝原因当作需求或验收标准,`;
73
73
  }
74
74
  const identityRule = job.role === "code-reviewer"
75
- ? "同一问题仍存在时复用原 finding ID;legacy finding 没有 ID 时沿用原始语义并补一个稳定 ID;"
75
+ ? "逐项核对修复 task 的代码变化、scope_note 和验证证据;实现者用可核实证据说明被质疑实现确有必要时,独立验证后关闭原问题。证据不能支撑必要性且问题仍存在时复用原 finding ID;legacy finding 没有 ID 时沿用原始语义并补一个稳定 ID;"
76
76
  : "已解决或已由等价证据闭环的问题不要重复报告,不得通过更换标题或措辞重复同一问题;";
77
77
  return `${reason}本轮是修复复核:逐项判断本工作项附带的上一次同角色 finding 是否仍成立。Finding 中的 recommendation 只是非绑定建议,不是需求或验收标准;先独立核对 underlying problem、直接证据和本次验收,不得因原建议指定了某种架构就要求照做。修正不得通过缩小已确认范围、改写用户决定或删除验收来让 finding 字面消失;这类偏离属于本次修正直接引入的回归。${identityRule}默认只复核历史 finding;新 blocker 仅允许是本次修正直接引入的回归,并必须说明“修正动作 → 新问题”的因果链,不得展开无关的故障模型、消费者或架构议题。`;
78
78
  }
@@ -1176,7 +1176,7 @@ function packetFieldDescriptions() {
1176
1176
  test_id: "测试契约里的 TEST ID。",
1177
1177
  semantic_status: "测试语义状态:RED 预期失败、GREEN 预期成功或特征化(characterization)通过。",
1178
1178
  covers_task_ids: "回归测试覆盖了哪些已完成任务(task)。",
1179
- scope_note: "范围扩大说明;任务(task)实现超出执行依据边界时填写。",
1179
+ scope_note: "实施范围说明;任务(task)实现超出执行依据边界,或代码审查修复保留被质疑实现时,填写原因、影响区域、计划一致性和验证依据。",
1180
1180
  };
1181
1181
  }
1182
1182
  /** jobs packet:返回工作项执行说明 */
@@ -1237,7 +1237,7 @@ export function jobsPacket(projectRoot, change, jobId) {
1237
1237
  ? `格式骨架:{"role":"code-reviewer","verdict":"pass","review_scope":{"job_id":"${job.job_id}","packet_digest":"${job.packet_digest}","checked_paths":[],"checked_docs":[],"unchecked":[]},"findings":[],"reviewer":{"kind":"codex-subagent","id":"<thread-or-agent-id>"}}。提交前按真实审查结果填写数组;不得从 boundFiles 自动复制 checked_paths。verdict 只能为 pass 或 fail;审查覆盖范围(review_scope)用来说明本次审查覆盖了哪些文件和文档,已检查路径(checked_paths)与未检查项(unchecked)必须合起来覆盖全部绑定文件(boundFiles),unchecked 条目格式为 {"path":"<path>","reason":"<reason>"};pass 不允许仍有未检查的绑定文件。`
1238
1238
  + `报告结论为 fail 时,问题列表(findings)至少包含一个可处理、可追溯的阻塞问题,字段为 {"id":"<stable-id>","blocking":true,"type":"implementation|spec|mixed","description":"<what>","evidence":"<why>","source_refs":["<path:line>"],"impact":"<impact>","suggested_action":"apply|propose"}。问题类型(type)中 implementation 表示纯代码实现问题,spec 表示方案/需求文档问题,mixed 表示需要使用者判断的混合问题。`
1239
1239
  + (packetContext?.task_execution_index
1240
- ? `本工作项带任务执行索引(task_execution_index):按 task 对照其执行依据快照(contract)审查——实现路线对照 design 引用原文、累计 diff 对照 guard 边界、测试断言对照 tests 声明的 scenario;每项的 required_evidence 是 task-start 冻结的证据口径,red_required/green_required 分别说明是否需要 RED/GREEN;fix 非空表示状态机创建的实现修复,source、parent_task_id 和 reason 说明其归属,code_review 来源还需核对 review_finding;每项的 scope_note 是执行者登记的范围扩大说明,判断其合理性与验证充分性;changed_paths 是归属线索不是结论(null 表示未知);unattributed_paths 中的无主改动逐个判断合理性;coverage_exemption_refs 解释未绑定 task 的 TEST 豁免。`
1240
+ ? `本工作项带任务执行索引(task_execution_index):按 task 对照其执行依据快照(contract)审查——实现路线对照 design 引用原文、累计 diff 对照 guard 边界、测试断言对照 tests 声明的 scenario;每项的 required_evidence 是 task-start 冻结的证据口径,red_required/green_required 分别说明是否需要 RED/GREEN;fix 非空表示状态机创建的实现修复,source、parent_task_id 和 reason 说明其归属,code_review 来源还需核对 review_findingscope_note 既可能解释必要的范围扩大,也可能说明代码审查修复为何保留原实现,均需结合 Diff、调用链和验证证据独立判断;changed_paths 是归属线索不是结论(null 表示未知);unattributed_paths 中的无主改动逐个判断合理性;coverage_exemption_refs 解释未绑定 task 的 TEST 豁免。`
1241
1241
  : "")
1242
1242
  : job.role === "verifier"
1243
1243
  ? `最小格式:{"role":"verifier","verdict":"pass","findings":[]${hasReviewScope ? `,"review_scope":{"checked_paths":${JSON.stringify(job.boundFiles.map(file => file.path))}}` : ""}}。verdict 只能为 pass 或 fail;核对代码审查记录(code_review_gate):passed 必须能追溯到已接受的代码审查工作项,skipped 必须能证明本次没有代码类改动。核对修复闭环:task_execution_index.fix.source=code_review 时必须核对 review_finding 对应问题是否关闭;source=self_test 时必须核对 parent_task_id、记录的自测原因、本次 attempt 验证和最新代码审查是否共同闭环。方案/混合问题必须有用户决策或后续修复证据。按 task_execution_index 的 required_evidence 核对测试证据:red_required 时需要同一 TEST 的 RED(expected_failure)后 GREEN;green_required 时每个声明 TEST 都需要允许的 GREEN 语义状态;测试运行证据应包含测试 ID(test_id)、命令(command)、工作目录(cwd)、退出码(exit_code)、语义状态(semantic_status)。修复 task 的回归测试运行可用回归覆盖任务列表(covers_task_ids)说明覆盖了哪些已完成任务;缺少任务尝试 ID(attempt_id)的旧证据只能弱引用。` +
@@ -245,6 +245,56 @@ function hasRejectedReviewReadyVerifier(events) {
245
245
  return events.some(ev => ev.event_type === "job_rejected" &&
246
246
  reviewReadyVerifierIds.has(ev.payload.job_id ?? ""));
247
247
  }
248
+ function repairedCodeReviewPreviousRejection(events, packetContext) {
249
+ const eventOrder = new Map(events.map((event, index) => [event.event_id, index]));
250
+ const codeReviewJobIds = new Set();
251
+ for (const event of events) {
252
+ if (event.event_type !== "transition_commit")
253
+ continue;
254
+ for (const job of event.payload.new_jobs ?? []) {
255
+ if (job.role === "code-reviewer")
256
+ codeReviewJobIds.add(job.job_id);
257
+ }
258
+ }
259
+ const latestAcceptedIndex = events.findLastIndex(event => event.event_type === "job_accepted" &&
260
+ codeReviewJobIds.has(String(event.payload.job_id ?? "")));
261
+ const latestStartApplyIndex = events.findLastIndex(event => event.event_type === "transition_commit" &&
262
+ event.payload.transition === "start-apply");
263
+ const historyBoundary = Math.max(latestAcceptedIndex, latestStartApplyIndex);
264
+ const repairedEntries = [...(packetContext.task_execution_index ?? [])]
265
+ .filter(entry => entry.fix?.source === "code_review" &&
266
+ entry.fix.review_finding &&
267
+ (eventOrder.get(entry.task_completed_event_ref) ?? -1) > historyBoundary)
268
+ .sort((left, right) => (eventOrder.get(right.task_completed_event_ref) ?? -1) -
269
+ (eventOrder.get(left.task_completed_event_ref) ?? -1));
270
+ const latestRef = repairedEntries[0]?.fix?.review_finding;
271
+ if (!latestRef)
272
+ return undefined;
273
+ const rejection = events.findLast(event => event.event_type === "job_rejected" &&
274
+ event.payload.job_id === latestRef.job_id);
275
+ const payload = rejection?.payload;
276
+ const findings = Array.isArray(payload?.findings)
277
+ ? payload.findings.filter(item => {
278
+ if (!item || typeof item !== "object" || Array.isArray(item))
279
+ return false;
280
+ const finding = item;
281
+ if (finding.blocking !== true || typeof finding.id !== "string")
282
+ return false;
283
+ return latestCodeReviewDecision(events, codeReviewDecisionScope(latestRef.job_id, finding.id))?.answer !== "dismiss";
284
+ })
285
+ : [];
286
+ if (findings.length === 0)
287
+ return undefined;
288
+ return {
289
+ result_kind: "review_failed",
290
+ reason: typeof payload?.reason === "string" && payload.reason.trim()
291
+ ? payload.reason
292
+ : "复核已执行的代码审查修复",
293
+ job_id: latestRef.job_id,
294
+ findings_job_id: latestRef.job_id,
295
+ findings,
296
+ };
297
+ }
248
298
  function createCodeReviewerJob(change, projectRoot, changeRoot, events) {
249
299
  const scan = scanCodeChangesForReview(projectRoot, events);
250
300
  const boundFiles = codeReviewBoundFiles(projectRoot, scan.paths);
@@ -252,6 +302,7 @@ function createCodeReviewerJob(change, projectRoot, changeRoot, events) {
252
302
  const facts = collectCodeReviewGateFacts(events);
253
303
  const latestRejected = facts.latestRejected;
254
304
  const reviewFailedStatus = latestCodeReviewFailedStatus(events);
305
+ const repairedPreviousRejection = repairedCodeReviewPreviousRejection(events, packetContext);
255
306
  const previousRejection = latestRejected && latestRejected.state === "rejected"
256
307
  ? {
257
308
  result_kind: latestRejected.result_kind ?? "invalid_report",
@@ -259,8 +310,14 @@ function createCodeReviewerJob(change, projectRoot, changeRoot, events) {
259
310
  ? dismissedCodeReviewSummary(reviewFailedStatus)
260
311
  : latestRejected.reason ?? "缺少拒绝原因",
261
312
  job_id: latestRejected.job.job_id,
313
+ ...(latestRejected.result_kind !== "review_failed" && repairedPreviousRejection?.findings?.length
314
+ ? {
315
+ findings: repairedPreviousRejection.findings,
316
+ findings_job_id: repairedPreviousRejection.findings_job_id,
317
+ }
318
+ : {}),
262
319
  }
263
- : undefined;
320
+ : repairedPreviousRejection;
264
321
  const packetInput = {
265
322
  role: "code-reviewer",
266
323
  gate_id: REVIEW_CODE_REVIEW_GATE_ID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peterxiaoyang/superspec",
3
- "version": "0.1.50",
3
+ "version": "0.1.51",
4
4
  "description": "SuperSpec 流程引擎 — transition engine with lightweight fact-sync",
5
5
  "type": "module",
6
6
  "engines": {
@@ -17,7 +17,7 @@ Explore 中需要用户决定业务、验收、范围或关键取舍时,先简
17
17
 
18
18
  计划材料没有枚举某个类、继承关系、方法或局部实现细节,不等于计划遗漏。只要正确修法能够由既有 task、已批准行为和仓库事实唯一推导,仍属于 Apply;只有需要重新决定公共接口、数据归属、迁移兼容、实现路线、验收或 task 边界时才回 Propose。
19
19
 
20
- Apply 以满足已批准行为的最小语义影响面为成功标准。需求未要求改变的公共契约、共享行为和兼容语义应保持不变;扩大公共实现边界必须有当前 task 和真实调用链支持。最小改动不能成为遗漏已确认消费者或验收路径的理由。
20
+ Apply 的成功标准是完整兑现已批准行为,并在满足验收的实现中选择语义影响面最小的方案。每项实现和测试变化都应服务于当前 task 和已批准行为,其必要性由仓库事实或直接影响链路支持;实施范围可以包含必要的跨文件或跨层改动,但应覆盖已批准的真实消费者和验收路径,不包含与当前交付无直接因果关系的变化。
21
21
 
22
22
  - 当前 task 尚未完成时,在其范围内直接修复;不要为同一实现问题新增 task 或回 propose。
23
23
  - 所有 task 已完成后,若问题仍能关联一个已完成 task、且不改变已批准行为和方案,主流程执行 `superspec transition reopen --change "<change>" --to apply --self-test-fix "<task>" --reason "<reason>"`,让工作流创建修复事项;随后继续 `next`,不得手改 tasks。
@@ -23,8 +23,8 @@ argument-hint: "本次代码审查说明"
23
23
 
24
24
  - 实现是否兑现当前任务的验收和边界,且与已批准的方案/规格一致。
25
25
  - 是否引入功能、数据、一致性、安全、权限、性能或兼容问题,以及直接的边界条件遗漏。
26
- - 改动是否覆盖计划及真实调用链已经确认的直接影响,并且每项语义变化都有必要性证据。任务勾选和测试通过不能替代对遗漏路径与无关改动的判断。
27
- - 文件或代码数量本身不是问题。局部需求改变无关生产行为、扩大共享边界或夹带重构时,只有能够证明这些变化并非当前验收所必需,才作为过度实现问题。
26
+ - 从批准范围反查实现是否覆盖已确认的消费者、兼容路径和直接影响链路;任务勾选和测试通过不能替代完整性判断。
27
+ - 从实际 Diff 反查每项语义变化是否为当前验收所需。文件数量、新增方法或重载本身不是问题;若公共契约、共享行为或无关生产逻辑被扩大,而现有证据不能说明局部方案为何无法安全、完整地满足验收,应作为纯实现问题交回 Apply 收缩。
28
28
  - 测试是否实际证明相关行为和直接回归风险,而非只存在一条通过记录。
29
29
  - 需求源已更新时,代码是否仍在执行过期计划;此类问题按方案或需求缺口归因,不把旧材料当作当前依据。
30
30
 
@@ -32,7 +32,7 @@ argument-hint: "本次代码审查说明"
32
32
 
33
33
  将问题归因为:纯实现问题(可回 apply 修复)、方案/需求问题(计划不能支持正确实现)或混合问题(需要主流程处理分歧),并说明依据。不要用审查建议创造新的需求或架构。
34
34
 
35
- 纯实现问题要说明为什么不需要改变计划;方案或混合问题要说明为什么单纯改代码无法满足已确认验收。无阻塞问题时也说明尚未验证的风险,避免把审查覆盖当作全局保证。
35
+ 纯实现问题要说明为什么不需要改变计划;方案或混合问题要说明为什么单纯改代码无法满足已确认验收。复核修复时,Apply 可以通过代码变化关闭问题,也可以用可核实的调用链、兼容约束或验证证据说明原实现必须保留;证据成立时关闭原问题,证据不足时沿用原 finding,不因实现者偏好或审查者偏好反复争论。无阻塞问题时也说明尚未验证的风险,避免把审查覆盖当作全局保证。
36
36
 
37
37
  ## 输出
38
38
 
@@ -8,7 +8,7 @@ metadata:
8
8
 
9
9
  # SuperSpec Apply
10
10
 
11
- 按当前 change 的已批准 task 完成小范围实现和真实验证。Apply 只执行已批准计划,不把发现的新需求悄悄带进代码。实现目标是在满足验收的同时保持最小语义影响面,而不只是让最终功能可用。
11
+ 完整兑现当前 change 的已批准 task,并提供真实验证。在满足验收的可行实现中,选择语义影响面最小的方案;新的需求,或需要重新决定已批准行为、边界或方案的事项,不属于 Apply
12
12
 
13
13
  ## 工作方式
14
14
 
@@ -17,7 +17,7 @@ metadata:
17
17
  每个 task 使用同一循环:
18
18
 
19
19
  1. 开始 task 前先检查当前工作区变化,并沿 task 引用链核对发生变化的需求源或计划材料;确认要实现的行为、边界和相关测试仍与当前计划一致。新变化使已批准行为、验收、边界或方案失效时,不按旧计划继续,停止实现并交回 Propose。
20
- 2. 在授权范围内实现最小改动;不要提前修改计划材料或扩大范围。优先复用现有边界或作局部适配,保持未被需求要求改变的公共接口、共享工具、默认行为和兼容语义不变。
20
+ 2. 以当前 task、已批准行为、仓库事实和直接影响链路界定实施范围,优先复用现有边界或作局部适配,完成满足当前验收所需的最小改动。
21
21
  3. 完成当前 task 要求的验证,如实报告测试、环境或覆盖不足的结果。
22
22
  4. 当前 task 完成后立即继续工作流并处理下一事项;不要总结交付或等待用户再次要求继续。
23
23
 
@@ -27,11 +27,11 @@ metadata:
27
27
 
28
28
  ### 最小语义影响面
29
29
 
30
- 最小改动不是追求最少文件或最少行数,而是只引入实现已批准行为所必需的语义变化。需求责任确实跨越多个层次时可以跨文件实现;局部需求不能成为重写共享逻辑或扩大公共行为的理由。
30
+ 最小改动不是追求最少文件或最少行数,而是让每项语义变化都有当前交付所需的直接理由。必要的跨文件或跨层改动属于实施范围;与 task 及其直接影响无法建立因果关系的变化不属于本次交付。
31
31
 
32
- - 不为方便当前实现或测试而改变无关生产行为,也不把顺手重构、历史清理或技术偏好带入本次交付。
33
- - 公共或共享实现的扩展必须由当前 task、现有结构和真实调用链证明必要;存在安全的局部实现时,保持公共边界不变。
34
- - 完成标准包括整体差异可解释,并覆盖批准范围内的真实消费者。无法对应当前 task 或必要直接影响的变化不应保留,最小改动也不能以遗漏需求为代价。
32
+ - 重构、清理或技术调整只有在其本身是兑现当前 task 的必要组成部分时才进入本次交付。
33
+ - 公共契约、共享行为、默认行为或兼容语义的变化需要当前 task 和真实调用链支持,并验证直接受影响的既有行为;局部实现已能完整满足验收时,保持公共边界稳定。
34
+ - 整体差异应能解释并覆盖批准范围内的真实消费者;影响面最小不能以遗漏需求或验收路径为代价。
35
35
 
36
36
  ### 保留得住的测试
37
37
 
@@ -48,13 +48,13 @@ metadata:
48
48
 
49
49
  所有 task 已完成后,自测发现仍能关联一个已完成 task、且不改变已批准行为和方案的实现问题,在同一 change 内按工作流安排修复;不要把它当作新需求。
50
50
 
51
+ 代码审查意见应结合批准行为、当前实现和直接证据独立判断。处理结果应通过实现变化关闭所指出的问题,或以可复核的调用链、兼容约束和验证结果证明现状确有必要;上述两种闭环均未形成时,该问题仍未解决。
52
+
51
53
  发现新的用户可见行为、验收、业务规则、影响范围,或发现计划中的数据来源、边界和实现路线不再成立时,停止实现并交回 propose 更新计划。不能关联现有 task 的问题也按此处理。能由当前 task 的引用链直接解释的局部连带改动可以继续;原因不明的扩展不能静默带入。
52
54
 
53
55
  范围扩大说明只能解释仍服务于当前 task 的局部连带改动,不能掩盖新增能力、改变验收、兼容策略或规范语义。用户在 apply 期间补充这些内容时,先回计划材料处理,再继续实现。
54
56
 
55
57
  ## Guardrails
56
58
 
57
- - 只改当前 task 授权范围内的实现和测试文件。
58
- - 不改变当前 task 未要求变化的共享语义;必要的公共改动必须有调用链证据,并验证直接受影响的既有行为。
59
59
  - 不修改计划材料、工作流记录、审查报告或验证材料。
60
60
  - 不代替后续审查或验证流程作结论。
@@ -18,14 +18,14 @@ metadata:
18
18
 
19
19
  人类可读正文使用简体中文。源码锚点采用能唯一定位的最短写法;文档引用使用 `文件#锚点`,让实现者能打开原材料。
20
20
 
21
- ## 从已确认结论生成计划
21
+ ## 计划完成标准
22
22
 
23
- 按已确认的用户结果组织计划,而不是按目录、数据库、接口或“先写测试/再写实现”的技术层流水线组织:
23
+ 计划以已确认的用户结果为共同边界。`proposal.md`、`specs/`、`design.md`、`tasks.md` 与 `test-contract.md` 应相互解释,形成一致的交付模型,使实现者能够在不重新决定产品语义、已批准技术路线或验收口径的前提下进入 Apply。
24
24
 
25
- 1. 先写长期成立的可观察行为和验收场景,再确定实现承担责任的边界与数据/控制流;不从当前类、表或框架倒推需求。
26
- 2. 为每个需验证行为写清前置条件、动作和可观察结果;预期来自规格、示例或独立事实,不能复刻实现算法。
27
- 3. task 优先按垂直交付拆分:一个 task 完成一段用户或调用方可验收的能力,并携带足以实现和验证它的来源、设计、验收与边界。共享机械改动只有确实不能独立交付时才按真实依赖拆开。
28
- 4. 每步只安排满足当前已确认行为所需的最小改动;不要把未来能力、理论故障模型、通用基础设施升级或未采纳方案预支进计划。
25
+ - 可观察行为和验收场景应描述长期成立的结果。前置条件、动作和预期来自规格、示例或独立事实,不从当前实现结构倒推需求,也不复刻实现算法。
26
+ - 设计应明确承担责任的边界以及必要的数据或控制流,使不同实现者能够作出一致实现。
27
+ - task 优先围绕用户或调用方可验收的能力形成垂直交付,并携带相应的来源、设计、验收和边界。共享机械改动依据真实交付依赖安排,不按技术层机械拆分。
28
+ - 每项计划变化都应服务于已确认行为及其直接交付链路。多个方案都能满足验收时,选择责任清晰且语义影响面较小的方案;计划范围只包含当前交付必需且有证据支持的能力、风险与依赖。
29
29
 
30
30
  参考实现是现有能力与候选机制的证据,不是本次 change 的默认结构。根据已确认目标重新判断最小责任边界;不能因为参考模块存在某组接口、实体、表或服务,就直接复制其形态。当前证据范围内尚未核实的外部仓库或模块变化,应标为待核实依赖、实施前置或风险,不得写成已经成立的实现事实。
31
31