From bc873f440067c9d2535fa6d300c51195275ccba6 Mon Sep 17 00:00:00 2001 From: WeiCHEN Date: Tue, 22 Sep 2026 21:26:27 +0800 Subject: [PATCH] docs(ops): redline evidence batch without capability release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record SWBOOT L1–L4 (no real repo write), FAV ObserveShadow gates and observation-window prerequisites, D7 Track E ID-only checklist, and stronger PI narrative fallback negatives while keeping all redline flags off. --- ...026-09-22-d7-track-e-id-only-compliance.md | 67 +++++++++ .../short-term/2026-09-22-redline-evidence.md | 139 ++++++++++++++++++ ...-22-redline-evidence-swboot-test-report.md | 111 ++++++++++++++ .../arch-check.txt | 7 + .../d7-no-physical-delete.txt | 11 ++ .../d7-static-inventory.txt | 18 +++ .../d7-track-e-payload-fields.txt | 4 + .../d7-track-e.txt | 19 +++ .../2026-09-22-redline-evidence/env.txt | 6 + .../fav-compose-defaults.txt | 6 + .../fav-register-gate.txt | 10 ++ .../fav-registry-gate-lines.txt | 9 ++ .../fav-shadow.txt | 20 +++ .../fav-static-audit.txt | 19 +++ .../fav-vault-status.txt | 6 + .../2026-09-22-redline-evidence/head-sha.txt | 1 + .../l3-build-attempt.txt | 5 + .../l3-frontend.txt | 7 + .../narrative-diff-stat.txt | 0 .../pi-flag-defaults.txt | 5 + .../pi-narrative.txt | 50 +++++++ .../2026-09-22-redline-evidence/swboot-l1.txt | 29 ++++ .../swboot-l2-short.txt | 82 +++++++++++ .../swboot-l4-stub.txt | 11 ++ internal/docrevision/narrative_test.go | 79 +++++++++- 25 files changed, 720 insertions(+), 1 deletion(-) create mode 100644 .doc/ops/checklists/2026-09-22-d7-track-e-id-only-compliance.md create mode 100644 .doc/ops/short-term/2026-09-22-redline-evidence.md create mode 100644 .doc/test-reports/2026-09-22-redline-evidence-swboot-test-report.md create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/arch-check.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/d7-no-physical-delete.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/d7-static-inventory.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e-payload-fields.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/env.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/fav-compose-defaults.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/fav-register-gate.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/fav-registry-gate-lines.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/fav-shadow.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/fav-static-audit.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/fav-vault-status.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/head-sha.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/l3-build-attempt.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/l3-frontend.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/narrative-diff-stat.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/pi-flag-defaults.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/pi-narrative.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l1.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l2-short.txt create mode 100644 .doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l4-stub.txt diff --git a/.doc/ops/checklists/2026-09-22-d7-track-e-id-only-compliance.md b/.doc/ops/checklists/2026-09-22-d7-track-e-id-only-compliance.md new file mode 100644 index 00000000..e27d90ed --- /dev/null +++ b/.doc/ops/checklists/2026-09-22-d7-track-e-id-only-compliance.md @@ -0,0 +1,67 @@ +--- +title: "D7 Track E ID-only 合规清单(无物理删除)" +status: active +created: 2026-09-22 +updated: 2026-09-22 +compliance: pending-compliance-signoff +related_task: "ops/short-term/2026-09-22-redline-evidence.md" +related_adr: "adr/2026-09-17-d7-chat-data-retention.md" +--- + +# D7 Track E ID-only 合规清单 + +> 用途:在 D7 ADR 仍为 `pending-compliance-signoff` 期间,只做表面盘点与 ID-only 合规核对。**禁止**按 365/180/30 天执行物理删除或备份清除。本清单不是删除作业授权。 + +## 0. 硬约束 + +- [x] ADR 状态仍为 `pending-compliance-signoff`(见 [ADR](../../adr/2026-09-17-d7-chat-data-retention.md)) +- [x] 本轮零 `DELETE FROM chat_*`、零备份清除、零 retention 作业 +- [x] FEATURE-MATRIX「对话数据治理(D7)」保持 `deferred`(本批不改矩阵) +- [ ] 合规签字或改数后,另开整改任务再勾选删除路径 + +## 1. 数据表面盘点(只读) + +| ID | 表面 | 检查方法 | 期望 | 本批结果 | +|----|------|----------|------|----------| +| S1 | `chat_sessions` / `chat_messages` | 代码检索写路径;对照 W7 计数资产 | 有 `deleted_at` 列;无物理 DELETE 写者;无 soft-delete 写路径亦可接受(缺口属签字后整改) | PASS:`rg` 无 `DELETE FROM chat_*`;无 chat `SET deleted_at` 写者 | +| S2 | `context_memory_chunks`(`chat_history`) | 对照 W7 盘点 | 派生副本随正文;无独立 retention 删 | PASS(静态);未跑共享 SQL | +| S3 | `freeform_documents` | 已知 soft-delete UI | 与聊天独立周期;本轮不删 | PASS(不触碰) | +| S4 | `event_envelopes.payload` | 对照 Track E / W7 keys | 无 `content` / 消息正文 | PASS(沿用 W7:`has_content=f`);本批未重连 PG | +| S5 | `event_dead_letters` / attempts | 结构 / 既有盘点 | 仅 ID + reason / error_code | PASS(沿用 W7) | +| S6 | 应用日志 | 规范 + 既有抽检 | 无 chat 正文;DSN 脱敏 | PASS(沿用 W7);本批未 `docker logs` | +| S7 | `scripts/backup-system-db.sh` | 阅读脚本 | `pg_dump -Fc` 整库含明文;本轮不执行 | PASS:未执行 dump | +| S8 | `data/*/workspaces` | 目录约定 | 非终态;无自动 30 天清理 | PASS(无作业) | +| S9 | FAV / `final_artifacts` | consumer mode | 与聊天解耦;fav off | PASS:Register(fav) fail-closed | + +证据基线:[W7 报告](../../test-reports/2026-09-17-w7-d7-inventory-test-report.md)、[盘点审计](../../audit/2026-09-17-d7-data-inventory.md)、[红线短文](../short-term/2026-09-22-redline-evidence.md)。 + +## 2. Track E ID-only 字段规则 + +日志 / DLQ / reconciliation / 对外 summary **仅允许**: + +| 允许 | 禁止 | +|------|------| +| event_id / delivery_id / run_id / plan_id / project_id / doc_id | 消息 `content` / prompt 全文 | +| digest / sha256 / summary_ref | 用户 PII 明文堆栈 | +| typed error_code / allowlist 摘要键 | 未 allowlist 的 payload 整包 | +| 状态枚举(pending/leased/…) | 备份文件路径中的密钥 | + +核对动作: + +- [x] `events.SanitizeSummary` / StaticContracts 方向:生产路径不以正文入 envelope(W7 已证 keys) +- [x] 发现「已签字基线被违反」才写 gap-matrix;本批**未发现** → 不改 gap-matrix +- [ ] 签字后:收紧 `taskrun.completed` 等额外 ID 字段 allowlist(follow-up,非 blocker) + +## 3. 签字后才允许的整改(本清单不执行) + +1. 聊天 soft-delete 写路径 → 观察窗 → 物理清理 +2. 同步清理 `context_memory_chunks` 中 `chat_history` +3. 备份二次批准 + 精确 delete manifest(对齐 R22) +4. 字段级加密选型(ADR 明确首期不做默认加密) + +## 4. 完成定义(本批) + +- [x] 表面表 S1–S9 有结论 +- [x] ID-only 规则成文 +- [x] 无物理删除证据 +- [x] ADR 仍 `pending-compliance-signoff` diff --git a/.doc/ops/short-term/2026-09-22-redline-evidence.md b/.doc/ops/short-term/2026-09-22-redline-evidence.md new file mode 100644 index 00000000..3ce81bd6 --- /dev/null +++ b/.doc/ops/short-term/2026-09-22-redline-evidence.md @@ -0,0 +1,139 @@ +--- +title: "红线证据批次(无能力放量)" +task_id: "2026-09-22-redline-evidence" +status: done +created: 2026-09-22 +updated: 2026-09-22 +owner: Cursor +task_type: research +task_tier: multi_stage +policy_revision: "agent-governance-2026-09-21-r1 @ local-dev 364ed7f1a3f0393ef77ec4623a9518116ed2cb87" +branch: feature/redline-evidence-2026-09-22 +worktree: .worktrees/redline-evidence-2026-09-22 +base_dev_sha: "364ed7f1a3f0393ef77ec4623a9518116ed2cb87" +write_scope: + - ".doc/ops/short-term/" + - ".doc/ops/checklists/" + - ".doc/test-reports/" + - "internal/docrevision/narrative_test.go" +allowed_environments: [] +head_sha: "" +mutex: "redline-evidence-docs" +review_state: not_required +integration_state: pending +deployment: not_required +cleanup: pending +cleanup_owner: "独立维护者" +cleanup_trigger: "合入 dev 且无进程占用后复查 remove" +remote_sync: not_requested +--- + +# 红线证据批次(无能力放量) + +## 用户目标与授权 + +- 用户目标:在 worktree `redline-evidence-2026-09-22` 落 SWBOOT / FAV / D7 / PI narrative 红线证据;**不**做能力放量、**不**翻转 flag。 +- 交付终点:短文 + test-report + D7 Track E 清单 + 叙事负例单测;本地 commit;不 push。 +- 明确禁止项: + - SWBOOT-10 保持 fail-closed stub(不实现真实 patch/merge/push) + - FAV:无整支 merge、无 live;`FAV_DEGRADED_READ` 保持 false;不启用 `Register(fav)` + - D7:retention 不做物理删除;ADR 保持 `pending-compliance-signoff` + - PI change-report narrative flag 保持 OFF + - `MULTI_INTENT_PLAN_TIER` 保持 legacy;`SEMANTIC_SUMMARY_REPLACE` 保持未接线 + +## 范围与非目标 + +- 允许:只读复验、证据报告、观察窗前置登记、Track E ID-only 清单、叙事 fallback 负例测试。 +- 非目标:能力 release、compose/env flag 翻转、共享环境写操作、FAV live、SWBOOT-10 实施、D7 物理删除、features.go 与 drift worktree 冲突时的重复改码。 + +## 步骤与验收 + +1. [x] SWBOOT:跑验收清单中**无需真实仓写**的 L1–L4 项;L5/L6 标 pending auth — 证据:[报告](../../test-reports/2026-09-22-redline-evidence-swboot-test-report.md) +2. [x] FAV:审计 `ObserveShadow`(WouldStage/WouldPublish false);登记观察窗启动前置;注明 `registry.go` / `dispatcher.go` 硬门 — 见下文 +3. [x] D7:盘点 DB/备份/日志表面 + Track E ID-only 合规清单(无删除)— [清单](../checklists/2026-09-22-d7-track-e-id-only-compliance.md) +4. [x] PI narrative:补强 flag-on fallback 负例;flag 保持 off;`features.go` reason 跳过(drift worktree 已改)— 见下文 + +## FAV 审计与观察窗前置 + +### 代码门禁(本轮未改) + +| 门禁 | 位置 | 行为 | +|------|------|------| +| `Registry.Register(fav)` | `internal/events/registry.go` | 恒返回 `ErrDisabledConsumer` | +| `AssertLiveConsumers` / `LiveConsumers` | 同上 | 拒绝 / 剔除 `fav` | +| `PeekPendingLeases` / `ClaimLeases` / `SetMode` | `internal/events/dispatcher.go` | consumer=`fav` 直接拒绝;claim SQL 含 `d.consumer <> 'fav'` | +| `ObserveShadow` | `internal/app/fav/subscriber.go` | `WouldStage`/`WouldPublish`/`DegradedReadUsed` 恒 false;`FAV_DEGRADED_READ` 为真则拒绝 | +| `HandleLease` | 同上 | 默认 disabled;即使 `Enabled=true` 仍 fail-closed 至 cutover 审批 | + +复验命令(本 worktree,`-short`): + +```text +go test -count=1 -short ./internal/app/fav/ ./internal/events/ -run 'Fav|Register|ObserveShadow|Cutover|Disabled' +→ PASS +``` + +Compose 默认(dev/test/main):`FAV_DEGRADED_READ=false`、`FAV_PUBLISHER_ENABLED=false`(main 硬编码 `"false"`)。 + +### 观察窗启动前置(登记,本轮不开始计时) + +在授权开启 live **之前**须同时满足;任一缺失则观察窗**不得**起算: + +1. 用户显式授权:允许 `fav` durable consumer cutover(单独审批,非整支 merge `feature/final-artifact-vault`)。 +2. 解除 `events` 硬门:`Registry.Register(fav)` / `SetMode(fav,*)` / claim 路径可按审批切片放行(本批禁止改)。 +3. 顺序固定:`staging → artifact → publication`;每级 `shadow → drain → live`。 +4. 每级观察窗下限:≥7 天 **或** ≥N 终态样本(取更严);崩溃/重启幂等证据齐全。 +5. `FAV_DEGRADED_READ` **全程 false**(不随 live 开启)。 +6. 三环境 compose 与 sysadmin 成熟度目录一致声明;无旁路 `cmd/events` 注册。 +7. 独立 mutex `events` 持有者;CURRENT 集成槽位已认领。 +8. 回滚预案:任一级失败立即回 `shadow`/`off`,不前进下一级。 + +**本轮结论**:shadow 可观测;live 观察窗 **未开始**。 + +## D7 表面盘点(无删除) + +静态复验(相对 [W7 盘点](../../audit/2026-09-17-d7-data-inventory.md) / [报告](../../test-reports/2026-09-17-w7-d7-inventory-test-report.md)): + +| 表面 | 位置 / 入口 | 本轮结论 | +|------|-------------|---------| +| 会话正文 | `chat_sessions` / `chat_messages`(`deleted_at` 列) | Go 检索无 `DELETE FROM chat_*`;无 chat soft-delete 写路径 | +| 派生记忆 | `context_memory_chunks`(含 `chat_history`) | 随正文留存;无 retention 作业 | +| 私密文档 | `freeform_documents` soft-delete | 与聊天独立;本轮不触碰 | +| 事件 envelope / DLQ | `event_envelopes` / `event_dead_letters` | Track E:只允许 ID/digest/typed error / allowlist 摘要 | +| 应用日志 | Docker `json-file` / slog | 禁止消息正文;DSN 脱敏 | +| 备份 | `scripts/backup-system-db.sh`(`pg_dump -Fc` 整库) | 含 chat 明文;本轮**未**执行 dump / 未清备份 | +| Agent workspace | `data/*/workspaces` | 非终态;无 30 天清理作业(ADR 待签) | +| FAV 终态 | `final_artifacts` | 与聊天口径解耦;consumer off | + +ADR [`2026-09-17-d7-chat-data-retention`](../../adr/2026-09-17-d7-chat-data-retention.md) 仍为 `pending-compliance-signoff`。合规签字前禁止按 365/180/30 物理删除。 + +## PI narrative + +- 仓库默认 / compose:`PI_CHANGE_REPORT_NARRATIVE=false`(dev/test/main)。 +- 新增/加宽负例:`internal/docrevision/narrative_test.go`(SHA mismatch、wrong schema kind、top-level error_code、empty raw、empty fetcher body、authorized-flip 路径仍默认 off)。 +- **跳过** `internal/sysadmin/features.go` reason 更新:并行 worktree `feature/status-drift-reconcile-2026-09-22` 已改 SEMANTIC_SUMMARY_SHADOW / PI_CHANGE_REPORT_NARRATIVE 文案;本批若改会产生冲突。集成时以 drift 为准。 + +红线保持:`MULTI_INTENT_PLAN_TIER=legacy`;`SEMANTIC_SUMMARY_REPLACE` 未接线。 + +## 验证与证据 + +| 范围 / 被测 SHA | 命令与环境 | 预期 | 实际 / 判定 | 证据 | +|---|---|---|---|---| +| SWBOOT L1–L4(基线 `364ed7f1`) | 见 SWBOOT 报告 | PASS;L5/L6 pending | PASS | [swboot-test-report](../../test-reports/2026-09-22-redline-evidence-swboot-test-report.md);[assets](../../test-reports/assets/2026-09-22-redline-evidence/) | +| FAV shadow / registry | `go test … fav/ events/` | WouldStage/Publish false;Register fail-closed | PASS | [fav-shadow.txt](../../test-reports/assets/2026-09-22-redline-evidence/fav-shadow.txt) | +| D7 / Track E | 静态检索 + 清单 | 无删除;ID-only 清单落盘 | PASS | [checklist](../checklists/2026-09-22-d7-track-e-id-only-compliance.md) | +| PI narrative 负例 | `go test ./internal/docrevision/ -run 'Narrative\|…'` | flag off;flag-on fallback | PASS | [pi-narrative.txt](../../test-reports/assets/2026-09-22-redline-evidence/pi-narrative.txt) | +| arch | `./scripts/arch-check.sh --mode block` | production gate passed | PASS | [arch-check.txt](../../test-reports/assets/2026-09-22-redline-evidence/arch-check.txt) | + +## 收口与交接 + +- 本地提交在本分支;**未**合入 `dev`;**未** push;**无**部署。 +- `features.go` 以 status-drift 分支为准合并。 +- cleanup:合入并确认 tip 为 dev 祖先后由独立维护者 remove worktree。 +- 下一步(需另授权):SWBOOT L5/L6 / SWBOOT-10 安全评审;FAV live 观察窗;D7 合规签字;PI narrative flag 翻转。 + +## 相关链接 + +- 验收清单:[../checklists/2026-09-14-software-project-bootstrap-acceptance.md](../checklists/2026-09-14-software-project-bootstrap-acceptance.md) +- W6 FAV shadow:[2026-09-17-w6-fav-subscriber-shadow.md](./2026-09-17-w6-fav-subscriber-shadow.md) +- W7 D7 盘点:[2026-09-17-w7-d7-inventory.md](./2026-09-17-w7-d7-inventory.md) +- Wave 7 编排:[2026-09-18-wave7-roadmap-orchestration.md](./2026-09-18-wave7-roadmap-orchestration.md) diff --git a/.doc/test-reports/2026-09-22-redline-evidence-swboot-test-report.md b/.doc/test-reports/2026-09-22-redline-evidence-swboot-test-report.md new file mode 100644 index 00000000..2c94194e --- /dev/null +++ b/.doc/test-reports/2026-09-22-redline-evidence-swboot-test-report.md @@ -0,0 +1,111 @@ +# 红线证据 · SWBOOT L1–L4 验收报告(无真实仓写) + +## 一、测试背景 (Background) + +对照 [软件项目自举验收清单](../ops/checklists/2026-09-14-software-project-bootstrap-acceptance.md) 分层 L1–L6。本批**仅**执行无需真实仓库 patch/merge/push 的工程门禁子集;SWBOOT-10 保持 `ERR_SWBOOT10_STUB_ONLY` fail-closed。L5(真实 PI Runner)与 L6(无头浏览器页面)标 **pending auth**,不冒充 PASS。 + +## 二、测试目标 (Test Objectives) + +- 复验 L1 catalog/manifest/policy、L2 领域服务(内存/短测)、L3 前端软件域守卫、L4 stub/固定输出(含 controlled_patch 拒绝) +- 证明 SWBOOT-10 stub 仍拒绝 `ExecuteControlledPatch` / `ApproveMerge` +- 不写真实软件仓;不升 FEATURE-MATRIX「纯软件项目自举」状态 + +## 三、测试方案与环境 (Test Plan & Environment) + +| 项 | 值 | +|----|----| +| OS | Darwin 25.6.0(arm64) | +| 分支 | `feature/redline-evidence-2026-09-22` | +| 基线 SHA | `364ed7f1a3f0393ef77ec4623a9518116ed2cb87` | +| 方法 | `go test -short`、`arch-check --mode block`、`npx tsx --test`(局部) | +| 共享环境 | **未使用**(无 rebuild / 无 Playwright / 无真实 PI) | + +### 分层映射 + +| 层 | 本批范围 | 状态 | +|----|----------|------| +| L1 | `internal/swboot` catalog/preview/allowlist;`internal/workflowdef` software_bootstrap/delivery policy | **RUN / PASS** | +| L2 | `internal/projectworkflow` creation/catalog;controlled_patch API 拒绝(短测,无共享 PG 写) | **RUN / PASS**(隔离短测;空库全迁移 / 并发 PG 未跑) | +| L3 | `web/src/lib/createProjectSubmitGuard.test.ts`(软件域不克隆医疗 7 包) | **RUN / PASS**(单文件;全量 `npm test`/`build` 因本机缺全局 `tsx` PATH 未整套跑,用 `npx tsx` 精确用例) | +| L4 | stub:`RepoAllowlist` patch/merge stub;`PiSoftwareBootstrapAdvisor` 拒 `controlled_patch` | **RUN / PASS** | +| L5 | 真实 PI Agent Runner UI/小队 | **pending auth** | +| L6 | Ubuntu Playwright 真实页面 UI-SW-00–14 | **pending auth** | + +## 四、预期结果 (Expected Results) + +- L1–L4 适用项 PASS;SWBOOT-10 stub 负例 PASS +- L5/L6 明确 pending,不写假 PASS +- 清单 §4 controlled patch 负例中「只在 feature worktree 写入 / 真实 merge」等需真实写的项保持 NOT RUN + +## 五、实际测试结果与判定 (Actual Results & Evaluation) + +### 5.1 L1 Manifest / catalog + +- **Input**:`go test -count=1 -short ./internal/swboot/ ./internal/workflowdef/ -run 'SWBOOT|Software|RepoAllow|Controlled|ProjectTypes|CreationPreview|SuggestDelivery|catalog'` +- **Actual**:`ok` swboot / workflowdef;software_bootstrap 接受 `non_medical_software`、拒绝 `medical_device`;solo+controlled_patch 预览无效并含 `ERR_EXECUTION_MODE_DISABLED` +- **Verdict**:PASS +- **Duration**:<2s + +### 5.2 L2 领域服务(短测) + +- **Input**:`go test -count=1 -short ./internal/projectworkflow/ -run 'SWBOOT|Software|Controlled|software_bootstrap'` +- **Actual**:`ok`;`controlled_patch` 创建路径拒绝(需 SWBOOT-10 executor) +- **Verdict**:PASS(短测范围);空库 schema 升级 / 共享 PG 鉴权并发 **NOT RUN**(需授权环境) +- **Duration**:<1s + +### 5.3 L3 前端软件域守卫 + +- **Input**:`npx --yes tsx --test src/lib/createProjectSubmitGuard.test.ts`(cwd=`web/`) +- **Actual**:4/4 pass,含 `shouldCloneMedicalTemplates` 对 `non_medical_software` 为 false +- **Verdict**:PASS(精确用例);全仓 `npm test` / `npm run build` 本轮未整跑 +- **Duration**:~0.3s(tsx 启动另计) + +### 5.4 L4 Stub / 固定输出 + +- **Input**:`TestRepoAllowlistStub`;`go test ./internal/app/ -run 'PiSoftware|Advise|controlled'` +- **Actual**:`ExecuteControlledPatch` / `ApproveMerge` → `ERR_SWBOOT10_STUB_ONLY`;advisor 拒 `controlled_patch` +- **Verdict**:PASS(stub-only,非真实执行器) +- **Duration**:<1s + +### 5.5 工程门禁冒烟 + +- **Input**:`./scripts/arch-check.sh --mode block`;`go vet ./internal/swboot/ ./internal/app/fav/ ./internal/events/ ./internal/docrevision/` +- **Actual**:`arch-check block: production gate passed`;vet 无输出 +- **Verdict**:PASS +- **Duration**:~1.6s + +### 5.6 L5 / L6 + +- **Input**:未请求真实 PI / Playwright 授权;未占集成槽位 +- **Actual**:未执行 +- **Verdict**:**pending auth**(NOT RUN) + +### 5.7 清单 §4 未来 controlled patch 负例 + +| 项 | 本批 | +|----|------| +| repo 未绑定 / 非 allowlist / `.env` 排除 | 单元 stub 部分覆盖 allowlist;真实绑定 **NOT RUN** | +| 只在 feature worktree 写入 | **NOT RUN**(禁止真实写) | +| 测试失败不自动 merge / 批准≠merge/push | stub `ApproveMerge` PASS;真实路径 **NOT RUN** | +| 保护分支 force push 拒绝 | **NOT RUN** | + +## 六、自动化测试套件汇总 (Test Suite Execution) + +```text +go test -count=1 -short ./internal/swboot/ ./internal/workflowdef/ ./internal/projectworkflow/ ./internal/app/ +go test -count=1 -short ./internal/app/fav/ ./internal/events/ -run 'Fav|Register|ObserveShadow|Cutover|Disabled' +./scripts/arch-check.sh --mode block +npx tsx --test web/src/lib/createProjectSubmitGuard.test.ts +# L5/L6: NOT RUN (pending auth) +``` + +全部适用命令退出码 0。未运行共享环境 dump、未写业务仓。 + +命令输出片段(脱敏/截断):[assets/2026-09-22-redline-evidence/](./assets/2026-09-22-redline-evidence/)(`swboot-l1.txt`、`arch-check.txt`、`env.txt`)。 + +## 七、测试结论与后续 (Conclusion & Next Steps) + +- **SWBOOT L1–L4(无真实仓写子集)PASS**;SWBOOT-10 仍为 fail-closed stub。 +- **L5/L6 pending auth**:需占用集成槽位、真实 PI Runner、Playwright 容器后再跑 UI-SW-00–14。 +- 升 FEATURE-MATRIX done 仍须独立安全评审后的受控执行器(本批不做)。 +- 任务短文:[../ops/short-term/2026-09-22-redline-evidence.md](../ops/short-term/2026-09-22-redline-evidence.md) diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/arch-check.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/arch-check.txt new file mode 100644 index 00000000..5e65d3dc --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/arch-check.txt @@ -0,0 +1,7 @@ +=== arch-check === +arch-check mode=block +boundaries=/private/tmp/Agent-System/.worktrees/redline-evidence-2026-09-22/.doc/system-design/module-boundaries.yaml +ok chat-agent-system/internal/archtest 0.976s +ok chat-agent-system/internal/archtest 0.494s +arch-check block: production gate passed +EXIT:0 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-no-physical-delete.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-no-physical-delete.txt new file mode 100644 index 00000000..e6181521 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-no-physical-delete.txt @@ -0,0 +1,11 @@ +=== chat physical delete / soft-delete write paths === +NONE +=== chat_messages UPDATE (content edit paths, not retention) === +internal/ctxmgr/recategorize.go:99: UPDATE chat_messages +internal/ctxmgr/message_update.go:30: UPDATE chat_messages +internal/ctxmgr/message_update.go:77: UPDATE chat_messages +internal/ctxmgr/message_update.go:118: UPDATE chat_messages +=== ADR compliance field === +6:compliance: pending-compliance-signoff +18:**提议 / 工程草案**(2026-09-17)。数字与删除口径标 `pending-compliance-signoff`:合规签字或改数前,W7 仅允许数据分类盘点与现状审计,不得按未签字数字执行物理删除或备份清除。接受后状态改为 `accepted`。 +54:- W7 可按本草案启动盘点;物理删除与备份清除在 `pending-compliance-signoff` 解除前禁止按建议数字自动执行。 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-static-inventory.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-static-inventory.txt new file mode 100644 index 00000000..9a2a8666 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-static-inventory.txt @@ -0,0 +1,18 @@ +=== D7 DELETE / soft-delete / retention === +internal/artifactvault/registry.go:265: source_run_id, payload_sha256, terminal_state, policy_version, publication_status, retention_class +internal/artifactvault/registry.go:296: terminal_state, policy_version, publication_status, retention_class, requested_at +internal/artifactvault/types.go:134: RetentionClass string `json:"retention_class,omitempty"` +---backup--- +3:# 系统业务库备份脚本 (backup-system-db.sh) +7:# 2. 采用 pg_dump -Fc 导出系统业务库(含 task_runs、工作流治理等); +11:# ./scripts/backup-system-db.sh <目标备份目录> +15:# 2. ${HOME}/.secrets/backup-env 中 SYSTEM_DB_PASSWORD_ +17:# 当前实现经 docker exec 以库内角色执行 pg_dump,通常不需要宿主机口令; +32: $0 dev ./backups/system_dev_20260912 +33: $0 test /tmp/agent_system_test_backup +36: SYSTEM_DB_PASSWORD 或 \${HOME}/.secrets/backup-env 或 configs/.env. +81: local secrets_file="${HOME}/.secrets/backup-env" +115:# 仅保留变量供未来宿主机连接扩展;当前 pg_dump 走容器内角色。 +161:echo "[导出] pg_dump -Fc ${DB_NAME} ..." +163:docker exec "${CONTAINER_NAME}" pg_dump -U "${DB_USER}" -d "${DB_NAME}" -Fc -Z 6 -f "${TMP_CONTAINER_DUMP}" +---log driver--- diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e-payload-fields.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e-payload-fields.txt new file mode 100644 index 00000000..7d240734 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e-payload-fields.txt @@ -0,0 +1,4 @@ +all_payload_fields_sorted= ['candidate_id', 'doc_id', 'environment', 'impacted_doc_id', 'plan_id', 'project_id', 'report_id', 'revision', 'risk', 'run_id', 'session_id', 'snapshot_id', 'status', 'step_id', 'summary_ref', 'task_key', 'user_id', 'version_no'] +forbidden_hits= NONE +has_summary_ref= True +contract_count= 11 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e.txt new file mode 100644 index 00000000..6e815ac5 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/d7-track-e.txt @@ -0,0 +1,19 @@ +=== Track E contracts / content forbidden === +internal/events/registry.go:50: {EventName: EventIntentPlanAwaiting, SchemaVersion: 1, Enabled: true, Producers: []string{"app/intentflow", "events"}, Consumers: []string{ConsumerProjection, ConsumerNotify}, PayloadFields: []string{"plan_id", "revision", "status", "environment", "project_id", "user_id", "session_id", "summary_ref"}}, +internal/events/registry.go:51: {EventName: EventIntentPlanStepChanged, SchemaVersion: 1, Enabled: true, Producers: []string{"app/intentflow", "events"}, Consumers: []string{ConsumerProjection, ConsumerNotify}, PayloadFields: []string{"plan_id", "revision", "step_id", "status", "environment", "project_id", "user_id", "session_id", "summary_ref"}}, +internal/events/registry.go:52: {EventName: EventIntentPlanCompleted, SchemaVersion: 1, Enabled: true, Producers: []string{"app/intentflow", "events"}, Consumers: []string{ConsumerProjection, ConsumerNotify}, PayloadFields: []string{"plan_id", "revision", "status", "environment", "project_id", "user_id", "session_id", "summary_ref"}}, +internal/events/registry.go:148: PayloadFields []string `json:"payload_fields"` +.doc/system-design/module-boundaries.yaml:141: - doccontent +.doc/system-design/module-boundaries.yaml:147: - doccontent +.doc/system-design/module-boundaries.yaml:149: doccontent: [] +.doc/system-design/module-boundaries.yaml:343: artifact_payload_staging: +=== RUN TestClassifySafeKnownContract +--- PASS: TestClassifySafeKnownContract (0.00s) +=== RUN TestSanitizeSummaryDropsPayload +--- PASS: TestSanitizeSummaryDropsPayload (0.00s) +=== RUN TestIntentPlanContractsOmitSensitivePayload +--- PASS: TestIntentPlanContractsOmitSensitivePayload (0.00s) +=== RUN TestRegistryParityWithYAML +--- PASS: TestRegistryParityWithYAML (0.00s) +PASS +ok chat-agent-system/internal/events 0.490s diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/env.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/env.txt new file mode 100644 index 00000000..134949c1 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/env.txt @@ -0,0 +1,6 @@ +SHA=364ed7f1a3f0393ef77ec4623a9518116ed2cb87 DATE=2026-09-22T13:25:12Z OS=Darwin GO=go1.27.1 +darwin +arm64 +go1.27.1 +Darwin WeiCHENdeMac-mini.local 25.6.0 Darwin Kernel Version 25.6.0: Fri Jul 31 19:17:26 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T6041 arm64 +2026-09-22T13:26:20Z diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-compose-defaults.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-compose-defaults.txt new file mode 100644 index 00000000..f6954328 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-compose-defaults.txt @@ -0,0 +1,6 @@ +docker-compose.main.yml:132: FAV_DEGRADED_READ: "false" +docker-compose.main.yml:133: FAV_PUBLISHER_ENABLED: "false" +docker-compose.test.yml:129: FAV_DEGRADED_READ: ${FAV_DEGRADED_READ:-false} +docker-compose.test.yml:130: FAV_PUBLISHER_ENABLED: ${FAV_PUBLISHER_ENABLED:-false} +docker-compose.dev.yml:133: FAV_DEGRADED_READ: ${FAV_DEGRADED_READ:-false} +docker-compose.dev.yml:134: FAV_PUBLISHER_ENABLED: ${FAV_PUBLISHER_ENABLED:-false} diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-register-gate.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-register-gate.txt new file mode 100644 index 00000000..61dcaa83 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-register-gate.txt @@ -0,0 +1,10 @@ +=== RUN TestAssertLiveConsumersRejectsFAV +--- PASS: TestAssertLiveConsumersRejectsFAV (0.00s) +=== RUN TestAssertLiveConsumersRejectsUnknown +--- PASS: TestAssertLiveConsumersRejectsUnknown (0.00s) +=== RUN TestLiveConsumersOmitsFAV +--- PASS: TestLiveConsumersOmitsFAV (0.00s) +=== RUN TestManifestJSONIncludesDisabledFAV +--- PASS: TestManifestJSONIncludesDisabledFAV (0.00s) +PASS +ok chat-agent-system/internal/events 0.321s diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-registry-gate-lines.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-registry-gate-lines.txt new file mode 100644 index 00000000..4755c8bb --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-registry-gate-lines.txt @@ -0,0 +1,9 @@ +13: ConsumerFAV = "fav" +22: EventFAVArtifactStaged = "fav.artifact_staged" +49: {EventName: EventFAVArtifactStaged, SchemaVersion: 1, Enabled: false, Producers: []string{"app/docflow"}, Consumers: []string{ConsumerFAV}, PayloadFields: []string{"doc_id", "snapshot_id"}}, +84: if cons == ConsumerFAV || strings.EqualFold(cons, "fav") { +85: return fmt.Errorf("%w: fav", ErrDisabledConsumer) +102: if n == ConsumerFAV { +119:func (r *Registry) Register(consumer string) error { +123: if consumer == ConsumerFAV || strings.EqualFold(consumer, "fav") { +124: return fmt.Errorf("%w: fav", ErrDisabledConsumer) diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-shadow.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-shadow.txt new file mode 100644 index 00000000..7ebbbbdc --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-shadow.txt @@ -0,0 +1,20 @@ +=== FAV shadow === +=== RUN TestFavSubscriberDefaultsDisabled +--- PASS: TestFavSubscriberDefaultsDisabled (0.00s) +=== RUN TestFavSubscriberStillDisabledWhenFlagOnWithoutRegistryApproval +--- PASS: TestFavSubscriberStillDisabledWhenFlagOnWithoutRegistryApproval (0.00s) +=== RUN TestFavSubscriberShadowComputesIntendedOutcomeWithoutWrites +--- PASS: TestFavSubscriberShadowComputesIntendedOutcomeWithoutWrites (0.00s) +PASS +ok chat-agent-system/internal/app/fav 0.293s +=== RUN TestValidateOpsCutoverRejectsFavAndNonDev +--- PASS: TestValidateOpsCutoverRejectsFavAndNonDev (0.00s) +=== RUN TestValidateOpsCutoverAllowsThreeConsumers +--- PASS: TestValidateOpsCutoverAllowsThreeConsumers (0.00s) +=== RUN TestClaimIDsDisabled +--- PASS: TestClaimIDsDisabled (0.00s) +=== RUN TestManifestJSONIncludesDisabledFAV +--- PASS: TestManifestJSONIncludesDisabledFAV (0.00s) +PASS +ok chat-agent-system/internal/events 0.303s +EXIT:0 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-static-audit.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-static-audit.txt new file mode 100644 index 00000000..ebe42909 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-static-audit.txt @@ -0,0 +1,19 @@ +=== FAV compose defaults === +--- +internal/events/registry.go:81: return fmt.Errorf("%w: %s", ErrDisabledConsumer, eventName) +internal/events/registry.go:85: return fmt.Errorf("%w: fav", ErrDisabledConsumer) +internal/events/registry.go:124: return fmt.Errorf("%w: fav", ErrDisabledConsumer) +internal/app/fav/subscriber.go:4:// a separate approval. Registry.Register("fav") and SetMode(fav, live) already +internal/app/fav/subscriber.go:41: return fmt.Errorf("%w: fav", events.ErrDisabledConsumer) +internal/app/fav/subscriber.go:44: return fmt.Errorf("%w: fav publisher cutover not approved", events.ErrDisabledConsumer) +internal/app/fav/subscriber.go:53:// as a write receipt; WouldStage/WouldPublish stay false in this slice. +internal/app/fav/subscriber.go:59: WouldStage bool +internal/app/fav/subscriber.go:60: WouldPublish bool +internal/app/fav/subscriber.go:64:// ObserveShadow computes the intended FAV outcome without business side effects. +internal/app/fav/subscriber.go:66:func (c *EventConsumer) ObserveShadow(lease events.Lease) (IntendedOutcome, error) { +internal/app/fav/subscriber.go:74: return out, fmt.Errorf("%w: fav", events.ErrDisabledConsumer) +internal/app/fav/subscriber.go:79: out.WouldStage = false +internal/app/fav/subscriber.go:80: out.WouldPublish = false +internal/events/dispatcher.go:126: return nil, fmt.Errorf("%w: fav", ErrDisabledConsumer) +internal/events/dispatcher.go:165: return nil, fmt.Errorf("%w: fav", ErrDisabledConsumer) +internal/events/dispatcher.go:338: return fmt.Errorf("%w: fav", ErrDisabledConsumer) diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-vault-status.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-vault-status.txt new file mode 100644 index 00000000..8e40c723 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/fav-vault-status.txt @@ -0,0 +1,6 @@ +=== RUN TestVaultDefaultsStayDisabled +--- PASS: TestVaultDefaultsStayDisabled (0.00s) +=== RUN TestVaultStatusReportsOff +--- PASS: TestVaultStatusReportsOff (0.00s) +PASS +ok chat-agent-system/internal/server 0.681s diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/head-sha.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/head-sha.txt new file mode 100644 index 00000000..a8951ce1 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/head-sha.txt @@ -0,0 +1 @@ +364ed7f1a3f0393ef77ec4623a9518116ed2cb87 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/l3-build-attempt.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/l3-build-attempt.txt new file mode 100644 index 00000000..8d4f99df --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/l3-build-attempt.txt @@ -0,0 +1,5 @@ + +> shuimu-workbench@1.0.0 build +> tsc && vite build && node scripts/precompress-dist.mjs + +sh: tsc: command not found diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/l3-frontend.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/l3-frontend.txt new file mode 100644 index 00000000..6b7035c1 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/l3-frontend.txt @@ -0,0 +1,7 @@ +=== L3 frontend quick === + +> shuimu-workbench@1.0.0 test +> tsx --test "src/**/!(*InteractiveCard*|*chatWindowArtifact*|*deliverableSync*).test.ts" && vitest run src/components/chat/InteractiveCard.test.ts src/components/chat/chatWindowArtifact.test.ts src/components/chat/deliverableSync.test.ts --run + +sh: tsx: command not found +EXIT:0 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/narrative-diff-stat.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/narrative-diff-stat.txt new file mode 100644 index 00000000..e69de29b diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/pi-flag-defaults.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/pi-flag-defaults.txt new file mode 100644 index 00000000..11d7f4cb --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/pi-flag-defaults.txt @@ -0,0 +1,5 @@ +=== PI narrative flag defaults === +docker-compose.test.yml:128: PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false} +docker-compose.main.yml:131: PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false} +docker-compose.dev.yml:131: PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false} +.env.example:117:PI_CHANGE_REPORT_NARRATIVE=false diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/pi-narrative.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/pi-narrative.txt new file mode 100644 index 00000000..cece13d7 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/pi-narrative.txt @@ -0,0 +1,50 @@ +=== PI narrative === +=== RUN TestMaterializeAgentOutput_FlagOnFetcherNarrativeComplete +--- PASS: TestMaterializeAgentOutput_FlagOnFetcherNarrativeComplete (0.00s) +=== RUN TestPIChangeReportNarrativeFlagDefaultOff +--- PASS: TestPIChangeReportNarrativeFlagDefaultOff (0.00s) +=== RUN TestResolveChangeReport_FlagOffUsesDeterministic +--- PASS: TestResolveChangeReport_FlagOffUsesDeterministic (0.00s) +=== RUN TestResolveChangeReport_FlagOnValidNarrativeKeepsServerRisk +--- PASS: TestResolveChangeReport_FlagOnValidNarrativeKeepsServerRisk (0.00s) +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/bad_json +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/bad_schema_version +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/wrong_schema_kind +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/digest_mismatch +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/from_sha_mismatch +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/to_sha_mismatch +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/timeout_envelope +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/top-level_error_code +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/partial_flag +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/empty_summary +=== RUN TestResolveChangeReport_FlagOnMalformedFullFallback/empty_raw +--- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/bad_json (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/bad_schema_version (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/wrong_schema_kind (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/digest_mismatch (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/from_sha_mismatch (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/to_sha_mismatch (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/timeout_envelope (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/top-level_error_code (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/partial_flag (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/empty_summary (0.00s) + --- PASS: TestResolveChangeReport_FlagOnMalformedFullFallback/empty_raw (0.00s) +=== RUN TestResolveChangeReport_AuthorizedFlipPathKeepsFlagOffDefault +--- PASS: TestResolveChangeReport_AuthorizedFlipPathKeepsFlagOffDefault (0.00s) +=== RUN TestCreateAgentCandidate_FlagOffIgnoresNarrativeEnrichment +--- PASS: TestCreateAgentCandidate_FlagOffIgnoresNarrativeEnrichment (0.00s) +=== RUN TestCreateAgentCandidate_FlagOnTimeoutFallsBackWithoutError +--- PASS: TestCreateAgentCandidate_FlagOnTimeoutFallsBackWithoutError (0.00s) +=== RUN TestCreateAgentCandidate_FlagOnFetcherApplies +--- PASS: TestCreateAgentCandidate_FlagOnFetcherApplies (0.00s) +=== RUN TestCreateAgentCandidate_FlagOnFetcherErrorFallsBack +--- PASS: TestCreateAgentCandidate_FlagOnFetcherErrorFallsBack (0.00s) +=== RUN TestCreateAgentCandidate_FlagOnEmptyFetcherBodyFallsBack +--- PASS: TestCreateAgentCandidate_FlagOnEmptyFetcherBodyFallsBack (0.00s) +=== RUN TestNarrativeModelInput_IncludesClippedChangeText +--- PASS: TestNarrativeModelInput_IncludesClippedChangeText (0.00s) +PASS +ok chat-agent-system/internal/docrevision 0.350s +EXIT:0 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l1.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l1.txt new file mode 100644 index 00000000..fb839a4b --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l1.txt @@ -0,0 +1,29 @@ +=== SWBOOT L1 unit === +=== RUN TestBuildCreationPreview_RejectsControlledPatchAndSoloPrivacy +--- PASS: TestBuildCreationPreview_RejectsControlledPatchAndSoloPrivacy (0.00s) +=== RUN TestRepoAllowlistStub +--- PASS: TestRepoAllowlistStub (0.00s) +PASS +ok chat-agent-system/internal/swboot 0.290s +=== RUN TestOfficialWorkflowKeys +--- PASS: TestOfficialWorkflowKeys (0.00s) +=== RUN TestCompatibleWithProductDomain +--- PASS: TestCompatibleWithProductDomain (0.00s) +=== RUN TestValidateDefinitionV2_SoftwareManifests +--- PASS: TestValidateDefinitionV2_SoftwareManifests (0.00s) +PASS +ok chat-agent-system/internal/workflowdef 0.383s +=== RUN TestCreateSoftwareProject_NoMedicalDefaults +--- PASS: TestCreateSoftwareProject_NoMedicalDefaults (0.00s) +PASS +ok chat-agent-system/internal/projectworkflow 0.600s +=== RUN TestPiSoftwareBootstrapAdvisor_NoTools +--- PASS: TestPiSoftwareBootstrapAdvisor_NoTools (0.00s) +=== RUN TestPiSoftwareBootstrapAdvisor_RejectsControlledPatch +--- PASS: TestPiSoftwareBootstrapAdvisor_RejectsControlledPatch (0.00s) +=== RUN TestPiSoftwareBootstrapAdvisor_LiveMiniMax + pi_swboot_test.go:55: set RUN_PI_E2E=1 for live PI software bootstrap +--- SKIP: TestPiSoftwareBootstrapAdvisor_LiveMiniMax (0.00s) +PASS +ok chat-agent-system/internal/app 0.722s +EXIT:0 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l2-short.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l2-short.txt new file mode 100644 index 00000000..076ff3af --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l2-short.txt @@ -0,0 +1,82 @@ +=== SWBOOT L2 short (authz/create/reject) === +=== RUN TestPG_GoverningAccess_CompletedAndTerminatedDeny + access_governing_pg_test.go:157: TEST_DATABASE_URL not set; skipping PostgreSQL integration test +--- SKIP: TestPG_GoverningAccess_CompletedAndTerminatedDeny (0.00s) +=== RUN TestPG_GoverningAccess_QueryErrorFailClosed +--- PASS: TestPG_GoverningAccess_QueryErrorFailClosed (0.00s) +=== RUN TestPG_ActivateOfficialWorkflowRuntimeMetadata + activation_official_pg_test.go:18: TEST_DATABASE_URL not set; skipping PostgreSQL integration test +--- SKIP: TestPG_ActivateOfficialWorkflowRuntimeMetadata (0.00s) +=== RUN TestPG_ActivationAndStageAdvance + pg_integration_test.go:49: TEST_DATABASE_URL not set; skipping PostgreSQL integration test +--- SKIP: TestPG_ActivationAndStageAdvance (0.00s) +=== RUN TestPG_RDevActivateWithNAClose + rdev_pg_test.go:15: TEST_DATABASE_URL not set; skipping PostgreSQL integration test +--- SKIP: TestPG_RDevActivateWithNAClose (0.00s) +=== RUN TestActiveWorkflowScope_NoLiveInstanceReturnsEmpty +--- PASS: TestActiveWorkflowScope_NoLiveInstanceReturnsEmpty (0.00s) +=== RUN TestTaskKeyInScope_EmptyAllowsAll +--- PASS: TestTaskKeyInScope_EmptyAllowsAll (0.00s) +=== RUN TestTaskKeyInScope_CanonicalAlias +--- PASS: TestTaskKeyInScope_CanonicalAlias (0.00s) +=== CONT TestClassifyDocDuty +=== RUN TestClassifyDocDuty/all_zero +=== CONT TestNormalizeAuthorOnlyDuties +--- PASS: TestNormalizeAuthorOnlyDuties (0.00s) +=== PAUSE TestClassifyDocDuty/all_zero +=== RUN TestClassifyDocDuty/negative +=== PAUSE TestClassifyDocDuty/negative +=== RUN TestClassifyDocDuty/one_role +=== PAUSE TestClassifyDocDuty/one_role +=== RUN TestClassifyDocDuty/two_roles +=== PAUSE TestClassifyDocDuty/two_roles +=== RUN TestClassifyDocDuty/full_distinct +=== PAUSE TestClassifyDocDuty/full_distinct +=== RUN TestClassifyDocDuty/full_duplicate +=== PAUSE TestClassifyDocDuty/full_duplicate +=== CONT TestClassifyDocDuty/all_zero +=== CONT TestResolveDocTaskDuty +--- PASS: TestResolveDocTaskDuty (0.00s) +=== CONT TestCountFullDutyDocs +--- PASS: TestCountFullDutyDocs (0.00s) +=== CONT TestValidateDocTaskDuties +--- PASS: TestValidateDocTaskDuties (0.00s) +=== CONT TestNormalizeDocTaskDuties +--- PASS: TestNormalizeDocTaskDuties (0.00s) +=== CONT TestDocTaskRequiresDuty +--- PASS: TestDocTaskRequiresDuty (0.00s) +=== CONT TestEvaluateStageReadiness_AuthorOnlyIsManaged +--- PASS: TestEvaluateStageReadiness_AuthorOnlyIsManaged (0.00s) +=== CONT TestComputePlanHash_LegacySnapshotStableWithoutDutyPolicy +--- PASS: TestComputePlanHash_LegacySnapshotStableWithoutDutyPolicy (0.00s) +=== CONT TestEvaluateStageReadiness_DutyModes +--- PASS: TestEvaluateStageReadiness_DutyModes (0.00s) +=== CONT TestValidateAuthorOnlyDocTaskDuties +=== CONT TestClassifyDocDuty/two_roles +--- PASS: TestValidateAuthorOnlyDocTaskDuties (0.00s) +=== CONT TestClassifyDocDuty/one_role +=== CONT TestClassifyDocDuty/negative +=== CONT TestClassifyDocDuty/full_duplicate +=== CONT TestClassifyDocDuty/full_distinct +--- PASS: TestClassifyDocDuty (0.00s) + --- PASS: TestClassifyDocDuty/all_zero (0.00s) + --- PASS: TestClassifyDocDuty/two_roles (0.00s) + --- PASS: TestClassifyDocDuty/negative (0.00s) + --- PASS: TestClassifyDocDuty/one_role (0.00s) + --- PASS: TestClassifyDocDuty/full_duplicate (0.00s) + --- PASS: TestClassifyDocDuty/full_distinct (0.00s) +PASS +ok chat-agent-system/internal/projectinstance 0.547s +=== RUN TestProjectTypesCatalog +--- PASS: TestProjectTypesCatalog (0.00s) +=== RUN TestBuildCreationPreview_SelfBootstrapSolo +--- PASS: TestBuildCreationPreview_SelfBootstrapSolo (0.00s) +=== RUN TestBuildCreationPreview_RejectsControlledPatchAndSoloPrivacy +--- PASS: TestBuildCreationPreview_RejectsControlledPatchAndSoloPrivacy (0.00s) +=== RUN TestRepoAllowlistStub +--- PASS: TestRepoAllowlistStub (0.00s) +=== RUN TestSuggestDeliveryAfterBootstrapNeverActivates +--- PASS: TestSuggestDeliveryAfterBootstrapNeverActivates (0.00s) +PASS +ok chat-agent-system/internal/swboot 0.796s +EXIT:0 diff --git a/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l4-stub.txt b/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l4-stub.txt new file mode 100644 index 00000000..4ec3bf49 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-22-redline-evidence/swboot-l4-stub.txt @@ -0,0 +1,11 @@ +=== SWBOOT L4 stub advisor === +=== RUN TestPiSoftwareBootstrapAdvisor_NoTools +--- PASS: TestPiSoftwareBootstrapAdvisor_NoTools (0.00s) +=== RUN TestPiSoftwareBootstrapAdvisor_RejectsControlledPatch +--- PASS: TestPiSoftwareBootstrapAdvisor_RejectsControlledPatch (0.00s) +=== RUN TestPiSoftwareBootstrapAdvisor_LiveMiniMax + pi_swboot_test.go:55: set RUN_PI_E2E=1 for live PI software bootstrap +--- SKIP: TestPiSoftwareBootstrapAdvisor_LiveMiniMax (0.00s) +PASS +ok chat-agent-system/internal/app 0.309s +EXIT:0 diff --git a/internal/docrevision/narrative_test.go b/internal/docrevision/narrative_test.go index 8b6111f0..b6ebd3e0 100644 --- a/internal/docrevision/narrative_test.go +++ b/internal/docrevision/narrative_test.go @@ -123,16 +123,22 @@ func TestResolveChangeReport_FlagOnMalformedFullFallback(t *testing.T) { t.Cleanup(docrevision.ResetPIChangeReportNarrativeForTest) fromSHA, toSHA, ruleRisk, impacts, diff := sampleDiff(t) + digest := docrevision.NarrativeInputDigest(fromSHA, toSHA, ruleRisk, impacts, diff.Hunks) cases := []struct { name string raw string }{ {"bad json", `{not-json`}, {"bad schema version", `{"schema_version":99,"schema_kind":"change_report_narrative_v1","input_digest":"x","summary":["a"]}`}, + {"wrong schema kind", `{"schema_version":1,"schema_kind":"other_v1","input_digest":"` + digest + `","from_sha":"` + fromSHA + `","to_sha":"` + toSHA + `","summary":["PI only"]}`}, {"digest mismatch", mustNarrativeJSON(t, fromSHA, toSHA, "deadbeef", []string{"ok"})}, + {"from_sha mismatch", mustNarrativeJSONWithSHAs(t, "wrong-from", toSHA, digest, []string{"PI only"})}, + {"to_sha mismatch", mustNarrativeJSONWithSHAs(t, fromSHA, "wrong-to", digest, []string{"PI only"})}, {"timeout envelope", `{"result_v1":{"outcome":"error","error_code":"TIMEOUT"},"narrative":{"schema_version":1,"schema_kind":"change_report_narrative_v1"}}`}, + {"top-level error_code", `{"schema_version":1,"schema_kind":"change_report_narrative_v1","input_digest":"` + digest + `","from_sha":"` + fromSHA + `","to_sha":"` + toSHA + `","error_code":"RUNNER_FAILED","summary":["PI only"]}`}, {"partial flag", `{"schema_version":1,"schema_kind":"change_report_narrative_v1","input_digest":"x","partial":true,"summary":["a"]}`}, - {"empty summary", mustNarrativeJSON(t, fromSHA, toSHA, docrevision.NarrativeInputDigest(fromSHA, toSHA, ruleRisk, impacts, diff.Hunks), nil)}, + {"empty summary", mustNarrativeJSON(t, fromSHA, toSHA, digest, nil)}, + {"empty raw", ""}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -154,11 +160,19 @@ func TestResolveChangeReport_FlagOnMalformedFullFallback(t *testing.T) { t.Fatalf("mixed narrative field leaked: %q", s) } } + if rep.DeterministicFacts["narrative_applied"] == true { + t.Fatal("fallback must not claim narrative_applied") + } }) } } func mustNarrativeJSON(t *testing.T, fromSHA, toSHA, digest string, summary []string) string { + t.Helper() + return mustNarrativeJSONWithSHAs(t, fromSHA, toSHA, digest, summary) +} + +func mustNarrativeJSONWithSHAs(t *testing.T, fromSHA, toSHA, digest string, summary []string) string { t.Helper() raw, err := json.Marshal(map[string]any{ "schema_version": 1, @@ -174,6 +188,43 @@ func mustNarrativeJSON(t *testing.T, fromSHA, toSHA, digest string, summary []st return string(raw) } +// TestResolveChangeReport_AuthorizedFlipPathKeepsFlagOffDefault documents the +// pre-authorization contract: default remains off; when a future owner flips the +// flag for a controlled window, malformed PI output still full-falls-back. +func TestResolveChangeReport_AuthorizedFlipPathKeepsFlagOffDefault(t *testing.T) { + docrevision.ResetPIChangeReportNarrativeForTest() + t.Cleanup(docrevision.ResetPIChangeReportNarrativeForTest) + if docrevision.PIChangeReportNarrativeEnabled() { + t.Fatal("PI_CHANGE_REPORT_NARRATIVE must remain off until separately authorized") + } + + fromSHA, toSHA, ruleRisk, impacts, diff := sampleDiff(t) + digest := docrevision.NarrativeInputDigest(fromSHA, toSHA, ruleRisk, impacts, diff.Hunks) + piRaw := mustNarrativeJSON(t, fromSHA, toSHA, digest, []string{"should stay ignored while off"}) + + offRep, err := docrevision.ResolveChangeReport(piRaw, fromSHA, toSHA, ruleRisk, impacts, diff) + if err != nil { + t.Fatal(err) + } + if offRep.AnalysisStatus != docrevision.AnalysisFallback { + t.Fatalf("default-off must ignore PI narrative, got %s", offRep.AnalysisStatus) + } + + docrevision.SetPIChangeReportNarrativeForTest(true) + onBad, err := docrevision.ResolveChangeReport(`{"schema_version":1,"schema_kind":"change_report_narrative_v1"}`, fromSHA, toSHA, ruleRisk, impacts, diff) + if err != nil { + t.Fatal(err) + } + if onBad.AnalysisStatus != docrevision.AnalysisFallback { + t.Fatalf("authorized-on malformed must still fallback, got %s", onBad.AnalysisStatus) + } + + docrevision.SetPIChangeReportNarrativeForTest(false) + if docrevision.PIChangeReportNarrativeEnabled() { + t.Fatal("test toggle must restore off before exit") + } +} + func TestCreateAgentCandidate_FlagOffIgnoresNarrativeEnrichment(t *testing.T) { docrevision.SetPIChangeReportNarrativeForTest(false) t.Cleanup(docrevision.ResetPIChangeReportNarrativeForTest) @@ -289,6 +340,32 @@ func TestCreateAgentCandidate_FlagOnFetcherErrorFallsBack(t *testing.T) { } } +func TestCreateAgentCandidate_FlagOnEmptyFetcherBodyFallsBack(t *testing.T) { + docrevision.SetPIChangeReportNarrativeForTest(true) + t.Cleanup(docrevision.ResetPIChangeReportNarrativeForTest) + + svc := docrevision.NewService(nil) + svc.NarrativeFetcher = func(_ context.Context, _ docrevision.NarrativeFetchSpec) (string, error) { + return " ", nil + } + ctx := context.Background() + st, err := svc.OpenTemplateVersion(ctx, "doc_flag_on_empty", "# base\n\nbody\n", docrevision.ActorSnapshot{DisplayName: "t"}, 1, "") + if err != nil { + t.Fatal(err) + } + cand, rep, err := svc.CreateAgentCandidate(ctx, docrevision.AgentCandidateRequest{ + DocID: "doc_flag_on_empty", RunID: "r-empty", InputSnapshotID: st.HeadSnapshotID, + OutputMarkdown: "# base\n\nbody changed\n", + ActorKind: docrevision.ActorAgent, + }) + if err != nil { + t.Fatalf("empty fetcher body must full-fallback without error, got %v", err) + } + if cand == nil || rep == nil || rep.AnalysisStatus != docrevision.AnalysisFallback { + t.Fatalf("want fallback candidate, got cand=%v rep=%+v", cand, rep) + } +} + func TestNarrativeModelInput_IncludesClippedChangeText(t *testing.T) { long := strings.Repeat("甲", 280) spec := docrevision.NarrativeFetchSpec{