agentic-flow-kit 1.8.7 → 2.0.0
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/README.md +113 -148
- package/bin/flow-kit.mjs +5 -2
- package/locales/en/installer.json +25 -0
- package/locales/en/overrides/.agents/commands/deploy.md +52 -0
- package/locales/en/overrides/.agents/commands/maintain.md +76 -0
- package/locales/en/overrides/.agents/commands/new-task.md +89 -0
- package/locales/en/overrides/.agents/commands/pipeline-run.md +56 -0
- package/locales/en/overrides/.agents/commands/pr-review.md +30 -0
- package/locales/en/overrides/.agents/commands/review.md +93 -0
- package/locales/en/overrides/.agents/commands/stages.md +256 -0
- package/locales/en/overrides/.agents/notes/pr-review-cost-discipline.md +48 -0
- package/locales/en/overrides/.agents/notes/process-order-rule.md +21 -0
- package/locales/en/overrides/.agents/notes/runtime-env.local.example.md +21 -0
- package/locales/en/overrides/.agents/pipelines/_TEMPLATE.md +75 -0
- package/locales/en/overrides/.agents/pipelines/pipeline-closing.md +28 -0
- package/locales/en/overrides/.agents/roles/implementer.md +28 -0
- package/locales/en/overrides/.agents/roles/independent-reviewer.md +39 -0
- package/locales/en/overrides/.agents/roles/pr-adjudicator.md +27 -0
- package/locales/en/overrides/.agents/roles/pr-finder.md +51 -0
- package/locales/en/overrides/.agents/roles/pr-verifier.md +44 -0
- package/locales/en/overrides/.agents/roles/ui-verifier.md +22 -0
- package/locales/en/overrides/.agents/roles/wiki-compiler.md +47 -0
- package/locales/en/overrides/.agents/skills/arena/SKILL.md +50 -0
- package/locales/en/overrides/.agents/skills/autonomous-run/SKILL.md +45 -0
- package/locales/en/overrides/.agents/skills/blast-radius/SKILL.md +53 -0
- package/locales/en/overrides/.agents/skills/csharp-quality-gates/SKILL.md +109 -0
- package/locales/en/overrides/.agents/skills/dev-tour/SKILL.md +98 -0
- package/locales/en/overrides/.agents/skills/dream/SKILL.md +63 -0
- package/locales/en/overrides/.agents/skills/eval/SKILL.md +42 -0
- package/locales/en/overrides/.agents/skills/interrogate/SKILL.md +49 -0
- package/locales/en/overrides/.agents/skills/number-vetting/SKILL.md +35 -0
- package/locales/en/overrides/.agents/skills/page-refactor/SKILL.md +192 -0
- package/locales/en/overrides/.agents/skills/papercut-fix/SKILL.md +92 -0
- package/locales/en/overrides/.agents/skills/pause-safely/SKILL.md +31 -0
- package/locales/en/overrides/.agents/skills/perf/SKILL.md +42 -0
- package/locales/en/overrides/.agents/skills/review-method/SKILL.md +92 -0
- package/locales/en/overrides/.agents/skills/session-resume/SKILL.md +69 -0
- package/locales/en/overrides/.agents/skills/shipyard-workflow-closeout/SKILL.md +109 -0
- package/locales/en/overrides/.agents/skills/show-me-your-work/SKILL.md +57 -0
- package/locales/en/overrides/.agents/skills/tfs-pr/SKILL.md +95 -0
- package/locales/en/overrides/.agents/skills/verify-ui/SKILL.md +116 -0
- package/locales/en/overrides/.agents/skills/visual-parity/SKILL.md +35 -0
- package/locales/en/overrides/.agents/skills/why/SKILL.md +56 -0
- package/locales/en/overrides/.agents/skills/wiki/SKILL.md +53 -0
- package/locales/en/overrides/.claude/agents/implementer.md +9 -0
- package/locales/en/overrides/.claude/agents/independent-reviewer.md +10 -0
- package/locales/en/overrides/.claude/agents/pr-adjudicator.md +10 -0
- package/locales/en/overrides/.claude/agents/pr-finder.md +10 -0
- package/locales/en/overrides/.claude/agents/pr-verifier.md +10 -0
- package/locales/en/overrides/.claude/agents/ui-verifier.md +10 -0
- package/locales/en/overrides/.claude/agents/wiki-compiler.md +10 -0
- package/locales/en/overrides/.claude/commands/wf-deploy.md +8 -0
- package/locales/en/overrides/.claude/commands/wf-maintain.md +8 -0
- package/locales/en/overrides/.claude/commands/wf-new-task.md +8 -0
- package/locales/en/overrides/.claude/commands/wf-pipeline-run.md +8 -0
- package/locales/en/overrides/.claude/commands/wf-pr-review.md +8 -0
- package/locales/en/overrides/.claude/commands/wf-review.md +8 -0
- package/locales/en/overrides/.claude/commands/wf-stages.md +8 -0
- package/locales/en/overrides/.cline/agents/implementer.yml +13 -0
- package/locales/en/overrides/.cline/agents/independent-reviewer.yml +11 -0
- package/locales/en/overrides/.cline/agents/pr-adjudicator.yml +11 -0
- package/locales/en/overrides/.cline/agents/pr-finder.yml +11 -0
- package/locales/en/overrides/.cline/agents/pr-verifier.yml +11 -0
- package/locales/en/overrides/.cline/agents/ui-verifier.yml +8 -0
- package/locales/en/overrides/.cline/agents/wiki-compiler.yml +14 -0
- package/locales/en/overrides/.cline/workflows/wf-deploy.md +8 -0
- package/locales/en/overrides/.cline/workflows/wf-maintain.md +8 -0
- package/locales/en/overrides/.cline/workflows/wf-new-task.md +8 -0
- package/locales/en/overrides/.cline/workflows/wf-pipeline-run.md +8 -0
- package/locales/en/overrides/.cline/workflows/wf-pr-review.md +8 -0
- package/locales/en/overrides/.cline/workflows/wf-review.md +8 -0
- package/locales/en/overrides/.cline/workflows/wf-stages.md +8 -0
- package/locales/en/overrides/.codebuddy/CODEBUDDY.md +7 -0
- package/locales/en/overrides/.codebuddy/agents/implementer.md +9 -0
- package/locales/en/overrides/.codebuddy/agents/independent-reviewer.md +10 -0
- package/locales/en/overrides/.codebuddy/agents/pr-adjudicator.md +10 -0
- package/locales/en/overrides/.codebuddy/agents/pr-finder.md +10 -0
- package/locales/en/overrides/.codebuddy/agents/pr-verifier.md +10 -0
- package/locales/en/overrides/.codebuddy/agents/ui-verifier.md +11 -0
- package/locales/en/overrides/.codebuddy/agents/wiki-compiler.md +10 -0
- package/locales/en/overrides/.codebuddy/commands/wf-deploy.md +8 -0
- package/locales/en/overrides/.codebuddy/commands/wf-maintain.md +8 -0
- package/locales/en/overrides/.codebuddy/commands/wf-new-task.md +8 -0
- package/locales/en/overrides/.codebuddy/commands/wf-pipeline-run.md +8 -0
- package/locales/en/overrides/.codebuddy/commands/wf-pr-review.md +8 -0
- package/locales/en/overrides/.codebuddy/commands/wf-review.md +8 -0
- package/locales/en/overrides/.codebuddy/commands/wf-stages.md +8 -0
- package/locales/en/overrides/.codebuddy/rules/backend-scope.md +5 -0
- package/locales/en/overrides/.codebuddy/rules/frontend-scope.md +5 -0
- package/locales/en/overrides/.codebuddy/skills/tfs-pr/SKILL.md +6 -0
- package/locales/en/overrides/.commandcode/agents/implementer.md +10 -0
- package/locales/en/overrides/.commandcode/agents/independent-reviewer.md +9 -0
- package/locales/en/overrides/.commandcode/agents/pr-adjudicator.md +9 -0
- package/locales/en/overrides/.commandcode/agents/pr-finder.md +9 -0
- package/locales/en/overrides/.commandcode/agents/pr-verifier.md +9 -0
- package/locales/en/overrides/.commandcode/agents/ui-verifier.md +10 -0
- package/locales/en/overrides/.commandcode/agents/wiki-compiler.md +10 -0
- package/locales/en/overrides/.commandcode/commands/wf-deploy.md +7 -0
- package/locales/en/overrides/.commandcode/commands/wf-maintain.md +7 -0
- package/locales/en/overrides/.commandcode/commands/wf-new-task.md +7 -0
- package/locales/en/overrides/.commandcode/commands/wf-pipeline-run.md +7 -0
- package/locales/en/overrides/.commandcode/commands/wf-pr-review.md +7 -0
- package/locales/en/overrides/.commandcode/commands/wf-review.md +7 -0
- package/locales/en/overrides/.commandcode/commands/wf-stages.md +7 -0
- package/locales/en/overrides/.opencode/agents/implementer.md +51 -0
- package/locales/en/overrides/.opencode/agents/independent-reviewer.md +18 -0
- package/locales/en/overrides/.opencode/agents/pr-adjudicator.md +18 -0
- package/locales/en/overrides/.opencode/agents/pr-finder.md +18 -0
- package/locales/en/overrides/.opencode/agents/pr-verifier.md +18 -0
- package/locales/en/overrides/.opencode/agents/ui-verifier.md +45 -0
- package/locales/en/overrides/.opencode/agents/wiki-compiler.md +51 -0
- package/locales/en/overrides/.opencode/commands/wf-deploy.md +7 -0
- package/locales/en/overrides/.opencode/commands/wf-maintain.md +7 -0
- package/locales/en/overrides/.opencode/commands/wf-new-task.md +7 -0
- package/locales/en/overrides/.opencode/commands/wf-pipeline-run.md +7 -0
- package/locales/en/overrides/.opencode/commands/wf-pr-review.md +7 -0
- package/locales/en/overrides/.opencode/commands/wf-review.md +7 -0
- package/locales/en/overrides/.opencode/commands/wf-stages.md +7 -0
- package/locales/en/overrides/.pi/agents/implementer.md +11 -0
- package/locales/en/overrides/.pi/agents/independent-reviewer.md +12 -0
- package/locales/en/overrides/.pi/agents/pr-adjudicator.md +12 -0
- package/locales/en/overrides/.pi/agents/pr-finder.md +11 -0
- package/locales/en/overrides/.pi/agents/pr-verifier.md +11 -0
- package/locales/en/overrides/.pi/agents/ui-verifier.md +11 -0
- package/locales/en/overrides/.pi/agents/wiki-compiler.md +11 -0
- package/locales/en/overrides/.qoder/agents/implementer.md +9 -0
- package/locales/en/overrides/.qoder/agents/independent-reviewer.md +10 -0
- package/locales/en/overrides/.qoder/agents/pr-adjudicator.md +10 -0
- package/locales/en/overrides/.qoder/agents/pr-finder.md +10 -0
- package/locales/en/overrides/.qoder/agents/pr-verifier.md +10 -0
- package/locales/en/overrides/.qoder/agents/ui-verifier.md +10 -0
- package/locales/en/overrides/.qoder/agents/wiki-compiler.md +10 -0
- package/locales/en/overrides/.qoder/commands/wf-deploy.md +8 -0
- package/locales/en/overrides/.qoder/commands/wf-maintain.md +8 -0
- package/locales/en/overrides/.qoder/commands/wf-new-task.md +8 -0
- package/locales/en/overrides/.qoder/commands/wf-pipeline-run.md +8 -0
- package/locales/en/overrides/.qoder/commands/wf-pr-review.md +8 -0
- package/locales/en/overrides/.qoder/commands/wf-review.md +8 -0
- package/locales/en/overrides/.qoder/commands/wf-stages.md +8 -0
- package/locales/en/overrides/.qwen/agents/implementer.md +10 -0
- package/locales/en/overrides/.qwen/agents/independent-reviewer.md +10 -0
- package/locales/en/overrides/.qwen/agents/pr-adjudicator.md +10 -0
- package/locales/en/overrides/.qwen/agents/pr-finder.md +10 -0
- package/locales/en/overrides/.qwen/agents/pr-verifier.md +10 -0
- package/locales/en/overrides/.qwen/agents/ui-verifier.md +10 -0
- package/locales/en/overrides/.qwen/agents/wiki-compiler.md +13 -0
- package/locales/en/overrides/.qwen/commands/wf-deploy.md +7 -0
- package/locales/en/overrides/.qwen/commands/wf-maintain.md +7 -0
- package/locales/en/overrides/.qwen/commands/wf-new-task.md +7 -0
- package/locales/en/overrides/.qwen/commands/wf-pipeline-run.md +7 -0
- package/locales/en/overrides/.qwen/commands/wf-pr-review.md +7 -0
- package/locales/en/overrides/.qwen/commands/wf-review.md +7 -0
- package/locales/en/overrides/.qwen/commands/wf-stages.md +7 -0
- package/locales/en/overrides/.trae/agents/implementer.md +9 -0
- package/locales/en/overrides/.trae/agents/independent-reviewer.md +10 -0
- package/locales/en/overrides/.trae/agents/pr-adjudicator.md +10 -0
- package/locales/en/overrides/.trae/agents/pr-finder.md +10 -0
- package/locales/en/overrides/.trae/agents/pr-verifier.md +10 -0
- package/locales/en/overrides/.trae/agents/ui-verifier.md +10 -0
- package/locales/en/overrides/.trae/agents/wiki-compiler.md +10 -0
- package/locales/en/overrides/.trae/commands/wf-deploy.md +8 -0
- package/locales/en/overrides/.trae/commands/wf-maintain.md +8 -0
- package/locales/en/overrides/.trae/commands/wf-new-task.md +8 -0
- package/locales/en/overrides/.trae/commands/wf-pipeline-run.md +8 -0
- package/locales/en/overrides/.trae/commands/wf-pr-review.md +8 -0
- package/locales/en/overrides/.trae/commands/wf-review.md +8 -0
- package/locales/en/overrides/.trae/commands/wf-stages.md +8 -0
- package/locales/en/overrides/.trae/rules/backend-scope.md +5 -0
- package/locales/en/overrides/.trae/rules/frontend-scope.md +5 -0
- package/locales/en/overrides/.zcode/agents/implementer.md +8 -0
- package/locales/en/overrides/.zcode/agents/independent-reviewer.md +8 -0
- package/locales/en/overrides/.zcode/agents/ui-verifier.md +8 -0
- package/locales/en/overrides/.zcode/agents/wiki-compiler.md +8 -0
- package/locales/en/overrides/AGENTS.md +52 -0
- package/locales/en/overrides/CLAUDE.md +20 -0
- package/locales/en/overrides/wiki/INDEX.md +33 -0
- package/locales/en/overrides/wiki/drafts-archive/README.md +6 -0
- package/locales/en/overrides/wiki//346/225/260/346/215/256/347/273/264/346/212/244/README.md +22 -0
- package/locales/en/overrides/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +230 -0
- package/locales/en/overrides/workflow/INDEX.md +13 -0
- package/locales/en/overrides/workflow/README.md +67 -0
- package/locales/en/overrides/workflow/archive/README.md +3 -0
- package/locales/en/overrides/workflow/delegations.md +4 -0
- package/locales/en/overrides/workflow/effective-policy.md +4 -0
- package/locales/en/overrides/workflow/gate-cost.md +4 -0
- package/locales/en/overrides/workflow/incidents/_TEMPLATE.md +49 -0
- package/locales/en/overrides/workflow/intents/_TEMPLATE.md +43 -0
- package/locales/en/overrides/workflow/metrics.md +4 -0
- package/locales/en/overrides/workflow/papercuts.md +3 -0
- package/locales/en/overrides/workflow/plans/_TEMPLATE.md +47 -0
- package/locales/en/overrides/workflow/regression-checklist.md +3 -0
- package/locales/en/overrides/workflow/specs/_TEMPLATE.md +86 -0
- package/locales/en/overrides/workflow/verify-evidence.md +4 -0
- package/locales/zh-CN/installer.json +25 -0
- package/package.json +23 -36
- package/schemas/install-manifest.schema.json +24 -0
- package/schemas/project-profile.schema.json +28 -0
- package/src/cli/args.mjs +41 -0
- package/src/cli/main.mjs +72 -0
- package/src/commands/add-host.mjs +29 -0
- package/src/commands/doctor.mjs +80 -0
- package/src/commands/install.mjs +149 -0
- package/src/commands/upgrade.mjs +30 -0
- package/src/git/config.mjs +61 -0
- package/src/hosts/registry.mjs +26 -0
- package/src/i18n.mjs +32 -0
- package/src/install/apply.mjs +124 -0
- package/src/install/manifest.mjs +40 -0
- package/src/install/plan.mjs +83 -0
- package/src/render/tokens.mjs +58 -0
- package/templates/engine/.agents/board/dev-tour.html +1940 -0
- package/templates/engine/.agents/board/index.html +835 -0
- package/templates/engine/.agents/commands/deploy.md +52 -0
- package/templates/engine/.agents/commands/maintain.md +76 -0
- package/templates/engine/.agents/commands/new-task.md +89 -0
- package/templates/engine/.agents/commands/pipeline-run.md +56 -0
- package/templates/engine/.agents/commands/pr-review.md +30 -0
- package/templates/engine/.agents/commands/review.md +93 -0
- package/templates/engine/.agents/commands/stages.md +256 -0
- package/templates/engine/.agents/config/commit-msg-rules.json +7 -0
- package/templates/engine/.agents/config/engine.json +11 -0
- package/templates/engine/.agents/config/gate-budgets.json +5 -0
- package/templates/engine/.agents/config/mechanism-registry.md +62 -0
- package/templates/engine/.agents/config/model-roles.json +26 -0
- package/templates/engine/.agents/config/model-roles.local.example.json +7 -0
- package/templates/engine/.agents/config/pr-review.json +67 -0
- package/templates/engine/.agents/config/principles.md +32 -0
- package/templates/engine/.agents/config/workflow-enums.txt +25 -0
- package/templates/engine/.agents/hooks/check-architecture.mjs +168 -0
- package/templates/engine/.agents/hooks/check-audit-fields.mjs +56 -0
- package/templates/engine/.agents/hooks/check-commit-plan-ref.mjs +84 -0
- package/templates/engine/.agents/hooks/check-confirm-order.mjs +281 -0
- package/templates/engine/.agents/hooks/check-delete-guard.mjs +63 -0
- package/templates/engine/.agents/hooks/check-notify-guard.mjs +55 -0
- package/templates/engine/.agents/hooks/commit-check.cjs +208 -0
- package/templates/engine/.agents/hooks/gates.json +33 -0
- package/templates/engine/.agents/hooks/local-pre-commit.mjs +27 -0
- package/templates/engine/.agents/hooks/pre-shell-check.cjs +361 -0
- package/templates/engine/.agents/hooks/pre-write-check.cjs +220 -0
- package/templates/engine/.agents/notes/gate-diet.md +75 -0
- package/templates/engine/.agents/notes/pr-review-cost-discipline.md +48 -0
- package/templates/engine/.agents/notes/process-order-rule.md +21 -0
- package/templates/engine/.agents/notes/runtime-env.local.example.md +21 -0
- package/templates/engine/.agents/pipelines/_TEMPLATE.md +75 -0
- package/templates/engine/.agents/pipelines/pipeline-closing.md +28 -0
- package/templates/engine/.agents/pipelines//347/244/272/344/276/213-/345/271/266/350/241/214/345/256/236/347/216/260/350/257/204/345/256/241.md +16 -0
- package/templates/engine/.agents/roles/implementer.md +28 -0
- package/templates/engine/.agents/roles/independent-reviewer.md +39 -0
- package/templates/engine/.agents/roles/pr-adjudicator.md +27 -0
- package/templates/engine/.agents/roles/pr-finder.md +51 -0
- package/templates/engine/.agents/roles/pr-verifier.md +44 -0
- package/templates/engine/.agents/roles/ui-verifier.md +22 -0
- package/templates/engine/.agents/roles/wiki-compiler.md +47 -0
- package/templates/engine/.agents/scripts/agg-delegations.mjs +439 -0
- package/templates/engine/.agents/scripts/agg-judgments.mjs +301 -0
- package/templates/engine/.agents/scripts/agg-kb-usage.mjs +196 -0
- package/templates/engine/.agents/scripts/append-compensation.mjs +109 -0
- package/templates/engine/.agents/scripts/archive-sweep.mjs +229 -0
- package/templates/engine/.agents/scripts/auto-tour.mjs +77 -0
- package/templates/engine/.agents/scripts/board-port.mjs +17 -0
- package/templates/engine/.agents/scripts/check-doc-body-drift.mjs +102 -0
- package/templates/engine/.agents/scripts/check-engine-artifacts.mjs +54 -0
- package/templates/engine/.agents/scripts/check-hook-wiring.mjs +75 -0
- package/templates/engine/.agents/scripts/check-lane-surface.mjs +393 -0
- package/templates/engine/.agents/scripts/check-ledger-doc-presence.mjs +71 -0
- package/templates/engine/.agents/scripts/check-ledger-invariant.mjs +873 -0
- package/templates/engine/.agents/scripts/check-local-gate-config.mjs +95 -0
- package/templates/engine/.agents/scripts/check-local-gates.mjs +248 -0
- package/templates/engine/.agents/scripts/check-loop-kit.mjs +550 -0
- package/templates/engine/.agents/scripts/check-loop-rev-semantics.mjs +59 -0
- package/templates/engine/.agents/scripts/check-loop-sections/s01-pairing.mjs +116 -0
- package/templates/engine/.agents/scripts/check-loop-sections/s03x-regr-evidence.mjs +38 -0
- package/templates/engine/.agents/scripts/check-loop-sections/s08-acceptance.mjs +130 -0
- package/templates/engine/.agents/scripts/check-loop-sections/s15-fingerprint.mjs +152 -0
- package/templates/engine/.agents/scripts/check-loop-sections/s23-convergence-audit.mjs +182 -0
- package/templates/engine/.agents/scripts/check-loop-sections/s29-regr-enforcer.mjs +69 -0
- package/templates/engine/.agents/scripts/check-loop.mjs +131 -0
- package/templates/engine/.agents/scripts/check-regression-consistency.mjs +252 -0
- package/templates/engine/.agents/scripts/check-schema-residue.mjs +144 -0
- package/templates/engine/.agents/scripts/check-scope-range.mjs +156 -0
- package/templates/engine/.agents/scripts/check-scope.mjs +121 -0
- package/templates/engine/.agents/scripts/check-scripts-syntax.mjs +138 -0
- package/templates/engine/.agents/scripts/check-secrets-lite.mjs +239 -0
- package/templates/engine/.agents/scripts/check-secrets.mjs +104 -0
- package/templates/engine/.agents/scripts/check-test-impact-map.mjs +107 -0
- package/templates/engine/.agents/scripts/ci-assert-frontend-deps.mjs +42 -0
- package/templates/engine/.agents/scripts/ci-commit-hygiene.mjs +86 -0
- package/templates/engine/.agents/scripts/ci-install-gitleaks.mjs +75 -0
- package/templates/engine/.agents/scripts/ci-push-before.mjs +49 -0
- package/templates/engine/.agents/scripts/closeout.mjs +127 -0
- package/templates/engine/.agents/scripts/compact-ledgers.mjs +70 -0
- package/templates/engine/.agents/scripts/confirm-doc.mjs +737 -0
- package/templates/engine/.agents/scripts/delegation-presence.mjs +38 -0
- package/templates/engine/.agents/scripts/dev-tour-model.mjs +1398 -0
- package/templates/engine/.agents/scripts/dream-run.mjs +283 -0
- package/templates/engine/.agents/scripts/ensure-board.mjs +187 -0
- package/templates/engine/.agents/scripts/failure-attribution.mjs +11 -0
- package/templates/engine/.agents/scripts/fill-intent.mjs +60 -0
- package/templates/engine/.agents/scripts/fill-plan.mjs +105 -0
- package/templates/engine/.agents/scripts/fill-spec.mjs +72 -0
- package/templates/engine/.agents/scripts/gen-dev-tour.mjs +191 -0
- package/templates/engine/.agents/scripts/gen-effective-policy.mjs +113 -0
- package/templates/engine/.agents/scripts/gen-engine-map.mjs +292 -0
- package/templates/engine/.agents/scripts/gen-narrative-claims.mjs +275 -0
- package/templates/engine/.agents/scripts/gen-verify-evidence.mjs +115 -0
- package/templates/engine/.agents/scripts/gen-wiki-board.mjs +221 -0
- package/templates/engine/.agents/scripts/gen-workflow-index.mjs +201 -0
- package/templates/engine/.agents/scripts/gen-workflow-metrics.mjs +692 -0
- package/templates/engine/.agents/scripts/git-merge-jsonl.mjs +62 -0
- package/templates/engine/.agents/scripts/judgment-ledger.mjs +68 -0
- package/templates/engine/.agents/scripts/kb-cache-evict.mjs +50 -0
- package/templates/engine/.agents/scripts/kb-search.mjs +423 -0
- package/templates/engine/.agents/scripts/lane-surface-audit.mjs +145 -0
- package/templates/engine/.agents/scripts/ledger-loader.mjs +77 -0
- package/templates/engine/.agents/scripts/ledger-shards.mjs +346 -0
- package/templates/engine/.agents/scripts/metrics-core.mjs +131 -0
- package/templates/engine/.agents/scripts/pipeline-run.mjs +964 -0
- package/templates/engine/.agents/scripts/policy.mjs +250 -0
- package/templates/engine/.agents/scripts/pr-create.mjs +54 -0
- package/templates/engine/.agents/scripts/pr-review.mjs +1551 -0
- package/templates/engine/.agents/scripts/quote-grounding.mjs +233 -0
- package/templates/engine/.agents/scripts/regen-ledgers.mjs +59 -0
- package/templates/engine/.agents/scripts/rewrite-file.mjs +135 -0
- package/templates/engine/.agents/scripts/rule-budget.mjs +246 -0
- package/templates/engine/.agents/scripts/stage-gates.mjs +812 -0
- package/templates/engine/.agents/scripts/test-impact-lib.mjs +451 -0
- package/templates/engine/.agents/scripts/test-impact.mjs +105 -0
- package/templates/engine/.agents/scripts/trust-mode.mjs +130 -0
- package/templates/engine/.agents/scripts/verify-scope.mjs +163 -0
- package/templates/engine/.agents/scripts/verify-wiki-consistency.mjs +264 -0
- package/templates/engine/.agents/scripts/verify.mjs +847 -0
- package/templates/engine/.agents/scripts/warn-format.mjs +57 -0
- package/templates/engine/.agents/scripts/wf-journal.mjs +230 -0
- package/templates/engine/.agents/scripts/workflow-board-server.mjs +696 -0
- package/templates/engine/.agents/scripts/workflow-enums.mjs +70 -0
- package/templates/engine/.agents/scripts/workflows-check.mjs +260 -0
- package/templates/engine/.agents/settings.json +33 -0
- package/templates/engine/.agents/skills/arena/SKILL.md +50 -0
- package/templates/engine/.agents/skills/autonomous-run/SKILL.md +45 -0
- package/templates/engine/.agents/skills/blast-radius/SKILL.md +53 -0
- package/templates/engine/.agents/skills/csharp-quality-gates/SKILL.md +109 -0
- package/templates/engine/.agents/skills/csharp-quality-gates/scripts/quality-gates.mjs +430 -0
- package/templates/engine/.agents/skills/dev-tour/SKILL.md +98 -0
- package/templates/engine/.agents/skills/dream/SKILL.md +63 -0
- package/templates/engine/.agents/skills/eval/SKILL.md +42 -0
- package/templates/engine/.agents/skills/eval/scripts/eval-score.mjs +59 -0
- package/templates/engine/.agents/skills/interrogate/SKILL.md +49 -0
- package/templates/engine/.agents/skills/interrogate/scripts/interrogate-plan.mjs +72 -0
- package/templates/engine/.agents/skills/number-vetting/SKILL.md +35 -0
- package/templates/engine/.agents/skills/page-refactor/SKILL.md +192 -0
- package/templates/engine/.agents/skills/page-refactor/references/templates.md +989 -0
- package/templates/engine/.agents/skills/papercut-fix/SKILL.md +92 -0
- package/templates/engine/.agents/skills/papercut-fix/references/response-template.md +81 -0
- package/templates/engine/.agents/skills/pause-safely/SKILL.md +31 -0
- package/templates/engine/.agents/skills/perf/SKILL.md +42 -0
- package/templates/engine/.agents/skills/review-method/SKILL.md +92 -0
- package/templates/engine/.agents/skills/review-method/golden-set.md +61 -0
- package/templates/engine/.agents/skills/session-resume/SKILL.md +69 -0
- package/templates/engine/.agents/skills/session-resume/scripts/worktree-audit.mjs +110 -0
- package/templates/engine/.agents/skills/shipyard-workflow-closeout/SKILL.md +109 -0
- package/templates/engine/.agents/skills/show-me-your-work/SKILL.md +57 -0
- package/templates/engine/.agents/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
- package/templates/engine/.agents/skills/show-me-your-work/scripts/decision-log.mjs +24 -0
- package/templates/engine/.agents/skills/tfs-pr/SKILL.md +95 -0
- package/templates/engine/.agents/skills/tfs-pr/scripts/tfs-pr.mjs +947 -0
- package/templates/engine/.agents/skills/verify-ui/SKILL.md +116 -0
- package/templates/engine/.agents/skills/verify-ui/features/.gitkeep +0 -0
- package/templates/engine/.agents/skills/verify-ui/scripts/gen-verify-feature.mjs +114 -0
- package/templates/engine/.agents/skills/visual-parity/SKILL.md +35 -0
- package/templates/engine/.agents/skills/why/SKILL.md +56 -0
- package/templates/engine/.agents/skills/why/scripts/why-evidence.mjs +74 -0
- package/templates/engine/.agents/skills/wiki/SKILL.md +53 -0
- package/templates/engine/.agents/tests/auto-tour.test.mjs +82 -0
- package/templates/engine/.agents/tests/dev-tour-model.test.mjs +452 -0
- package/templates/engine/.agents/tests/fill-plan-lite.test.mjs +46 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-bare-mid-path.md +14 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-bare-mid-path.model.json +179 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-fake-path-guard.md +21 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-fake-path-guard.model.json +194 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-h3-batches.md +20 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-h3-batches.model.json +207 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-issues.md +24 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-issues.model.json +225 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-l1-single-file.md +15 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-l1-single-file.model.json +160 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layer-word-unique.md +29 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layer-word-unique.model.json +266 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layers-fallback.md +17 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layers-fallback.model.json +207 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-numbered-steps.md +39 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-numbered-steps.model.json +359 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-path-guess.md +16 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-path-guess.model.json +131 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-table-rows.md +29 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-table-rows.model.json +190 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-task-checkboxes.md +39 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-task-checkboxes.model.json +362 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-union-label.md +24 -0
- package/templates/engine/.agents/tests/fixtures/dev-tour/plan-union-label.model.json +224 -0
- package/templates/engine/.agents/tests/tools/gen-synthetic-plan.mjs +108 -0
- package/templates/git/.gitattributes +22 -0
- package/templates/git/.githooks/commit-msg +72 -0
- package/templates/git/.githooks/post-commit +62 -0
- package/templates/git/.githooks/pre-commit +197 -0
- package/templates/git/.githooks/pre-merge-commit +28 -0
- package/templates/git/.githooks/pre-push +201 -0
- package/templates/git/.gitignore.engine +11 -0
- package/templates/hosts/claude/CLAUDE.md +55 -0
- package/templates/hosts/claude/agents/implementer.md +7 -0
- package/templates/hosts/claude/agents/independent-reviewer.md +8 -0
- package/templates/hosts/claude/agents/pr-adjudicator.md +8 -0
- package/templates/hosts/claude/agents/pr-finder.md +8 -0
- package/templates/hosts/claude/agents/pr-verifier.md +8 -0
- package/templates/hosts/claude/agents/ui-verifier.md +8 -0
- package/templates/hosts/claude/agents/wiki-compiler.md +8 -0
- package/templates/hosts/claude/commands/wf-deploy.md +10 -0
- package/templates/hosts/claude/commands/wf-maintain.md +10 -0
- package/templates/hosts/claude/commands/wf-new-task.md +10 -0
- package/templates/hosts/claude/commands/wf-pipeline-run.md +10 -0
- package/templates/hosts/claude/commands/wf-pr-review.md +10 -0
- package/templates/hosts/claude/commands/wf-review.md +10 -0
- package/templates/hosts/claude/commands/wf-stages.md +10 -0
- package/templates/hosts/claude/settings.json +57 -0
- package/templates/hosts/cline/agents/implementer.yml +15 -0
- package/templates/hosts/cline/agents/independent-reviewer.yml +20 -0
- package/templates/hosts/cline/agents/pr-adjudicator.yml +18 -0
- package/templates/hosts/cline/agents/pr-finder.yml +19 -0
- package/templates/hosts/cline/agents/pr-verifier.yml +18 -0
- package/templates/hosts/cline/agents/ui-verifier.yml +17 -0
- package/templates/hosts/cline/agents/wiki-compiler.yml +20 -0
- package/templates/hosts/cline/hooks/PreToolUse.cjs +260 -0
- package/templates/hosts/cline/workflows/wf-deploy.md +8 -0
- package/templates/hosts/cline/workflows/wf-maintain.md +8 -0
- package/templates/hosts/cline/workflows/wf-new-task.md +8 -0
- package/templates/hosts/cline/workflows/wf-pipeline-run.md +8 -0
- package/templates/hosts/cline/workflows/wf-pr-review.md +8 -0
- package/templates/hosts/cline/workflows/wf-review.md +8 -0
- package/templates/hosts/cline/workflows/wf-stages.md +8 -0
- package/templates/hosts/codebuddy/CODEBUDDY.md +7 -0
- package/templates/hosts/codebuddy/agents/implementer.md +9 -0
- package/templates/hosts/codebuddy/agents/independent-reviewer.md +10 -0
- package/templates/hosts/codebuddy/agents/pr-adjudicator.md +10 -0
- package/templates/hosts/codebuddy/agents/pr-finder.md +10 -0
- package/templates/hosts/codebuddy/agents/pr-verifier.md +10 -0
- package/templates/hosts/codebuddy/agents/ui-verifier.md +11 -0
- package/templates/hosts/codebuddy/agents/wiki-compiler.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-deploy.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-maintain.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-new-task.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-pipeline-run.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-pr-review.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-review.md +10 -0
- package/templates/hosts/codebuddy/commands/wf-stages.md +10 -0
- package/templates/hosts/codebuddy/rules/backend-scope.md +5 -0
- package/templates/hosts/codebuddy/rules/frontend-scope.md +5 -0
- package/templates/hosts/codebuddy/settings.json +31 -0
- package/templates/hosts/codebuddy/skills/tfs-pr/SKILL.md +6 -0
- package/templates/hosts/commandcode/agents/implementer.md +10 -0
- package/templates/hosts/commandcode/agents/independent-reviewer.md +9 -0
- package/templates/hosts/commandcode/agents/pr-adjudicator.md +9 -0
- package/templates/hosts/commandcode/agents/pr-finder.md +9 -0
- package/templates/hosts/commandcode/agents/pr-verifier.md +9 -0
- package/templates/hosts/commandcode/agents/ui-verifier.md +11 -0
- package/templates/hosts/commandcode/agents/wiki-compiler.md +10 -0
- package/templates/hosts/commandcode/commands/wf-deploy.md +9 -0
- package/templates/hosts/commandcode/commands/wf-maintain.md +9 -0
- package/templates/hosts/commandcode/commands/wf-new-task.md +9 -0
- package/templates/hosts/commandcode/commands/wf-pipeline-run.md +9 -0
- package/templates/hosts/commandcode/commands/wf-pr-review.md +9 -0
- package/templates/hosts/commandcode/commands/wf-review.md +9 -0
- package/templates/hosts/commandcode/commands/wf-stages.md +9 -0
- package/templates/hosts/commandcode/settings.json +59 -0
- package/templates/hosts/opencode/agents/implementer.md +51 -0
- package/templates/hosts/opencode/agents/independent-reviewer.md +18 -0
- package/templates/hosts/opencode/agents/pr-adjudicator.md +18 -0
- package/templates/hosts/opencode/agents/pr-finder.md +18 -0
- package/templates/hosts/opencode/agents/pr-verifier.md +18 -0
- package/templates/hosts/opencode/agents/ui-verifier.md +45 -0
- package/templates/hosts/opencode/agents/wiki-compiler.md +51 -0
- package/templates/hosts/opencode/commands/wf-deploy.md +7 -0
- package/templates/hosts/opencode/commands/wf-maintain.md +7 -0
- package/templates/hosts/opencode/commands/wf-new-task.md +7 -0
- package/templates/hosts/opencode/commands/wf-pipeline-run.md +7 -0
- package/templates/hosts/opencode/commands/wf-pr-review.md +7 -0
- package/templates/hosts/opencode/commands/wf-review.md +7 -0
- package/templates/hosts/opencode/commands/wf-stages.md +7 -0
- package/templates/hosts/pi/agents/implementer.md +10 -0
- package/templates/hosts/pi/agents/independent-reviewer.md +11 -0
- package/templates/hosts/pi/agents/pr-adjudicator.md +11 -0
- package/templates/hosts/pi/agents/pr-finder.md +10 -0
- package/templates/hosts/pi/agents/pr-verifier.md +10 -0
- package/templates/hosts/pi/agents/ui-verifier.md +10 -0
- package/templates/hosts/pi/agents/wiki-compiler.md +10 -0
- package/templates/hosts/pi/extensions/lib/gates-core.cjs +196 -0
- package/templates/hosts/pi/extensions/shipyard-gates.ts +95 -0
- package/templates/hosts/pi/settings.json +5 -0
- package/templates/hosts/qoder/agents/implementer.md +9 -0
- package/templates/hosts/qoder/agents/independent-reviewer.md +10 -0
- package/templates/hosts/qoder/agents/pr-adjudicator.md +10 -0
- package/templates/hosts/qoder/agents/pr-finder.md +10 -0
- package/templates/hosts/qoder/agents/pr-verifier.md +10 -0
- package/templates/hosts/qoder/agents/ui-verifier.md +10 -0
- package/templates/hosts/qoder/agents/wiki-compiler.md +10 -0
- package/templates/hosts/qoder/commands/wf-deploy.md +8 -0
- package/templates/hosts/qoder/commands/wf-maintain.md +8 -0
- package/templates/hosts/qoder/commands/wf-new-task.md +8 -0
- package/templates/hosts/qoder/commands/wf-pipeline-run.md +8 -0
- package/templates/hosts/qoder/commands/wf-pr-review.md +8 -0
- package/templates/hosts/qoder/commands/wf-review.md +8 -0
- package/templates/hosts/qoder/commands/wf-stages.md +8 -0
- package/templates/hosts/qoder/settings.json +63 -0
- package/templates/hosts/qwen/agents/implementer.md +8 -0
- package/templates/hosts/qwen/agents/independent-reviewer.md +8 -0
- package/templates/hosts/qwen/agents/pr-adjudicator.md +8 -0
- package/templates/hosts/qwen/agents/pr-finder.md +8 -0
- package/templates/hosts/qwen/agents/pr-verifier.md +8 -0
- package/templates/hosts/qwen/agents/ui-verifier.md +8 -0
- package/templates/hosts/qwen/agents/wiki-compiler.md +11 -0
- package/templates/hosts/qwen/commands/wf-deploy.md +9 -0
- package/templates/hosts/qwen/commands/wf-maintain.md +9 -0
- package/templates/hosts/qwen/commands/wf-new-task.md +9 -0
- package/templates/hosts/qwen/commands/wf-pipeline-run.md +9 -0
- package/templates/hosts/qwen/commands/wf-pr-review.md +9 -0
- package/templates/hosts/qwen/commands/wf-review.md +9 -0
- package/templates/hosts/qwen/commands/wf-stages.md +9 -0
- package/templates/hosts/qwen/settings.json +59 -0
- package/templates/hosts/trae/agents/implementer.md +9 -0
- package/templates/hosts/trae/agents/independent-reviewer.md +10 -0
- package/templates/hosts/trae/agents/pr-adjudicator.md +10 -0
- package/templates/hosts/trae/agents/pr-finder.md +10 -0
- package/templates/hosts/trae/agents/pr-verifier.md +10 -0
- package/templates/hosts/trae/agents/ui-verifier.md +10 -0
- package/templates/hosts/trae/agents/wiki-compiler.md +10 -0
- package/templates/hosts/trae/commands/wf-deploy.md +8 -0
- package/templates/hosts/trae/commands/wf-maintain.md +8 -0
- package/templates/hosts/trae/commands/wf-new-task.md +8 -0
- package/templates/hosts/trae/commands/wf-pipeline-run.md +8 -0
- package/templates/hosts/trae/commands/wf-pr-review.md +8 -0
- package/templates/hosts/trae/commands/wf-review.md +8 -0
- package/templates/hosts/trae/commands/wf-stages.md +8 -0
- package/templates/hosts/trae/hooks.json +17 -0
- package/templates/hosts/trae/rules/backend-scope.md +5 -0
- package/templates/hosts/trae/rules/frontend-scope.md +5 -0
- package/templates/hosts/zcode/agents/implementer.md +8 -0
- package/templates/hosts/zcode/agents/independent-reviewer.md +8 -0
- package/templates/hosts/zcode/agents/ui-verifier.md +8 -0
- package/templates/hosts/zcode/agents/wiki-compiler.md +8 -0
- package/templates/hosts/zcode/config.json +31 -0
- package/templates/hosts/zcode/workflows/code-review.dwf.ts +428 -0
- package/templates/hosts/zcode/workflows/fullchain-flow-test.dwf.ts +898 -0
- package/templates/profiles/dotnet/.agents/config/lane-surfaces.txt +33 -0
- package/templates/profiles/dotnet/.agents/config/rule-budgets.txt +84 -0
- package/templates/profiles/dotnet/.agents/config/test-impact-map.json +590 -0
- package/templates/profiles/dotnet/.agents/config/workflow-modules.txt +18 -0
- package/templates/profiles/dotnet/.agents/hooks/commit-check.config.json +18 -0
- package/templates/profiles/dotnet/.agents/hooks/delete-guard-allowlist.txt +28 -0
- package/templates/profiles/dotnet/.agents/notes/runtime-env.md +178 -0
- package/templates/profiles/dotnet/AGENTS.md +52 -0
- package/templates/profiles/empty/.agents/config/lane-surfaces.txt +12 -0
- package/templates/profiles/empty/.agents/config/rule-budgets.txt +3 -0
- package/templates/profiles/empty/.agents/config/test-impact-map.json +8 -0
- package/templates/profiles/empty/.agents/config/workflow-modules.txt +2 -0
- package/templates/profiles/empty/.agents/hooks/commit-check.config.json +5 -0
- package/templates/profiles/empty/.agents/hooks/delete-guard-allowlist.txt +2 -0
- package/templates/profiles/empty/.agents/notes/runtime-env.md +44 -0
- package/templates/profiles/empty/AGENTS.md +52 -0
- package/templates/profiles/node/.agents/config/lane-surfaces.txt +12 -0
- package/templates/profiles/node/.agents/config/rule-budgets.txt +3 -0
- package/templates/profiles/node/.agents/config/test-impact-map.json +8 -0
- package/templates/profiles/node/.agents/config/workflow-modules.txt +2 -0
- package/templates/profiles/node/.agents/hooks/commit-check.config.json +5 -0
- package/templates/profiles/node/.agents/hooks/delete-guard-allowlist.txt +2 -0
- package/templates/profiles/node/.agents/notes/runtime-env.md +44 -0
- package/templates/profiles/node/AGENTS.md +52 -0
- package/templates/scaffold/wiki/INDEX.md +33 -0
- package/templates/scaffold/wiki/drafts-archive/README.md +7 -0
- package/templates/scaffold/wiki//344/272/247/345/223/201/351/234/200/346/261/202/.gitkeep +0 -0
- package/templates/scaffold/wiki//345/274/200/345/217/221/346/226/271/346/241/210/.gitkeep +0 -0
- package/templates/scaffold/wiki//346/225/260/346/215/256/347/273/264/346/212/244/README.md +22 -0
- package/templates/scaffold/wiki//346/265/213/350/257/225/346/212/245/345/221/212/.gitkeep +0 -0
- package/templates/scaffold/wiki//347/224/250/346/210/267/345/212/237/350/203/275/.gitkeep +0 -0
- package/templates/scaffold/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +230 -0
- package/templates/scaffold/wiki//351/241/271/347/233/256/350/247/204/350/214/203/.gitkeep +0 -0
- package/templates/scaffold/workflow/INDEX.md +13 -0
- package/templates/scaffold/workflow/README.md +67 -0
- package/templates/scaffold/workflow/archive/README.md +3 -0
- package/templates/scaffold/workflow/delegations.d/.gitkeep +0 -0
- package/templates/scaffold/workflow/delegations.md +4 -0
- package/templates/scaffold/workflow/effective-policy.md +4 -0
- package/templates/scaffold/workflow/evidence/.gitkeep +0 -0
- package/templates/scaffold/workflow/gate-cost.md +4 -0
- package/templates/scaffold/workflow/gate-metrics.d/.gitkeep +0 -0
- package/templates/scaffold/workflow/incidents/_TEMPLATE.md +49 -0
- package/templates/scaffold/workflow/intents/_TEMPLATE.md +43 -0
- package/templates/scaffold/workflow/metrics.md +4 -0
- package/templates/scaffold/workflow/papercuts.d/.gitkeep +0 -0
- package/templates/scaffold/workflow/papercuts.md +3 -0
- package/templates/scaffold/workflow/plans/_TEMPLATE.md +47 -0
- package/templates/scaffold/workflow/regression-checklist.d/.gitkeep +0 -0
- package/templates/scaffold/workflow/regression-checklist.md +3 -0
- package/templates/scaffold/workflow/research/.gitkeep +0 -0
- package/templates/scaffold/workflow/reviews.d/.gitkeep +0 -0
- package/templates/scaffold/workflow/specs/_TEMPLATE.md +87 -0
- package/templates/scaffold/workflow/verify-evidence/.gitkeep +0 -0
- package/templates/scaffold/workflow/verify-evidence.md +4 -0
- package/LICENSE +0 -21
- package/modules/gates/README.md +0 -25
- package/modules/gates/dotnet-ca/README.md +0 -24
- package/modules/gates/dotnet-ca/check-architecture.sh +0 -150
- package/modules/gates/dotnet-ca/local-pre-commit.line +0 -1
- package/modules/gates/generated-readonly/README.md +0 -13
- package/modules/gates/generated-readonly/check-generated.mjs +0 -61
- package/modules/gates/generated-readonly/local-pre-commit.line +0 -1
- package/modules/gates/node-layer/README.md +0 -9
- package/modules/gates/node-layer/check-node-layer.mjs +0 -29
- package/modules/gates/node-layer/local-pre-commit.line +0 -1
- package/modules/gates/py-import/README.md +0 -9
- package/modules/gates/py-import/check-py-import.mjs +0 -30
- package/modules/gates/py-import/local-pre-commit.line +0 -1
- package/modules/gates/test-protect/README.md +0 -23
- package/modules/gates/test-protect/check-test-protect.mjs +0 -64
- package/modules/gates/test-protect/local-pre-commit.line +0 -1
- package/modules/hosts/README.md +0 -57
- package/modules/hosts/claude/agents/implementer.md +0 -51
- package/modules/hosts/claude/agents/independent-reviewer.md +0 -49
- package/modules/hosts/claude/agents/ui-verifier.md +0 -48
- package/modules/hosts/claude/commands/wf-build.md +0 -69
- package/modules/hosts/claude/commands/wf-deploy.md +0 -58
- package/modules/hosts/claude/commands/wf-design.md +0 -49
- package/modules/hosts/claude/commands/wf-gate-checklist.md +0 -77
- package/modules/hosts/claude/commands/wf-maintain.md +0 -76
- package/modules/hosts/claude/commands/wf-new-task.md +0 -102
- package/modules/hosts/claude/commands/wf-pipeline-run.md +0 -35
- package/modules/hosts/claude/commands/wf-plan.md +0 -40
- package/modules/hosts/claude/commands/wf-review.md +0 -49
- package/modules/hosts/claude/commands/wf-sync-hosts.md +0 -64
- package/modules/hosts/claude/commands/wf-test.md +0 -68
- package/modules/hosts/codex/agents/implementer.md +0 -51
- package/modules/hosts/codex/agents/independent-reviewer.md +0 -49
- package/modules/hosts/codex/agents/ui-verifier.md +0 -48
- package/modules/hosts/codex/commands/wf-build.md +0 -69
- package/modules/hosts/codex/commands/wf-deploy.md +0 -58
- package/modules/hosts/codex/commands/wf-design.md +0 -49
- package/modules/hosts/codex/commands/wf-gate-checklist.md +0 -77
- package/modules/hosts/codex/commands/wf-maintain.md +0 -76
- package/modules/hosts/codex/commands/wf-new-task.md +0 -102
- package/modules/hosts/codex/commands/wf-pipeline-run.md +0 -35
- package/modules/hosts/codex/commands/wf-plan.md +0 -40
- package/modules/hosts/codex/commands/wf-review.md +0 -49
- package/modules/hosts/codex/commands/wf-sync-hosts.md +0 -64
- package/modules/hosts/codex/commands/wf-test.md +0 -68
- package/modules/hosts/codex/skills/flow-kit/SKILL.md +0 -14
- package/modules/hosts/cursor/agents/implementer.md +0 -51
- package/modules/hosts/cursor/agents/independent-reviewer.md +0 -49
- package/modules/hosts/cursor/agents/ui-verifier.md +0 -48
- package/modules/hosts/cursor/commands/wf-build.md +0 -69
- package/modules/hosts/cursor/commands/wf-deploy.md +0 -58
- package/modules/hosts/cursor/commands/wf-design.md +0 -49
- package/modules/hosts/cursor/commands/wf-gate-checklist.md +0 -77
- package/modules/hosts/cursor/commands/wf-maintain.md +0 -76
- package/modules/hosts/cursor/commands/wf-new-task.md +0 -102
- package/modules/hosts/cursor/commands/wf-pipeline-run.md +0 -35
- package/modules/hosts/cursor/commands/wf-plan.md +0 -40
- package/modules/hosts/cursor/commands/wf-review.md +0 -49
- package/modules/hosts/cursor/commands/wf-sync-hosts.md +0 -64
- package/modules/hosts/cursor/commands/wf-test.md +0 -68
- package/modules/hosts/omp/agents/implementer.md +0 -52
- package/modules/hosts/omp/agents/independent-reviewer.md +0 -50
- package/modules/hosts/omp/agents/ui-verifier.md +0 -49
- package/modules/hosts/opencode/agents/implementer.md +0 -59
- package/modules/hosts/opencode/agents/independent-reviewer.md +0 -53
- package/modules/hosts/opencode/agents/ui-verifier.md +0 -52
- package/modules/hosts/opencode/commands/wf-build.md +0 -69
- package/modules/hosts/opencode/commands/wf-deploy.md +0 -58
- package/modules/hosts/opencode/commands/wf-design.md +0 -49
- package/modules/hosts/opencode/commands/wf-gate-checklist.md +0 -77
- package/modules/hosts/opencode/commands/wf-maintain.md +0 -76
- package/modules/hosts/opencode/commands/wf-new-task.md +0 -102
- package/modules/hosts/opencode/commands/wf-pipeline-run.md +0 -35
- package/modules/hosts/opencode/commands/wf-plan.md +0 -40
- package/modules/hosts/opencode/commands/wf-review.md +0 -49
- package/modules/hosts/opencode/commands/wf-sync-hosts.md +0 -64
- package/modules/hosts/opencode/commands/wf-test.md +0 -68
- package/modules/hosts/trae/agents/implementer.md +0 -52
- package/modules/hosts/trae/agents/independent-reviewer.md +0 -51
- package/modules/hosts/trae/agents/ui-verifier.md +0 -50
- package/modules/hosts/trae/commands/wf-build.md +0 -70
- package/modules/hosts/trae/commands/wf-deploy.md +0 -59
- package/modules/hosts/trae/commands/wf-design.md +0 -50
- package/modules/hosts/trae/commands/wf-gate-checklist.md +0 -78
- package/modules/hosts/trae/commands/wf-maintain.md +0 -77
- package/modules/hosts/trae/commands/wf-new-task.md +0 -103
- package/modules/hosts/trae/commands/wf-pipeline-run.md +0 -36
- package/modules/hosts/trae/commands/wf-plan.md +0 -41
- package/modules/hosts/trae/commands/wf-review.md +0 -50
- package/modules/hosts/trae/commands/wf-sync-hosts.md +0 -65
- package/modules/hosts/trae/commands/wf-test.md +0 -69
- package/modules/hosts/trae/hooks/post-edit-check.cjs +0 -98
- package/modules/hosts/trae/hooks/pre-shell-check.cjs +0 -192
- package/modules/hosts/trae/hooks/resolve-shell.cjs +0 -30
- package/modules/hosts/trae/hooks.json +0 -29
- package/modules/hosts/trae/rules/beautify-input-chinese.md +0 -6
- package/modules/hosts/trae/rules/git-commit-message.md +0 -10
- package/modules/hosts/trae/rules/project-workflow.md +0 -14
- package/modules/hosts/zcode/agents/implementer.md +0 -51
- package/modules/hosts/zcode/agents/independent-reviewer.md +0 -49
- package/modules/hosts/zcode/agents/ui-verifier.md +0 -48
- package/src/add-gate.mjs +0 -116
- package/src/add-gate.test.mjs +0 -103
- package/src/add-host.mjs +0 -68
- package/src/cli.mjs +0 -169
- package/src/doctor.mjs +0 -629
- package/src/executor.mjs +0 -262
- package/src/executor.test.mjs +0 -295
- package/src/fresh-init.test.mjs +0 -64
- package/src/gate-dotnet-ca.test.mjs +0 -124
- package/src/gates.test.mjs +0 -143
- package/src/init.mjs +0 -382
- package/src/init.test.mjs +0 -163
- package/src/instance-ref.test.mjs +0 -62
- package/src/jscpd-drift.test.mjs +0 -145
- package/src/pack.test.mjs +0 -24
- package/src/profiles.mjs +0 -183
- package/src/render.mjs +0 -132
- package/src/rule-budget.test.mjs +0 -103
- package/src/run-tests.mjs +0 -56
- package/src/stack-profile.test.mjs +0 -95
- package/src/sync-hosts.mjs +0 -257
- package/src/sync-hosts.test.mjs +0 -291
- package/src/sync.mjs +0 -228
- package/src/sync.test.mjs +0 -452
- package/src/trae-hooks.test.mjs +0 -127
- package/src/validate.mjs +0 -77
- package/src/validate.test.mjs +0 -95
- package/templates/.gitattributes +0 -17
- package/templates/AGENTS.md +0 -58
- package/templates/_agents/board/index.html +0 -676
- package/templates/_agents/commands/build.md +0 -84
- package/templates/_agents/commands/deploy.md +0 -66
- package/templates/_agents/commands/design.md +0 -63
- package/templates/_agents/commands/gate-checklist.md +0 -91
- package/templates/_agents/commands/maintain.md +0 -90
- package/templates/_agents/commands/new-task.md +0 -110
- package/templates/_agents/commands/pipeline-run.md +0 -43
- package/templates/_agents/commands/plan.md +0 -48
- package/templates/_agents/commands/review.md +0 -62
- package/templates/_agents/commands/sync-hosts.md +0 -77
- package/templates/_agents/commands/test.md +0 -87
- package/templates/_agents/control-surfaces.txt +0 -53
- package/templates/_agents/engine-lock.json +0 -342
- package/templates/_agents/hooks/check-index-incremental.mjs +0 -48
- package/templates/_agents/hooks/check-pairing-incremental.mjs +0 -66
- package/templates/_agents/hooks/check-wiki-ledger.mjs +0 -30
- package/templates/_agents/hooks/commit-check.cjs +0 -193
- package/templates/_agents/hooks/local-pre-commit +0 -6
- package/templates/_agents/lane-surfaces.txt +0 -17
- package/templates/_agents/level-hints.txt +0 -30
- package/templates/_agents/loop/papercut-debt/DESIGN.md +0 -112
- package/templates/_agents/mechanism-registry.md +0 -35
- package/templates/_agents/metric-claims.txt +0 -81
- package/templates/_agents/notes/engine-internals.md +0 -17
- package/templates/_agents/notes/runtime-env.md +0 -18
- package/templates/_agents/roles/implementer.md +0 -47
- package/templates/_agents/roles/independent-reviewer.md +0 -45
- package/templates/_agents/roles/ui-verifier.md +0 -44
- package/templates/_agents/rule-budgets.txt +0 -20
- package/templates/_agents/scripts/agg-delegations.cjs +0 -302
- package/templates/_agents/scripts/agg-delegations.test.mjs +0 -282
- package/templates/_agents/scripts/agg-gate-stats.mjs +0 -113
- package/templates/_agents/scripts/agg-gate-stats.test.mjs +0 -95
- package/templates/_agents/scripts/agg-judgments.mjs +0 -156
- package/templates/_agents/scripts/agg-judgments.test.mjs +0 -93
- package/templates/_agents/scripts/archive-sweep.mjs +0 -235
- package/templates/_agents/scripts/archive-sweep.test.mjs +0 -129
- package/templates/_agents/scripts/check-attribution.mjs +0 -37
- package/templates/_agents/scripts/check-convergence.mjs +0 -124
- package/templates/_agents/scripts/check-engine-integrity.mjs +0 -220
- package/templates/_agents/scripts/check-engine-integrity.test.mjs +0 -215
- package/templates/_agents/scripts/check-funnel.mjs +0 -56
- package/templates/_agents/scripts/check-hygiene.mjs +0 -167
- package/templates/_agents/scripts/check-hygiene.test.mjs +0 -182
- package/templates/_agents/scripts/check-index-incremental.test.mjs +0 -73
- package/templates/_agents/scripts/check-lane-surface.mjs +0 -238
- package/templates/_agents/scripts/check-lane-surface.test.mjs +0 -302
- package/templates/_agents/scripts/check-ledger-invariant.mjs +0 -586
- package/templates/_agents/scripts/check-ledger-invariant.test.mjs +0 -639
- package/templates/_agents/scripts/check-loop-rev.test.mjs +0 -336
- package/templates/_agents/scripts/check-loop-unit.test.mjs +0 -103
- package/templates/_agents/scripts/check-loop.mjs +0 -1987
- package/templates/_agents/scripts/check-loop.sh +0 -6
- package/templates/_agents/scripts/check-loop.test.mjs +0 -3912
- package/templates/_agents/scripts/check-metric-claims.mjs +0 -277
- package/templates/_agents/scripts/check-prejudgment.mjs +0 -64
- package/templates/_agents/scripts/check-registry.mjs +0 -45
- package/templates/_agents/scripts/check-scope.mjs +0 -96
- package/templates/_agents/scripts/check-scope.test.mjs +0 -99
- package/templates/_agents/scripts/check-scripts-syntax.mjs +0 -84
- package/templates/_agents/scripts/check-scripts-syntax.test.mjs +0 -72
- package/templates/_agents/scripts/check-spec-verif.mjs +0 -47
- package/templates/_agents/scripts/check-stage-index.mjs +0 -40
- package/templates/_agents/scripts/check-stage-index.test.mjs +0 -77
- package/templates/_agents/scripts/closeout-l1.mjs +0 -217
- package/templates/_agents/scripts/closeout-l1.test.mjs +0 -105
- package/templates/_agents/scripts/commit-check-trigger.test.mjs +0 -205
- package/templates/_agents/scripts/compact-ledgers.mjs +0 -97
- package/templates/_agents/scripts/compact-ledgers.test.mjs +0 -162
- package/templates/_agents/scripts/confirm-doc.mjs +0 -610
- package/templates/_agents/scripts/confirm-doc.test.mjs +0 -1119
- package/templates/_agents/scripts/doctor.test.mjs +0 -770
- package/templates/_agents/scripts/draft-sediment.mjs +0 -178
- package/templates/_agents/scripts/draft-sediment.test.mjs +0 -272
- package/templates/_agents/scripts/edit-face-check.mjs +0 -109
- package/templates/_agents/scripts/edit-face-check.test.mjs +0 -99
- package/templates/_agents/scripts/ensure-board.mjs +0 -185
- package/templates/_agents/scripts/fill-intent.mjs +0 -91
- package/templates/_agents/scripts/fill-intent.test.mjs +0 -113
- package/templates/_agents/scripts/fill-plan.mjs +0 -79
- package/templates/_agents/scripts/fill-plan.test.mjs +0 -224
- package/templates/_agents/scripts/fill-spec.mjs +0 -70
- package/templates/_agents/scripts/fill-spec.test.mjs +0 -266
- package/templates/_agents/scripts/fixture-scripts.mjs +0 -153
- package/templates/_agents/scripts/fixture-scripts.test.mjs +0 -112
- package/templates/_agents/scripts/gate-checklist.mjs +0 -209
- package/templates/_agents/scripts/gate-checklist.test.mjs +0 -201
- package/templates/_agents/scripts/gate-seg.mjs +0 -54
- package/templates/_agents/scripts/gate-seg.test.mjs +0 -112
- package/templates/_agents/scripts/gen-effective-policy.mjs +0 -107
- package/templates/_agents/scripts/gen-effective-policy.test.mjs +0 -100
- package/templates/_agents/scripts/gen-verify-evidence.mjs +0 -107
- package/templates/_agents/scripts/gen-verify-evidence.test.mjs +0 -84
- package/templates/_agents/scripts/gen-wiki-board.mjs +0 -276
- package/templates/_agents/scripts/gen-wiki-board.test.mjs +0 -249
- package/templates/_agents/scripts/gen-workflow-dashboard.mjs +0 -251
- package/templates/_agents/scripts/gen-workflow-dashboard.test.mjs +0 -188
- package/templates/_agents/scripts/gen-workflow-index.mjs +0 -201
- package/templates/_agents/scripts/gen-workflow-index.test.mjs +0 -152
- package/templates/_agents/scripts/gen-workflow-metrics.mjs +0 -466
- package/templates/_agents/scripts/gen-workflow-metrics.test.mjs +0 -343
- package/templates/_agents/scripts/git-merge-jsonl.mjs +0 -63
- package/templates/_agents/scripts/git-merge-jsonl.test.mjs +0 -125
- package/templates/_agents/scripts/judgment-ledger.mjs +0 -65
- package/templates/_agents/scripts/judgment-ledger.test.mjs +0 -129
- package/templates/_agents/scripts/kb-cache-evict.mjs +0 -50
- package/templates/_agents/scripts/kb-cache-evict.test.mjs +0 -104
- package/templates/_agents/scripts/kb-search.mjs +0 -413
- package/templates/_agents/scripts/kb-search.test.mjs +0 -299
- package/templates/_agents/scripts/lane-surface-audit.mjs +0 -143
- package/templates/_agents/scripts/lane-surface-audit.test.mjs +0 -93
- package/templates/_agents/scripts/ledger-doc-chain.test.mjs +0 -166
- package/templates/_agents/scripts/ledger-loader.mjs +0 -108
- package/templates/_agents/scripts/ledger-loader.test.mjs +0 -131
- package/templates/_agents/scripts/ledger-prune.mjs +0 -96
- package/templates/_agents/scripts/ledger-prune.test.mjs +0 -120
- package/templates/_agents/scripts/ledger-shards.mjs +0 -344
- package/templates/_agents/scripts/ledger-shards.test.mjs +0 -356
- package/templates/_agents/scripts/ledger-ts-monotonic.test.mjs +0 -135
- package/templates/_agents/scripts/level-criteria.mjs +0 -231
- package/templates/_agents/scripts/level-criteria.test.mjs +0 -281
- package/templates/_agents/scripts/mechanism-retire-watch.mjs +0 -183
- package/templates/_agents/scripts/mechanism-retire-watch.test.mjs +0 -114
- package/templates/_agents/scripts/papercut-debt-controller.mjs +0 -141
- package/templates/_agents/scripts/papercut-debt-controller.test.mjs +0 -127
- package/templates/_agents/scripts/papercut-debt-sensor.mjs +0 -170
- package/templates/_agents/scripts/papercut-debt-sensor.test.mjs +0 -146
- package/templates/_agents/scripts/pipeline-run-docs.mjs +0 -76
- package/templates/_agents/scripts/pipeline-run-run.mjs +0 -63
- package/templates/_agents/scripts/pipeline-run.mjs +0 -867
- package/templates/_agents/scripts/pipeline-run.test.mjs +0 -517
- package/templates/_agents/scripts/policy.mjs +0 -281
- package/templates/_agents/scripts/pre-push.test.mjs +0 -105
- package/templates/_agents/scripts/resolve-merge-conflicts.mjs +0 -110
- package/templates/_agents/scripts/resolve-merge-conflicts.test.mjs +0 -87
- package/templates/_agents/scripts/rewrite-file.mjs +0 -133
- package/templates/_agents/scripts/rewrite-file.test.mjs +0 -148
- package/templates/_agents/scripts/rule-budget.mjs +0 -96
- package/templates/_agents/scripts/rule-budget.test.mjs +0 -118
- package/templates/_agents/scripts/run-tests.mjs +0 -99
- package/templates/_agents/scripts/run-tests.test.mjs +0 -105
- package/templates/_agents/scripts/solidify-task.mjs +0 -109
- package/templates/_agents/scripts/solidify-task.test.mjs +0 -129
- package/templates/_agents/scripts/stage-gates.mjs +0 -343
- package/templates/_agents/scripts/stage-gates.test.mjs +0 -105
- package/templates/_agents/scripts/trust-mode.mjs +0 -67
- package/templates/_agents/scripts/trust-mode.test.mjs +0 -110
- package/templates/_agents/scripts/verify-wiki-consistency.mjs +0 -253
- package/templates/_agents/scripts/verify-wiki-consistency.test.mjs +0 -123
- package/templates/_agents/scripts/verify.mjs +0 -345
- package/templates/_agents/scripts/verify.test.mjs +0 -373
- package/templates/_agents/scripts/wf-journal.mjs +0 -231
- package/templates/_agents/scripts/wf-journal.test.mjs +0 -216
- package/templates/_agents/scripts/wiki-search.mjs +0 -19
- package/templates/_agents/scripts/wiki-search.test.mjs +0 -57
- package/templates/_agents/scripts/workflow-board-server.mjs +0 -393
- package/templates/_agents/scripts/workflow-board-server.test.mjs +0 -201
- package/templates/_agents/scripts/workflow-enums.mjs +0 -70
- package/templates/_agents/scripts/workflow-enums.test.mjs +0 -118
- package/templates/_agents/scripts/workflows-check.mjs +0 -260
- package/templates/_agents/scripts/workflows-check.test.mjs +0 -278
- package/templates/_agents/scripts-test-exempt.txt +0 -16
- package/templates/_agents/skills/papercut-fix/SKILL.md +0 -60
- package/templates/_agents/skills/papercut-fix/references/response-template.md +0 -81
- package/templates/_agents/skills/review-method/SKILL.md +0 -58
- package/templates/_agents/skills/verify-ui/SKILL.md +0 -61
- package/templates/_agents/skills/wiki/SKILL.md +0 -106
- package/templates/_agents/workflow-enums.txt +0 -26
- package/templates/_agents/workflow-modules.txt +0 -11
- package/templates/_agents/workflows/_TEMPLATE.md +0 -75
- package/templates/_agents/workflows/pipeline-closing.md +0 -28
- package/templates/_agents/workflows//347/244/272/344/276/213-/345/271/266/350/241/214/345/256/236/347/216/260/350/257/204/345/256/241.md +0 -16
- package/templates/_githooks/commit-msg +0 -17
- package/templates/_githooks/post-commit +0 -10
- package/templates/_githooks/pre-commit +0 -126
- package/templates/_githooks/pre-merge-commit +0 -29
- package/templates/_githooks/pre-push +0 -136
- package/templates/_github/workflows/kit-ci.yml +0 -49
- package/templates/wiki/INDEX.md +0 -73
- package/templates/wiki/drafts-archive/README.md +0 -3
- package/templates/wiki//344/272/247/345/223/201/351/234/200/346/261/202/README.md +0 -3
- package/templates/wiki//345/274/200/345/217/221/346/226/271/346/241/210/README.md +0 -3
- package/templates/wiki//346/225/260/346/215/256/347/273/264/346/212/244/README.md +0 -3
- package/templates/wiki//346/265/213/350/257/225/346/212/245/345/221/212/README.md +0 -3
- package/templates/wiki//347/224/250/346/210/267/345/212/237/350/203/275/README.md +0 -3
- package/templates/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +0 -77
- package/templates/wiki//351/241/271/347/233/256/350/247/204/350/214/203/README.md +0 -3
- package/templates/wiki//351/241/271/347/233/256/350/256/241/345/210/222/README.md +0 -3
- package/templates/workflow/README.md +0 -91
- package/templates/workflow/delegations.md +0 -29
- package/templates/workflow/incidents/_TEMPLATE.md +0 -51
- package/templates/workflow/intents/_TEMPLATE.md +0 -51
- package/templates/workflow/papercuts.md +0 -9
- package/templates/workflow/plans/_TEMPLATE.md +0 -47
- package/templates/workflow/regression-checklist.md +0 -23
- package/templates/workflow/specs/_TEMPLATE.md +0 -80
- /package/templates/{_agents → engine/.agents}/board/marked.min.js +0 -0
- /package/templates/{_agents/workflows → engine/.agents/pipelines}/steps//347/244/272/344/276/213-/351/203/250/347/275/262/351/252/214/350/257/201.md" +0 -0
- /package/templates/{_agents → engine/.agents}/scripts/git-core.mjs +0 -0
|
@@ -1,3912 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// check-loop.test.mjs — 闭环门禁 fixture 套件(2026-09-26 check-loop-node:自 check-loop.test.sh 全量移植)
|
|
3
|
-
// 方法:现场构造临时 workflow 根(含枚举单源内联件),spawn node check-loop.mjs(CHECK_LOOP_ROOT 注入,
|
|
4
|
-
// git 模式场景改 cwd 注入),断言 exit code 与输出关键词——场景集与关键词断言沿 sh 版逐条移植。
|
|
5
|
-
// git 场景用 git init 现场建仓;rule-budget --staged 两场景 node 直跑(2026-10-13 门禁去 sh 化——
|
|
6
|
-
// rule-budget.mjs 无 sh 依赖)。
|
|
7
|
-
// 用法:node templates/_agents/scripts/check-loop.test.mjs(npm test 随跑)
|
|
8
|
-
import fs from 'node:fs';
|
|
9
|
-
import os from 'node:os';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { spawnSync } from 'node:child_process';
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import { computeFingerprint } from './confirm-doc.mjs';
|
|
14
|
-
import { POLICIES, loadKitPolicy } from './policy.mjs';
|
|
15
|
-
import { entryConfirmed, laneOfEntry, laneOfDoc } from './stage-gates.mjs';
|
|
16
|
-
import { parseAddedDatesLog, addedDatesWithCache, scenarioIdsOf, extractThenByScenario, sectionHasBody, sedimentLandingIsShell } from './check-loop.mjs';
|
|
17
|
-
|
|
18
|
-
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const CHECK_LOOP = path.join(SCRIPT_DIR, 'check-loop.mjs');
|
|
20
|
-
const GIT = process.platform === 'win32' ? 'git.exe' : 'git'; // Windows spawn 不补 .exe 扩展名(gitInit/gitCommitAll/场景 43/44/47 共用)
|
|
21
|
-
// 注意(2026-10-08 checkloop-importable 后):check-loop.mjs 已有 isMain 主守卫,可 import(runCheckLoop/四纯零件,直测见 check-loop-unit.test.mjs)——本套件保持端到端 spawn 作为行为回归网,
|
|
22
|
-
// 不可从其 import 函数直测(2026-10-04 review-fix-batch 实证;可 import 化记 papercuts 待 L2 重构)
|
|
23
|
-
|
|
24
|
-
let pass = 0;
|
|
25
|
-
let fail = 0;
|
|
26
|
-
const check = (desc, cond, detail = '') => {
|
|
27
|
-
if (cond) { pass++; console.log(`PASS ${desc}`); }
|
|
28
|
-
else { fail++; console.log(`FAIL ${desc}${detail ? '\n ' + detail : ''}`); }
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// 夹具闭包工具(2026-10-12 fixture-closure-rootfix):本文件此前 5 处手写依赖枚举,2026-10-12 那批
|
|
32
|
-
// 台账改动漏拷 ledger-loader.mjs 就假绿过(看板漂移门恒绿)。改为按入口自动求传递闭包——新增依赖自动跟随。
|
|
33
|
-
const { copyScriptClosure, copyAgentsFiles } = await import('./fixture-scripts.mjs');
|
|
34
|
-
|
|
35
|
-
// mkfix:空 fixture 根(四目录 + 枚举单源内联 10 键——fixture 自包含,测消费逻辑而非当前词表值;
|
|
36
|
-
// 2026-10-13 B6b-2 起 +incident.source.all/incident.env.all 两键:检查 24 归因枚举消费面)
|
|
37
|
-
const ENUMS_FIXTURE = [
|
|
38
|
-
'doc.status.all=draft approved done superseded cancelled',
|
|
39
|
-
'doc.status.confirmed=approved done superseded cancelled',
|
|
40
|
-
'doc.status.active=draft approved',
|
|
41
|
-
'doc.status.terminal=done superseded cancelled',
|
|
42
|
-
'doc.status.abandoned=superseded cancelled',
|
|
43
|
-
'incident.status.all=open fixed closed',
|
|
44
|
-
'incident.status.active=open',
|
|
45
|
-
'level.all=L1 L2 L3',
|
|
46
|
-
'incident.source.all=self user test monitor',
|
|
47
|
-
'incident.env.all=dev staging prod none',
|
|
48
|
-
].join('\n') + '\n';
|
|
49
|
-
|
|
50
|
-
const mkfix = () => {
|
|
51
|
-
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'check-loop-test-'));
|
|
52
|
-
for (const s of ['intents', 'specs', 'plans', 'incidents']) {
|
|
53
|
-
fs.mkdirSync(path.join(d, 'workflow', s), { recursive: true });
|
|
54
|
-
}
|
|
55
|
-
fs.mkdirSync(path.join(d, '.agents'), { recursive: true });
|
|
56
|
-
fs.writeFileSync(path.join(d, '.agents', 'workflow-enums.txt'), ENUMS_FIXTURE);
|
|
57
|
-
return d;
|
|
58
|
-
};
|
|
59
|
-
const w = (root, rel, content) => fs.writeFileSync(path.join(root, rel), content);
|
|
60
|
-
|
|
61
|
-
const INTENT = (name, fm, body = '') => `---\n${fm}\n---\n# INTENT — ${name}\n${body}`;
|
|
62
|
-
const PLAN = (name, fm, body = '') => `---\n${fm}\n---\n# PLAN — ${name}\n${body}`;
|
|
63
|
-
const SPEC = (name, fm, body = '') => `---\n${fm}\n---\n# SPEC — ${name}\n${body}`;
|
|
64
|
-
const 三件套 = (refs) => `## 复盘三件套(缺一不可)
|
|
65
|
-
|
|
66
|
-
1. 结构性修复
|
|
67
|
-
- 修复 commit:abc1234
|
|
68
|
-
- 影响环境:dev
|
|
69
|
-
- 是否需要新 intent:
|
|
70
|
-
${refs ? ' - 是 → ../intents/2026-09-12-ghost.md\n' : ' - 否 → 理由:实现 bug 单点修复\n'}
|
|
71
|
-
2. 防复发验证
|
|
72
|
-
- 回归清单条目在案
|
|
73
|
-
|
|
74
|
-
3. 规范条目
|
|
75
|
-
- AGENTS.md 某节
|
|
76
|
-
`;
|
|
77
|
-
|
|
78
|
-
// run:默认 CHECK_LOOP_ROOT 注入(fixture 模式);{git:true} 时 cwd 注入(真 git 模式,测 tracked 过滤);
|
|
79
|
-
// opts.args 替换默认 `--all`(套件默认全量 = 分道前裸跑语义;2026-10-13 分道后裸跑=默认硬门,沿源仓
|
|
80
|
-
// check-loop.test.mjs 同款约定——硬门默认入口用 { args: [] },分道场景显式传 --hygiene);
|
|
81
|
-
// opts.env 增量覆盖子进程 env(CHECK_LOOP_GIT 替身注入用,2026-10-05-gitout-fail-open)
|
|
82
|
-
const run = (root, opts = {}) => spawnSync(process.execPath, [CHECK_LOOP, ...(opts.args ?? ['--all'])], {
|
|
83
|
-
...(opts.git
|
|
84
|
-
? { cwd: root, ...(opts.env ? { env: { ...process.env, ...opts.env } } : {}) }
|
|
85
|
-
: { cwd: ROOT_CWD, env: { ...process.env, ...(opts.env || {}), CHECK_LOOP_ROOT: root } }),
|
|
86
|
-
encoding: 'utf8',
|
|
87
|
-
});
|
|
88
|
-
const ROOT_CWD = process.cwd();
|
|
89
|
-
const outOf = (r) => `${r.stdout || ''}${r.stderr || ''}`;
|
|
90
|
-
const expectHard = (desc, root, kw) => {
|
|
91
|
-
const r = run(root);
|
|
92
|
-
check(desc, r.status === 1 && outOf(r).includes(kw), `exit=${r.status}\n${outOf(r)}`);
|
|
93
|
-
};
|
|
94
|
-
const expectOk = (desc, root) => {
|
|
95
|
-
const r = run(root);
|
|
96
|
-
check(desc, r.status === 0, `exit=${r.status}\n${outOf(r)}`);
|
|
97
|
-
};
|
|
98
|
-
// 夹具 git 调用防瞬时失败(2026-10-05-gitout-fail-open 夹具侧加固):宿主/CI 资源压力下 spawnSync 可能
|
|
99
|
-
// 瞬时 `r.error`,git 侧也可能瞬时 status≠0(如 index.lock 争用)——对夹具 setup 调用(init/add/commit/
|
|
100
|
-
// rev-parse)两者都永不合法:重试一次,持续失败抛错响亮失败。绝不静默产出空 sha / 缺提交的假夹具
|
|
101
|
-
// (那会让证据场景拿到「无裁决」的错误结果——CI run 37252874044 flake 的实际根因侧,诊断详见同名 incident)
|
|
102
|
-
function gitRetry(args, root) {
|
|
103
|
-
const opts = { cwd: root, encoding: 'utf8' };
|
|
104
|
-
let r = spawnSync(GIT, args, opts);
|
|
105
|
-
if (r.error || r.status !== 0) {
|
|
106
|
-
r = spawnSync(GIT, args, opts);
|
|
107
|
-
if (r.error || r.status !== 0) {
|
|
108
|
-
throw new Error(`fixture git 瞬时失败(重试后仍 ${r.error ? (r.error.code || r.error.message) : `exit ${r.status}`}): git ${args.join(' ')}\n${(r.stderr || '').slice(0, 300)}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return r;
|
|
112
|
-
}
|
|
113
|
-
const shortSha = (root, args = ['rev-parse', '--short', 'HEAD']) => {
|
|
114
|
-
const out = gitRetry(args, root).stdout.trim();
|
|
115
|
-
if (!out) throw new Error(`fixture git 空输出(假夹具防线): git ${args.join(' ')}`);
|
|
116
|
-
return out;
|
|
117
|
-
};
|
|
118
|
-
const gitInit = (root) => {
|
|
119
|
-
gitRetry(['init', '-q'], root);
|
|
120
|
-
};
|
|
121
|
-
const gitCommitAll = (root, msg) => {
|
|
122
|
-
gitRetry(['add', '-A'], root);
|
|
123
|
-
gitRetry(['-c', 'user.email=t@t', '-c', 'user.name=t', 'commit', '-qm', msg], root);
|
|
124
|
-
};
|
|
125
|
-
// gitCommitAllAt:带 author/committer 日期的提交(B6b-2 §21/§3x 场景用——那两段锚取 git 首加日期,
|
|
126
|
-
// 夹具须钉死在锚日/锚后,套件跨日重跑才不翻面;iso 形如 '2026-10-13T01:00:00.000Z')
|
|
127
|
-
const gitCommitAllAt = (root, msg, iso) => {
|
|
128
|
-
gitRetry(['add', '-A'], root);
|
|
129
|
-
const env = { ...process.env, GIT_AUTHOR_DATE: iso, GIT_COMMITTER_DATE: iso };
|
|
130
|
-
const r = spawnSync(GIT, ['-c', 'user.email=t@t', '-c', 'user.name=t', 'commit', '-qm', msg], { cwd: root, encoding: 'utf8', env });
|
|
131
|
-
if (r.error || r.status !== 0) throw new Error(`fixture git 回溯提交失败: git commit(${iso})\n${(r.stderr || '').slice(0, 300)}`);
|
|
132
|
-
};
|
|
133
|
-
const rmfix = (root) => fs.rmSync(root, { recursive: true, force: true });
|
|
134
|
-
// writeLedger:fixture 台账辅助(检查 15 配对用;行 schema 同 confirm-doc.mjs appendLedger)
|
|
135
|
-
const writeLedger = (root, entries) => {
|
|
136
|
-
fs.mkdirSync(path.join(root, '.agents'), { recursive: true });
|
|
137
|
-
fs.writeFileSync(path.join(root, '.agents', 'confirmations.jsonl'),
|
|
138
|
-
entries.map((e) => JSON.stringify(e)).join('\n') + '\n');
|
|
139
|
-
};
|
|
140
|
-
// mkConfirmedDoc:落一份「带真实指纹 + 台账配对」的文档(复刻 confirm-doc 落态形态)
|
|
141
|
-
const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
142
|
-
const noFp = `---\n${fmBody}\n---\n# DOC\n`;
|
|
143
|
-
const fp = computeFingerprint(noFp);
|
|
144
|
-
fs.writeFileSync(path.join(root, rel), `---\n${fmBody}\n确认指纹: ${fp.slice(0, 16)}\n---\n# DOC\n`);
|
|
145
|
-
return { rel: rel.replace(/\\/g, '/'), fp };
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// ---- 场景 1:全合法闭环(intent+plan,L1,done 全勾验带证据)→ exit 0 ----
|
|
149
|
-
{
|
|
150
|
-
const T = mkfix();
|
|
151
|
-
w(T, 'workflow/intents/2026-09-12-ok.md', INTENT('ok', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:dotnet test 全绿)\n'));
|
|
152
|
-
w(T, 'workflow/plans/2026-09-12-ok.md', PLAN('ok', '状态: done\n级别: L1'));
|
|
153
|
-
expectOk('全合法闭环(L1 done 勾验带证据) → exit 0', T);
|
|
154
|
-
rmfix(T);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ---- 场景 2:intent 缺 plan → hard ----
|
|
158
|
-
{
|
|
159
|
-
const T = mkfix();
|
|
160
|
-
w(T, 'workflow/intents/2026-09-12-a.md', INTENT('a', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
161
|
-
expectHard('intent 缺 plan → hard 配对断裂', T, '配对断裂');
|
|
162
|
-
rmfix(T);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ---- 场景 3:L2 intent 缺 spec → hard ----
|
|
166
|
-
{
|
|
167
|
-
const T = mkfix();
|
|
168
|
-
w(T, 'workflow/intents/2026-09-12-b.md', INTENT('b', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
169
|
-
w(T, 'workflow/plans/2026-09-12-b.md', PLAN('b', '状态: approved\n级别: L2'));
|
|
170
|
-
expectHard('L2 intent 缺 spec → hard 配对断裂', T, 'intent 缺 spec');
|
|
171
|
-
rmfix(T);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ---- 场景 4:L3 spec 缺确认三件 → hard ----
|
|
175
|
-
{
|
|
176
|
-
const T = mkfix();
|
|
177
|
-
w(T, 'workflow/intents/2026-09-12-c.md', INTENT('c', '状态: approved\n级别: L3\n日期: 2026-09-12'));
|
|
178
|
-
w(T, 'workflow/specs/2026-09-12-c.md', SPEC('c', '状态: approved\n级别: L3', '\n## 确认与复核\n'));
|
|
179
|
-
w(T, 'workflow/plans/2026-09-12-c.md', PLAN('c', '状态: approved\n级别: L3'));
|
|
180
|
-
expectHard('L3 spec 缺确认三件 → hard', T, 'L3 确认缺失');
|
|
181
|
-
rmfix(T);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ---- 场景 5:L3 spec 确认三件齐全 → exit 0 ----
|
|
185
|
-
{
|
|
186
|
-
const T = mkfix();
|
|
187
|
-
w(T, 'workflow/intents/2026-09-12-d.md', INTENT('d', '状态: approved\n级别: L3\n日期: 2026-09-12'));
|
|
188
|
-
w(T, 'workflow/specs/2026-09-12-d.md', SPEC('d', '状态: approved\n级别: L3\n确认结果: approved\n确认时间: 2026-09-12', '\n## 确认与复核\n- 独立复核:已由 independent-reviewer 新会话完成,结论 approved\n'));
|
|
189
|
-
w(T, 'workflow/plans/2026-09-12-d.md', PLAN('d', '状态: approved\n级别: L3'));
|
|
190
|
-
expectOk('L3 确认三件齐全(frontmatter 2 件+正文独立复核) → exit 0', T);
|
|
191
|
-
rmfix(T);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// ---- 场景 6:新建 done intent 验收未勾验 → hard;带附注不影响判定(papercut #4 回归)----
|
|
195
|
-
// 换锚(2026-09-28 check8-git-anchor)后「新建」由 **git 首次加入日期**判定(文件名字段不再作锚),
|
|
196
|
-
// 故本场景须为真 git 仓——否则不可判定、走存量口径而不再 hard。**意图与断言强度均不变**。
|
|
197
|
-
{
|
|
198
|
-
const T = mkfix();
|
|
199
|
-
gitInit(T);
|
|
200
|
-
w(T, 'workflow/intents/2026-09-13-e.md', INTENT('e', '状态: done\n级别: L1\n日期: 2026-09-13\n备注: 回溯补档,原工作 2026-09-12 完成', '\n## 验收标准(可测试)\n- [ ] 用例通过\n'));
|
|
201
|
-
w(T, 'workflow/plans/2026-09-13-e.md', PLAN('e', '状态: done\n级别: L1'));
|
|
202
|
-
gitCommitAll(T, 'add');
|
|
203
|
-
expectHard('新建 done 未勾验(附注在备注键) → hard 验收未对账', T, '验收未对账');
|
|
204
|
-
rmfix(T);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ---- 场景 7:存量(<2026-09-12) done 未勾验 → warning,exit 0 ----
|
|
208
|
-
{
|
|
209
|
-
const T = mkfix();
|
|
210
|
-
w(T, 'workflow/intents/2026-09-01-f.md', INTENT('f', '状态: done\n级别: L1\n日期: 2026-09-01', '\n## 验收标准(可测试)\n- [ ] 用例通过\n'));
|
|
211
|
-
w(T, 'workflow/plans/2026-09-01-f.md', PLAN('f', '状态: done\n级别: L1'));
|
|
212
|
-
expectOk('存量 done 未勾验 → warning 不阻断', T);
|
|
213
|
-
rmfix(T);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ---- 场景 8:incident 选「是」但 intent 不存在 → hard 回路断档 ----
|
|
217
|
-
{
|
|
218
|
-
const T = mkfix();
|
|
219
|
-
w(T, 'workflow/incidents/2026-09-12-g.md', `---\n状态: fixed\n级别: L2\n发现: 2026-09-12\n---\n# INCIDENT — g\n\n${三件套(true)}`);
|
|
220
|
-
w(T, 'workflow/plans/2026-09-12-g.md', PLAN('g', '状态: done\n级别: L2'));
|
|
221
|
-
w(T, 'workflow/specs/2026-09-12-g.md', SPEC('g', '状态: approved\n级别: L2'));
|
|
222
|
-
expectHard('incident 回路引用不存在 intent → hard 回路断档', T, '回路断档');
|
|
223
|
-
rmfix(T);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ---- 场景 9:incident 流程 legacy → 豁免级别/配对检查,exit 0 ----
|
|
227
|
-
{
|
|
228
|
-
const T = mkfix();
|
|
229
|
-
w(T, 'workflow/incidents/2026-08-27-h.md', `---\n状态: closed\n流程: legacy\n发现: 2026-08-27\n---\n# INCIDENT — h(legacy 回填,无级别无配对)\n\n${三件套(false)}`);
|
|
230
|
-
expectOk('incident legacy 豁免配对 → exit 0', T);
|
|
231
|
-
rmfix(T);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// ---- 场景 10:无 frontmatter → 状态键缺失 warning,exit 0(非 L3) ----
|
|
235
|
-
{
|
|
236
|
-
const T = mkfix();
|
|
237
|
-
w(T, 'workflow/intents/2026-09-12-i.md', '# INTENT — i(未迁移旧格式)\n\n状态:draft\n');
|
|
238
|
-
w(T, 'workflow/plans/2026-09-12-i.md', PLAN('i', '状态: approved\n级别: L1'));
|
|
239
|
-
const r = run(T);
|
|
240
|
-
check('无 frontmatter → warning 提示状态键缺失', r.status === 0 && /WARN 状态未确认.*i\.md/.test(outOf(r)), outOf(r));
|
|
241
|
-
rmfix(T);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// ---- 场景 11:frontmatter 状态值域外(自造值)→ warning 带当前值,exit 0 ----
|
|
245
|
-
{
|
|
246
|
-
const T = mkfix();
|
|
247
|
-
w(T, 'workflow/intents/2026-09-12-j.md', INTENT('j', '状态: 进行中\n级别: L1\n日期: 2026-09-12'));
|
|
248
|
-
w(T, 'workflow/plans/2026-09-12-j.md', PLAN('j', '状态: approved\n级别: L1'));
|
|
249
|
-
const r = run(T);
|
|
250
|
-
check('状态值域外 → warning 且带当前值提示', r.status === 0 && /WARN 状态未确认.*j\.md.*进行中/.test(outOf(r)), outOf(r));
|
|
251
|
-
rmfix(T);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// ---- 场景 12:无 frontmatter 的旧格式 L3 声明 → warning 引导补 frontmatter(不回退读正文,双源防回归)----
|
|
255
|
-
{
|
|
256
|
-
const T = mkfix();
|
|
257
|
-
for (const s of ['intents', 'plans', 'specs']) {
|
|
258
|
-
w(T, `workflow/${s}/2026-09-12-k.md`, `# ${s.toUpperCase().slice(0, -1)} — k\n\n级别:L3\n状态:approved\n`);
|
|
259
|
-
}
|
|
260
|
-
const r = run(T);
|
|
261
|
-
check('无 frontmatter 旧格式 → warning 引导补 frontmatter(不回退读正文)',
|
|
262
|
-
r.status === 0 && /WARN 状态未确认.*k\.md(frontmatter 状态键当前值:『缺失』)/.test(outOf(r)), outOf(r));
|
|
263
|
-
rmfix(T);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ---- 场景 13:git fixture——L1 intent 与迁移 SQL 同 commit → warning 疑似判低(检查 10)----
|
|
267
|
-
{
|
|
268
|
-
const T = mkfix();
|
|
269
|
-
gitInit(T);
|
|
270
|
-
w(T, 'workflow/intents/2026-09-12-m.md', INTENT('m', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 场景构造项(证据:fixture)\n'));
|
|
271
|
-
w(T, 'workflow/plans/2026-09-12-m.md', PLAN('m', '状态: done\n级别: L1'));
|
|
272
|
-
fs.mkdirSync(path.join(T, 'docs', 'scripts'), { recursive: true });
|
|
273
|
-
w(T, 'docs/scripts/01-add-table.sql', 'CREATE TABLE t(id INT);\n');
|
|
274
|
-
gitCommitAll(T, 'test');
|
|
275
|
-
const r = run(T);
|
|
276
|
-
check('L1 入口文档提交触及迁移 SQL → warning 疑似判低', r.status === 0 && /WARN 级别疑似判低.*m\.md/.test(outOf(r)), outOf(r));
|
|
277
|
-
rmfix(T);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// ---- 场景 14:git fixture——L3 intent 与迁移 SQL 同 commit → 级别一致不告警 ----
|
|
281
|
-
{
|
|
282
|
-
const T = mkfix();
|
|
283
|
-
gitInit(T);
|
|
284
|
-
w(T, 'workflow/intents/2026-09-12-n.md', INTENT('n', '状态: done\n级别: L3\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 场景构造项(证据:fixture)\n'));
|
|
285
|
-
w(T, 'workflow/specs/2026-09-12-n.md', SPEC('n', '状态: done\n级别: L3\n确认结果: approved\n确认时间: 2026-09-12', '\n## 确认与复核\n- 独立复核:已由 independent-reviewer 新会话完成,结论 approved\n'));
|
|
286
|
-
w(T, 'workflow/plans/2026-09-12-n.md', PLAN('n', '状态: done\n级别: L3'));
|
|
287
|
-
fs.mkdirSync(path.join(T, 'docs', 'scripts'), { recursive: true });
|
|
288
|
-
w(T, 'docs/scripts/01-add-table.sql', 'CREATE TABLE t(id INT);\n');
|
|
289
|
-
gitCommitAll(T, 'test');
|
|
290
|
-
const r = run(T);
|
|
291
|
-
check('L3 入口文档提交触及迁移 SQL → 级别一致不告警', r.status === 0 && !outOf(r).includes('级别疑似判低'), outOf(r));
|
|
292
|
-
rmfix(T);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// ---- 场景 15:ZCode Adapter 缺失不告警(宿主未装),存在且断线仍告警 ----
|
|
296
|
-
{
|
|
297
|
-
const T = mkfix();
|
|
298
|
-
w(T, 'workflow/intents/2026-09-12-o.md', INTENT('o', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:全绿)\n'));
|
|
299
|
-
w(T, 'workflow/plans/2026-09-12-o.md', PLAN('o', '状态: done\n级别: L1'));
|
|
300
|
-
fs.mkdirSync(path.join(T, '.agents', 'roles'), { recursive: true });
|
|
301
|
-
fs.mkdirSync(path.join(T, '.zcode', 'agents'), { recursive: true });
|
|
302
|
-
for (const r of ['implementer', 'independent-reviewer', 'ui-verifier']) w(T, `.agents/roles/${r}.md`, `# role ${r}\n`);
|
|
303
|
-
w(T, '.zcode/agents/implementer.md', '---\nname: implementer\n---\n断线内容,无角色引用\n');
|
|
304
|
-
const r = run(T);
|
|
305
|
-
check('ZCode 缺失跳过/存在断线仍告警',
|
|
306
|
-
r.status === 0 && outOf(r).includes('ZCode implementer 未引用公共角色') && !outOf(r).includes('ZCode 缺'), outOf(r));
|
|
307
|
-
rmfix(T);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// ---- 场景 16:skills 引用仓库内与用户级均不存在 → 仍报引用断档 ----
|
|
311
|
-
{
|
|
312
|
-
const T = mkfix();
|
|
313
|
-
w(T, 'workflow/README.md', '# WF README\n\n技能见 .agents/skills/fake-skill(仓库内与用户级均不存在)\n');
|
|
314
|
-
w(T, 'workflow/intents/2026-09-12-p.md', INTENT('p', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:全绿)\n'));
|
|
315
|
-
w(T, 'workflow/plans/2026-09-12-p.md', PLAN('p', '状态: done\n级别: L1'));
|
|
316
|
-
const r = run(T);
|
|
317
|
-
check('skills 引用仓库内与用户级均无 → 仍报引用断档', r.status === 0 && /引用断档.*fake-skill/.test(outOf(r)), outOf(r));
|
|
318
|
-
rmfix(T);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// ---- 场景 17:存量 done 含「存量对账豁免」声明 → 出账,无验收对账 warning ----
|
|
322
|
-
{
|
|
323
|
-
const T = mkfix();
|
|
324
|
-
w(T, 'workflow/intents/2026-09-01-q.md', INTENT('q', '状态: done\n级别: L1\n日期: 2026-09-01', '\n## 验收标准(可测试)\n> **存量对账豁免(2026-09-12,用户拍板不追溯)**:下方未勾项均为第二轮 UI/浏览器实测项。\n- [ ] 用例通过\n'));
|
|
325
|
-
w(T, 'workflow/plans/2026-09-01-q.md', PLAN('q', '状态: done\n级别: L1'));
|
|
326
|
-
const r = run(T);
|
|
327
|
-
check('存量含豁免声明 → 出账不计未对账', r.status === 0 && !outOf(r).includes('验收对账存量'), outOf(r));
|
|
328
|
-
rmfix(T);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// ---- 场景 18:新建 done intent 缺「## 验收标准」节 → hard(2026-09-16 补口回归)----
|
|
332
|
-
{
|
|
333
|
-
const T = mkfix();
|
|
334
|
-
gitInit(T);
|
|
335
|
-
w(T, 'workflow/intents/2026-09-13-r.md', INTENT('r', '状态: done\n级别: L1\n日期: 2026-09-13', '\n## 目标\n关单前未写验收标准节。\n'));
|
|
336
|
-
w(T, 'workflow/plans/2026-09-13-r.md', PLAN('r', '状态: done\n级别: L1'));
|
|
337
|
-
gitCommitAll(T, 'add');
|
|
338
|
-
expectHard('新建 done 缺验收标准节 → hard 验收未对账', T, '缺「## 验收标准」节');
|
|
339
|
-
rmfix(T);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// ---- 场景 19:新建 done 勾选项缩进写法 → hard(口径放宽后仍拦)----
|
|
343
|
-
{
|
|
344
|
-
const T = mkfix();
|
|
345
|
-
gitInit(T);
|
|
346
|
-
w(T, 'workflow/intents/2026-09-13-s.md', INTENT('s', '状态: done\n级别: L1\n日期: 2026-09-13', '\n## 验收标准(可测试)\n - [ ] 缩进未勾项(此前后静默通过)\n'));
|
|
347
|
-
w(T, 'workflow/plans/2026-09-13-s.md', PLAN('s', '状态: done\n级别: L1'));
|
|
348
|
-
gitCommitAll(T, 'add');
|
|
349
|
-
expectHard('新建 done 勾选项缩进 → hard 验收未对账', T, '验收未对账');
|
|
350
|
-
rmfix(T);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// ---- 场景 20:incident 状态值域外 → warning 带当前值 ----
|
|
354
|
-
{
|
|
355
|
-
const T = mkfix();
|
|
356
|
-
w(T, 'workflow/incidents/2026-09-13-t.md', `---\n状态: 已修复\n级别: L1\n发现: 2026-09-13\n---\n# INCIDENT — t\n\n${三件套(false)}`);
|
|
357
|
-
w(T, 'workflow/plans/2026-09-13-t.md', PLAN('t', '状态: done\n级别: L1'));
|
|
358
|
-
const r = run(T);
|
|
359
|
-
check('incident 状态值域外 → warning 且带当前值', r.status === 0 && /WARN 状态非法.*t\.md.*已修复/.test(outOf(r)), outOf(r));
|
|
360
|
-
rmfix(T);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// ---- 场景 21:incident 缺级别 → warning 明示配对检查已跳过 ----
|
|
364
|
-
{
|
|
365
|
-
const T = mkfix();
|
|
366
|
-
w(T, 'workflow/incidents/2026-09-13-u.md', `---\n状态: open\n发现: 2026-09-13\n---\n# INCIDENT — u\n\n${三件套(false)}`);
|
|
367
|
-
const r = run(T);
|
|
368
|
-
check('incident 缺级别 → warning 明示配对已跳过', r.status === 0 && /WARN 级别缺失.*u\.md.*配对检查已跳过/.test(outOf(r)), outOf(r));
|
|
369
|
-
rmfix(T);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// ---- 场景 22:存量 done 缺验收标准节 → 聚合 warning 不 hard ----
|
|
373
|
-
{
|
|
374
|
-
const T = mkfix();
|
|
375
|
-
w(T, 'workflow/intents/2026-09-01-v.md', INTENT('v', '状态: done\n级别: L1\n日期: 2026-09-01', '\n## 目标\n存量旧格式,缺验收标准节。\n'));
|
|
376
|
-
w(T, 'workflow/plans/2026-09-01-v.md', PLAN('v', '状态: done\n级别: L1'));
|
|
377
|
-
const r = run(T);
|
|
378
|
-
check('存量 done 缺节 → 聚合 warning 不 hard', r.status === 0 && outOf(r).includes('验收对账存量'), outOf(r));
|
|
379
|
-
rmfix(T);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ---- 场景 23/24:git 仓库模式——未跟踪并行半成品不拦 push;提交进 HEAD 后仍 hard ----
|
|
383
|
-
{
|
|
384
|
-
const T = mkfix();
|
|
385
|
-
gitInit(T);
|
|
386
|
-
w(T, 'workflow/intents/2026-09-12-ok2.md', INTENT('ok2', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 场景构造项(证据:fixture)\n'));
|
|
387
|
-
w(T, 'workflow/plans/2026-09-12-ok2.md', PLAN('ok2', '状态: done\n级别: L1'));
|
|
388
|
-
gitCommitAll(T, 'base');
|
|
389
|
-
w(T, 'workflow/intents/2026-09-18-parallel-wip.md', INTENT('并行会话半成品', '状态: draft\n级别: L1\n日期: 2026-09-18'));
|
|
390
|
-
let r = run(T, { git: true });
|
|
391
|
-
check('仓库模式:未跟踪 draft intent(无 plan)不进扫描,不拦 push', r.status === 0 && !outOf(r).includes('parallel-wip'), `exit=${r.status}\n${outOf(r)}`);
|
|
392
|
-
gitCommitAll(T, 'wip');
|
|
393
|
-
r = run(T, { git: true });
|
|
394
|
-
check('仓库模式:已提交的入口缺 plan 断档仍 hard-block', r.status === 1 && /缺 plan.*parallel-wip/.test(outOf(r)), `exit=${r.status}\n${outOf(r)}`);
|
|
395
|
-
rmfix(T);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// ---- 场景 25:检索层——模块合法 + INDEX 一致 → exit 0 无新告警(拷真实生成器+读取器+词表测接线)----
|
|
399
|
-
{
|
|
400
|
-
const T = mkfix();
|
|
401
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
402
|
-
copyScriptClosure(path.join(T, '.agents', 'scripts'), ['gen-workflow-index.mjs']);
|
|
403
|
-
copyAgentsFiles(path.join(T, '.agents'), ['workflow-modules.txt']);
|
|
404
|
-
w(T, 'workflow/intents/2026-09-22-mod-ok.md', INTENT('mod ok', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline', '\n## 验收标准(可测试)\n- [ ] 用例通过\n'));
|
|
405
|
-
w(T, 'workflow/plans/2026-09-22-mod-ok.md', PLAN('mod ok', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
406
|
-
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-index.mjs'], { cwd: T });
|
|
407
|
-
const r = run(T);
|
|
408
|
-
check('检索层:模块合法+INDEX 一致 → exit 0 无新告警',
|
|
409
|
-
r.status === 0 && !outOf(r).includes('WARN 模块元数据') && !outOf(r).includes('WARN 索引漂移'), outOf(r));
|
|
410
|
-
rmfix(T);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// ---- 场景 26:模块枚举非法 + 2026-09-22 起新建缺字段 → WARN 不阻断 ----
|
|
414
|
-
{
|
|
415
|
-
const T = mkfix();
|
|
416
|
-
fs.copyFileSync(path.join(SCRIPT_DIR, '..', 'workflow-modules.txt'), path.join(T, '.agents', 'workflow-modules.txt'));
|
|
417
|
-
w(T, 'workflow/intents/2026-09-22-mod-bad.md', INTENT('mod bad', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: 不存在的模块'));
|
|
418
|
-
w(T, 'workflow/plans/2026-09-22-mod-bad.md', PLAN('mod bad', '状态: approved\n级别: L1'));
|
|
419
|
-
const r = run(T);
|
|
420
|
-
const o = outOf(r);
|
|
421
|
-
check('检索层:模块非法/缺字段 → WARN 且 exit 0',
|
|
422
|
-
r.status === 0 && o.includes('WARN 模块元数据') && o.includes('不在词表') && o.includes('缺「模块:」字段'), o);
|
|
423
|
-
rmfix(T);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ---- 场景 27:INDEX 漂移 → WARN 索引漂移,exit 0(调生成器 --check 口径)----
|
|
427
|
-
{
|
|
428
|
-
const T = mkfix();
|
|
429
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
430
|
-
copyScriptClosure(path.join(T, '.agents', 'scripts'), ['gen-workflow-index.mjs']);
|
|
431
|
-
copyAgentsFiles(path.join(T, '.agents'), ['workflow-modules.txt']);
|
|
432
|
-
w(T, 'workflow/intents/2026-09-22-drift.md', INTENT('drift', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
433
|
-
w(T, 'workflow/plans/2026-09-22-drift.md', PLAN('drift', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
434
|
-
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-index.mjs'], { cwd: T });
|
|
435
|
-
w(T, 'workflow/plans/2026-09-22-drift.md', PLAN('drift', '状态: done\n级别: L1\n模块: pipeline')); // 生成后改状态 → 漂移
|
|
436
|
-
const r = run(T);
|
|
437
|
-
check('检索层:INDEX 漂移 → WARN 且 exit 0', r.status === 0 && outOf(r).includes('WARN 索引漂移'), outOf(r));
|
|
438
|
-
rmfix(T);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// ---- 场景 27b:DASHBOARD 漂移门(2026-10-08 selfmeasure-and-modularize 检查 11 扩覆盖面)----
|
|
442
|
-
// 三态:陈旧盘面 → WARN;盘面缺失(装户从未生成过看板)→ 不报(防常驻噪声);一致 → 不报。
|
|
443
|
-
{
|
|
444
|
-
const mkDashFix = () => {
|
|
445
|
-
const T = mkfix();
|
|
446
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
447
|
-
// 看板/索引生成器的台账读取依赖(ledger-loader 等)由闭包自动带入——此前手写枚举漏拷过,
|
|
448
|
-
// 表现是「import 失败 → 看板静默不生成 → 漂移门恒绿」,即假绿比红危险(2026-10-12 已修,见 fixture-scripts.mjs)
|
|
449
|
-
copyScriptClosure(path.join(T, '.agents', 'scripts'), ['gen-workflow-index.mjs', 'gen-workflow-dashboard.mjs']);
|
|
450
|
-
copyAgentsFiles(path.join(T, '.agents'), ['workflow-modules.txt']);
|
|
451
|
-
w(T, 'workflow/intents/2026-09-22-dash.md', INTENT('dash', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
452
|
-
w(T, 'workflow/plans/2026-09-22-dash.md', PLAN('dash', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
453
|
-
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-index.mjs'], { cwd: T });
|
|
454
|
-
return T;
|
|
455
|
-
};
|
|
456
|
-
// ① DASHBOARD.md 从未生成(装户形态)→ 不得出 WARN
|
|
457
|
-
let T = mkDashFix();
|
|
458
|
-
let r = run(T);
|
|
459
|
-
check('观测面:装户未生成过 DASHBOARD.md → 不出仪表盘漂移 WARN(防常驻噪声)',
|
|
460
|
-
r.status === 0 && !outOf(r).includes('WARN 仪表盘漂移'), outOf(r));
|
|
461
|
-
rmfix(T);
|
|
462
|
-
// ② 生成后改事实 → 漂移 WARN,且 exit 0(advisory 不阻断)
|
|
463
|
-
T = mkDashFix();
|
|
464
|
-
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-dashboard.mjs'], { cwd: T });
|
|
465
|
-
w(T, 'workflow/intents/2026-09-22-dash.md', INTENT('dash', '状态: done\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
466
|
-
r = run(T);
|
|
467
|
-
check('观测面:DASHBOARD 漂移 → WARN 仪表盘漂移 且 exit 0',
|
|
468
|
-
r.status === 0 && outOf(r).includes('WARN 仪表盘漂移') && outOf(r).includes('gen-workflow-dashboard.mjs'), outOf(r));
|
|
469
|
-
rmfix(T);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// ---- 场景 28/29:常驻面预算——合规无告警;AGENTS.md 超限 → WARN 含一进一出 ----
|
|
473
|
-
{
|
|
474
|
-
const T = mkfix();
|
|
475
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
476
|
-
fs.copyFileSync(path.join(SCRIPT_DIR, 'rule-budget.mjs'), path.join(T, '.agents', 'scripts', 'rule-budget.mjs'));
|
|
477
|
-
copyAgentsFiles(path.join(T, '.agents'), ['rule-budgets.txt']);
|
|
478
|
-
// 超限样本自校准:从拷入的预算表读 AGENTS.md 上限(预算可上调,硬编码样本会随上调失效——2026-10-06-v114-backflow-batch)
|
|
479
|
-
const agLimit = Number(fs.readFileSync(path.join(T, '.agents', 'rule-budgets.txt'), 'utf8').split(/\r?\n/).find((l) => l.startsWith('AGENTS.md ')).trim().split(' ').pop());
|
|
480
|
-
w(T, 'AGENTS.md', '# AGENTS 小体积 fixture\n');
|
|
481
|
-
fs.mkdirSync(path.join(T, '.agents', 'commands'), { recursive: true });
|
|
482
|
-
w(T, '.agents/commands/plan.md', '# 小命令\n');
|
|
483
|
-
w(T, 'workflow/intents/2026-09-22-budget-ok.md', INTENT('budget ok', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
484
|
-
w(T, 'workflow/plans/2026-09-22-budget-ok.md', PLAN('budget ok', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
485
|
-
let r = run(T);
|
|
486
|
-
check('常驻面预算:合规 → 无超限告警且 exit 0', r.status === 0 && !outOf(r).includes('WARN 常驻面超限'), outOf(r));
|
|
487
|
-
w(T, 'AGENTS.md', 'x'.repeat(agLimit + 512));
|
|
488
|
-
w(T, 'workflow/intents/2026-09-22-budget-bad.md', INTENT('budget bad', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
489
|
-
w(T, 'workflow/plans/2026-09-22-budget-bad.md', PLAN('budget bad', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
490
|
-
r = run(T);
|
|
491
|
-
const o = outOf(r);
|
|
492
|
-
check('常驻面预算:超限 → WARN(含一进一出提示) 且 exit 0', r.status === 0 && o.includes('WARN 常驻面超限') && o.includes('一进一出'), o);
|
|
493
|
-
rmfix(T);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// ---- 场景 30:rule-budget --staged 硬拦路径(node 直跑——2026-10-13 去 sh 化后无 sh 依赖)----
|
|
497
|
-
{
|
|
498
|
-
const T = mkfix();
|
|
499
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
500
|
-
fs.copyFileSync(path.join(SCRIPT_DIR, 'rule-budget.mjs'), path.join(T, '.agents', 'scripts', 'rule-budget.mjs'));
|
|
501
|
-
copyAgentsFiles(path.join(T, '.agents'), ['rule-budgets.txt']);
|
|
502
|
-
const agLimit = Number(fs.readFileSync(path.join(T, '.agents', 'rule-budgets.txt'), 'utf8').split(/\r?\n/).find((l) => l.startsWith('AGENTS.md ')).trim().split(' ').pop());
|
|
503
|
-
gitInit(T);
|
|
504
|
-
w(T, 'AGENTS.md', 'x'.repeat(agLimit + 512));
|
|
505
|
-
gitRetry(['add', 'AGENTS.md'], T);
|
|
506
|
-
let r = spawnSync(process.execPath, ['.agents/scripts/rule-budget.mjs', '--staged'], { cwd: T, encoding: 'utf8' });
|
|
507
|
-
check('rule-budget --staged:暂存超限 → exit 1(硬拦路径)',
|
|
508
|
-
r.status === 1 && `${r.stdout}${r.stderr}`.includes('常驻面超限:AGENTS.md'), `${r.stdout}${r.stderr}`);
|
|
509
|
-
w(T, 'AGENTS.md', '# 小体积\n');
|
|
510
|
-
gitRetry(['add', 'AGENTS.md'], T);
|
|
511
|
-
r = spawnSync(process.execPath, ['.agents/scripts/rule-budget.mjs', '--staged'], { cwd: T, encoding: 'utf8' });
|
|
512
|
-
check('rule-budget --staged:合规 → exit 0(不误拦)', r.status === 0, `${r.stdout}${r.stderr}`);
|
|
513
|
-
rmfix(T);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// ---- 场景 31/32/33:确认态留痕(检查 14)——无 approved 历史 WARN;先 approved 后 done 无 WARN;非 git 跳过 ----
|
|
517
|
-
{
|
|
518
|
-
const T = mkfix();
|
|
519
|
-
gitInit(T);
|
|
520
|
-
w(T, 'workflow/incidents/2026-09-23-confirm-gate.md', `---\n状态: closed\n级别: L1\n发现: 2026-09-23\n模块: material\n---\n# INCIDENT — confirm gate\n\n${三件套(false)}`);
|
|
521
|
-
w(T, 'workflow/plans/2026-09-23-confirm-gate.md', PLAN('confirm gate', '状态: done\n级别: L1\n模块: material'));
|
|
522
|
-
gitCommitAll(T, 'test');
|
|
523
|
-
let r = run(T);
|
|
524
|
-
check('新 done 无 approved 历史 → WARN 确认态缺失 且 exit 0',
|
|
525
|
-
r.status === 0 && /WARN 确认态缺失.*2026-09-23-confirm-gate\.md/.test(outOf(r)), outOf(r));
|
|
526
|
-
rmfix(T);
|
|
527
|
-
}
|
|
528
|
-
{
|
|
529
|
-
const T = mkfix();
|
|
530
|
-
gitInit(T);
|
|
531
|
-
w(T, 'workflow/incidents/2026-09-23-confirm-ok.md', `---\n状态: closed\n级别: L1\n发现: 2026-09-23\n模块: material\n---\n# INCIDENT — confirm ok\n\n${三件套(false)}`);
|
|
532
|
-
w(T, 'workflow/plans/2026-09-23-confirm-ok.md', PLAN('confirm ok', '状态: approved\n级别: L1\n模块: material'));
|
|
533
|
-
gitCommitAll(T, 'approved');
|
|
534
|
-
w(T, 'workflow/plans/2026-09-23-confirm-ok.md', PLAN('confirm ok', '状态: done\n级别: L1\n模块: material'));
|
|
535
|
-
gitCommitAll(T, 'done');
|
|
536
|
-
const r = run(T);
|
|
537
|
-
check('approved → done 留痕 → 无确认态缺失告警', r.status === 0 && !outOf(r).includes('WARN 确认态缺失'), outOf(r));
|
|
538
|
-
rmfix(T);
|
|
539
|
-
}
|
|
540
|
-
{
|
|
541
|
-
const T = mkfix();
|
|
542
|
-
w(T, 'workflow/incidents/2026-09-23-confirm-nogit.md', `---\n状态: closed\n级别: L1\n发现: 2026-09-23\n模块: material\n---\n# INCIDENT — confirm nogit\n\n${三件套(false)}`);
|
|
543
|
-
w(T, 'workflow/plans/2026-09-23-confirm-nogit.md', PLAN('confirm nogit', '状态: done\n级别: L1\n模块: material'));
|
|
544
|
-
const r = run(T);
|
|
545
|
-
check('非 git fixture → 检查项 14 跳过,不误报', r.status === 0 && !outOf(r).includes('WARN 确认态缺失'), outOf(r));
|
|
546
|
-
rmfix(T);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// ---- 场景 34/35/36:枚举单源——缺文件 fail-loud;缺键 fail-loud;CRLF 容忍 ----
|
|
550
|
-
{
|
|
551
|
-
const T = mkfix();
|
|
552
|
-
fs.rmSync(path.join(T, '.agents', 'workflow-enums.txt'));
|
|
553
|
-
w(T, 'workflow/intents/2026-09-12-ok.md', INTENT('ok', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:全绿)\n'));
|
|
554
|
-
expectHard('枚举单源文件缺失 → exit 1 fail-loud(不静默退化)', T, '枚举单源');
|
|
555
|
-
rmfix(T);
|
|
556
|
-
}
|
|
557
|
-
{
|
|
558
|
-
const T = mkfix();
|
|
559
|
-
const f = path.join(T, '.agents', 'workflow-enums.txt');
|
|
560
|
-
fs.writeFileSync(f, fs.readFileSync(f, 'utf8').replace(/^doc\.status\.confirmed=.*\n/m, ''));
|
|
561
|
-
expectHard('枚举单源缺键 → exit 1 fail-loud', T, '缺键');
|
|
562
|
-
rmfix(T);
|
|
563
|
-
}
|
|
564
|
-
{
|
|
565
|
-
const T = mkfix();
|
|
566
|
-
fs.writeFileSync(path.join(T, '.agents', 'workflow-enums.txt'), ENUMS_FIXTURE.replace(/\n/g, '\r\n'));
|
|
567
|
-
w(T, 'workflow/intents/2026-09-12-ok.md', INTENT('ok', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
568
|
-
w(T, 'workflow/plans/2026-09-12-ok.md', PLAN('ok', '状态: approved\n级别: L1'));
|
|
569
|
-
const r = run(T);
|
|
570
|
-
check('枚举单源 CRLF 行尾 → approved/done 判定不受 \\r 影响', r.status === 0 && !outOf(r).includes('状态未确认'), outOf(r));
|
|
571
|
-
rmfix(T);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// ---- 场景 37/38/39/40:检查 15 确认指纹对账——无指纹拦 / 配对齐过 / 指纹不符拦 / 生效日前豁免 ----
|
|
575
|
-
{
|
|
576
|
-
const T = mkfix();
|
|
577
|
-
w(T, 'workflow/intents/2026-09-27-cg.md', INTENT('cg', '状态: approved\n级别: L1\n日期: 2026-09-27'));
|
|
578
|
-
w(T, 'workflow/plans/2026-09-27-cg.md', PLAN('cg', '状态: draft\n级别: L1'));
|
|
579
|
-
expectHard('检查15:生效日起新档 approved 无指纹无台账 → hard 确认未对账', T, '确认未对账');
|
|
580
|
-
rmfix(T);
|
|
581
|
-
}
|
|
582
|
-
{
|
|
583
|
-
const T = mkfix();
|
|
584
|
-
const i1 = mkConfirmedDoc(T, 'workflow/intents/2026-09-27-ok15.md', '状态: approved\n级别: L1\n日期: 2026-09-27');
|
|
585
|
-
const p1 = mkConfirmedDoc(T, 'workflow/plans/2026-09-27-ok15.md', '状态: approved\n级别: L1');
|
|
586
|
-
writeLedger(T, [
|
|
587
|
-
{ ts: 'T', doc: i1.rel, stage: 'approved', fingerprint: i1.fp, prev: 'draft' },
|
|
588
|
-
{ ts: 'T', doc: p1.rel, stage: 'approved', fingerprint: p1.fp, prev: 'draft' },
|
|
589
|
-
]);
|
|
590
|
-
expectOk('检查15:指纹+台账配对齐 → exit 0', T);
|
|
591
|
-
rmfix(T);
|
|
592
|
-
}
|
|
593
|
-
{
|
|
594
|
-
const T = mkfix();
|
|
595
|
-
const i1 = mkConfirmedDoc(T, 'workflow/intents/2026-09-27-bad15.md', '状态: approved\n级别: L1\n日期: 2026-09-27');
|
|
596
|
-
w(T, 'workflow/plans/2026-09-27-bad15.md', PLAN('bad15', '状态: draft\n级别: L1'));
|
|
597
|
-
writeLedger(T, [{ ts: 'T', doc: i1.rel, stage: 'approved', fingerprint: 'e'.repeat(64), prev: 'draft' }]); // 指纹不符
|
|
598
|
-
expectHard('检查15:指纹与台账不配对 → hard 确认未对账', T, '确认未对账');
|
|
599
|
-
rmfix(T);
|
|
600
|
-
}
|
|
601
|
-
{
|
|
602
|
-
const T = mkfix();
|
|
603
|
-
// 豁免理由(2026-09-28 锚改台账 ts 后):**台账无行** ⇒ 存量未受管,与文档自报日期无关。
|
|
604
|
-
// 本场景构造的正是「日期早 + 无台账」——换锚前后结论一致,但理由已从「日期早于生效日」变为「无台账行」。
|
|
605
|
-
w(T, 'workflow/intents/2026-09-26-old15.md', INTENT('old15', '状态: approved\n级别: L1\n日期: 2026-09-26'));
|
|
606
|
-
w(T, 'workflow/plans/2026-09-26-old15.md', PLAN('old15', '状态: draft\n级别: L1'));
|
|
607
|
-
expectOk('检查15:存量 approved 无台账行 → 豁免 exit 0', T);
|
|
608
|
-
rmfix(T);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// ---- 场景 40:检查 8——证据写在 [x] 续行(仓库通写法「(证据:…)」另起一行)→ 不报缺证据 ----
|
|
612
|
-
{
|
|
613
|
-
const T = mkfix();
|
|
614
|
-
w(T, 'workflow/intents/2026-09-12-ev1.md', INTENT('ev1', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
615
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过\n(证据:commit fixture;套件全绿)\n- [x] 文档补齐\n(证据:AGENTS.md 第 1 行)\n'));
|
|
616
|
-
w(T, 'workflow/plans/2026-09-12-ev1.md', PLAN('ev1', '状态: done\n级别: L1'));
|
|
617
|
-
const r = run(T);
|
|
618
|
-
check('证据在续行(全角冒号)→ 不报验收缺证据',
|
|
619
|
-
r.status === 0 && !outOf(r).includes('验收缺证据'), `exit=${r.status}\n${outOf(r)}`);
|
|
620
|
-
rmfix(T);
|
|
621
|
-
}
|
|
622
|
-
// ---- 场景 41:检查 8——[x] 无证据且续行也无 → 报验收缺证据(hard 拦;回归:不可把豁免放大成漏检)----
|
|
623
|
-
// 换锚后须真 git 仓:否则不可判定 → 走存量口径,「验收缺证据」这条 blocker 也不再产出。
|
|
624
|
-
{
|
|
625
|
-
const T = mkfix();
|
|
626
|
-
gitInit(T);
|
|
627
|
-
w(T, 'workflow/intents/2026-09-12-ev2.md', INTENT('ev2', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
628
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过\n- [x] 文档补齐\n(说明:无证据行)\n'));
|
|
629
|
-
w(T, 'workflow/plans/2026-09-12-ev2.md', PLAN('ev2', '状态: done\n级别: L1'));
|
|
630
|
-
gitCommitAll(T, 'add');
|
|
631
|
-
const r = run(T);
|
|
632
|
-
check('[x] 续行仍无证据 → 报验收缺证据(hard 拦 exit 1)',
|
|
633
|
-
r.status === 1 && outOf(r).includes('验收缺证据'), `exit=${r.status}\n${outOf(r)}`);
|
|
634
|
-
rmfix(T);
|
|
635
|
-
}
|
|
636
|
-
// evidenceFixture:场景 43/44/47 共用脚手架——git 仓 + L1 done 三件(plan「改动方案」节声明 declared)+
|
|
637
|
-
// intent 证据引用唯一内容提交,返回 { T, sha }。createDeclared=false 时声明【从未存在】的文件(唯一内容
|
|
638
|
-
// 提交必然不触及,判定确定——2026-10-04 实测原「二次提交区分触及/不触及」方案在 npm test 环境偶发失败
|
|
639
|
-
// (git 时序敏感),负例改 ghost 构造)
|
|
640
|
-
const evidenceFixture = (slug, declared, createDeclared) => {
|
|
641
|
-
const T = mkfix();
|
|
642
|
-
gitInit(T);
|
|
643
|
-
if (createDeclared) {
|
|
644
|
-
fs.mkdirSync(path.join(T, path.dirname(declared)), { recursive: true });
|
|
645
|
-
w(T, declared, '// fixture\n');
|
|
646
|
-
}
|
|
647
|
-
w(T, `workflow/plans/2026-10-04-${slug}.md`, PLAN(slug, '状态: done\n级别: L1', `\n## 改动方案\n- ${declared}:声明文件${createDeclared ? '' : '(实际不存在,证据必不触及)'}\n`));
|
|
648
|
-
w(T, `workflow/intents/2026-10-04-${slug}.md`, INTENT(slug, '状态: done\n级别: L1\n日期: 2026-10-04',
|
|
649
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:commit PLACEHOLDER)\n'));
|
|
650
|
-
gitCommitAll(T, `feat: ${slug} fixture`);
|
|
651
|
-
const sha = shortSha(T, ['rev-parse', 'HEAD']).slice(0, 7);
|
|
652
|
-
w(T, `workflow/intents/2026-10-04-${slug}.md`, INTENT(slug, '状态: done\n级别: L1\n日期: 2026-10-04',
|
|
653
|
-
`\n## 验收标准(可测试)\n- [x] 用例通过(证据:commit ${sha})\n`));
|
|
654
|
-
gitCommitAll(T, `docs: ${slug} evidence`);
|
|
655
|
-
return { T, sha };
|
|
656
|
-
};
|
|
657
|
-
|
|
658
|
-
// ---- 场景 43:检查 8 证据真相——L1 plan「改动方案」节(fill-plan 输出)+ 证据 SHA 触及声明文件 → 不报「证据无关」(2026-10-04 plan-section-name-evidence 回归)----
|
|
659
|
-
{
|
|
660
|
-
const { T } = evidenceFixture('ev3', 'src/foo.js', true);
|
|
661
|
-
const r = run(T);
|
|
662
|
-
// fixture 不构造确认记录 → done 必触「确认未对账」hard(exit 1 预期);核心断言是证据面不出现「证据无关」
|
|
663
|
-
check('L1 plan「改动方案」节 + 证据触及声明文件 → 不报证据无关',
|
|
664
|
-
!outOf(r).includes('证据无关'), `exit=${r.status}\n${outOf(r)}`);
|
|
665
|
-
rmfix(T);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// ---- 场景 44:检查 8 证据真相——L1 plan「改动方案」节 + 证据 SHA 不触及声明文件 → 仍报「证据无关」(负例,防过度豁免)----
|
|
669
|
-
{
|
|
670
|
-
const { T } = evidenceFixture('ev4', 'src/ghost.js', false);
|
|
671
|
-
const r = run(T);
|
|
672
|
-
check('L1 plan「改动方案」节 + 证据不触及声明文件 → 仍报证据无关(负例)',
|
|
673
|
-
outOf(r).includes('证据无关'), `exit=${r.status}\n${outOf(r)}`);
|
|
674
|
-
rmfix(T);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// ---- 场景 47:检查 8 证据真相——证据 SHA 触及声明文件 → 校验通过 exit 0(2026-10-04 review-fix-batch:
|
|
678
|
-
// 补 spec 2026-10-04-plan-section-name-evidence §49「校验通过(type=sha)」断言强度——场景 43 的 fixture
|
|
679
|
-
// 触「确认未对账」hard 只能间接断言无「证据无关」;本场景沿场景 5x 造法(锚前自报日期豁免存量面)走满
|
|
680
|
-
// type=sha 放行路径:证据为真实 SHA + 声明文件被提交触及 + 提交无执行器标记 → exit 0 只能经 type=sha 达成
|
|
681
|
-
// (text 需无 hex 串 / process 需 runId 标记 / forged·irrelevant 均 exit 1,三条岔路均被构造排除)----
|
|
682
|
-
{
|
|
683
|
-
const T = mkfix();
|
|
684
|
-
gitInit(T);
|
|
685
|
-
w(T, 'workflow/plans/2026-09-26-sh1.md', PLAN('sh1', '状态: done\n级别: L1\n日期: 2026-09-26\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
686
|
-
fs.mkdirSync(path.join(T, 'src'), { recursive: true });
|
|
687
|
-
w(T, 'src/app.mjs', '// fixture\n');
|
|
688
|
-
gitCommitAll(T, 'feat: sh1 实现(无执行器标记)');
|
|
689
|
-
const sha = shortSha(T);
|
|
690
|
-
w(T, 'workflow/intents/2026-09-26-sh1.md', INTENT('sh1', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 用例通过(证据:${sha})\n`));
|
|
691
|
-
expectOk('证据 SHA 触及声明文件(无执行器标记)→ type=sha 校验通过 exit 0', T);
|
|
692
|
-
rmfix(T);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// ---- 场景 42:检查 4——fill-{intent,spec,plan}.mjs 花括号展开,三路皆存在 → 不报引用断档 ----
|
|
696
|
-
{
|
|
697
|
-
const T = mkfix();
|
|
698
|
-
w(T, 'AGENTS.md', '# AGENTS\n\n起草先跑 `node .agents/scripts/fill-{intent,spec,plan}.mjs` 拿结构化草稿。\n');
|
|
699
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
700
|
-
for (const n of ['fill-intent', 'fill-spec', 'fill-plan']) w(T, `.agents/scripts/${n}.mjs`, '// stub\n');
|
|
701
|
-
w(T, 'workflow/intents/2026-09-12-br1.md', INTENT('br1', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
702
|
-
w(T, 'workflow/plans/2026-09-12-br1.md', PLAN('br1', '状态: done\n级别: L1'));
|
|
703
|
-
const r = run(T);
|
|
704
|
-
check('花括号展开三路存在 → 不报引用断档',
|
|
705
|
-
r.status === 0 && !outOf(r).includes('引用断档'), `exit=${r.status}\n${outOf(r)}`);
|
|
706
|
-
rmfix(T);
|
|
707
|
-
}
|
|
708
|
-
// ---- 场景 45(原 43,2026-10-04 review-fix-batch 改号避让证据真相场景重号;旧号无文档按号引用):检查 4——花括号展开三路皆不存在 → 仍报引用断档(带完整展开式文案)----
|
|
709
|
-
{
|
|
710
|
-
const T = mkfix();
|
|
711
|
-
w(T, 'AGENTS.md', '# AGENTS\n\n起草先跑 `node .agents/scripts/fill-{alpha,beta,gamma}.mjs` 拿结构化草稿。\n');
|
|
712
|
-
w(T, 'workflow/intents/2026-09-12-br2.md', INTENT('br2', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
713
|
-
w(T, 'workflow/plans/2026-09-12-br2.md', PLAN('br2', '状态: done\n级别: L1'));
|
|
714
|
-
const r = run(T);
|
|
715
|
-
check('花括号展开三路皆无 → 仍报引用断档',
|
|
716
|
-
r.status === 0 && /引用断档.*fill-\{alpha,beta,gamma\}\.mjs/.test(outOf(r)), `exit=${r.status}\n${outOf(r)}`);
|
|
717
|
-
rmfix(T);
|
|
718
|
-
}
|
|
719
|
-
// ---- 场景 46(原 44,同场景 45 缘由改号):检查 2——<主题> 与 .md 同行 = 命名约定描述 → 不报模板未填 ----
|
|
720
|
-
{
|
|
721
|
-
const T = mkfix();
|
|
722
|
-
w(T, 'workflow/intents/2026-09-01-nm1.md', INTENT('nm1', '状态: done\n级别: L1\n日期: 2026-09-01',
|
|
723
|
-
'\n编排脚本(`.agents/workflows/<主题>.md`):frontmatter + stages 表。\n复制本模板为 YYYY-MM-DD-<主题>.md 后填写。\n'));
|
|
724
|
-
w(T, 'workflow/plans/2026-09-01-nm1.md', PLAN('nm1', '状态: done\n级别: L1'));
|
|
725
|
-
const r = run(T);
|
|
726
|
-
check('<主题>+.md 命名约定行 → 不报模板未填',
|
|
727
|
-
r.status === 0 && !outOf(r).includes('模板未填'), `exit=${r.status}\n${outOf(r)}`);
|
|
728
|
-
rmfix(T);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// ---- 场景 5x/5y/5z:检查 8 证据核验——过程证据形态(2026-10-03 check-evidence-process)----
|
|
732
|
-
// 造法:gitInit(激活证据核验的 rev-parse 路径)+ 锚前自报日期(豁免检查 15 存量面);
|
|
733
|
-
// 证据引用「改动面外文件的提交」:带执行器标记 → process 放行;无标记 → irrelevant 拦;SHA 不存在 → forged 拦。
|
|
734
|
-
{
|
|
735
|
-
const T = mkfix();
|
|
736
|
-
gitInit(T);
|
|
737
|
-
w(T, 'workflow/plans/2026-09-26-pe1.md', PLAN('pe1', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
738
|
-
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
739
|
-
w(T, 'notes/other.md', 'x\n');
|
|
740
|
-
gitCommitAll(T, 'docs(x): 演练穿越(pipeline-run 20260926-0900-pe1-abc1)');
|
|
741
|
-
const sha = shortSha(T);
|
|
742
|
-
w(T, 'workflow/intents/2026-09-26-pe1.md', INTENT('pe1', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
743
|
-
expectOk('过程证据:提交信息带执行器标记(改动面外)→ 放行 exit 0', T);
|
|
744
|
-
rmfix(T);
|
|
745
|
-
}
|
|
746
|
-
{
|
|
747
|
-
const T = mkfix();
|
|
748
|
-
gitInit(T);
|
|
749
|
-
w(T, 'workflow/plans/2026-09-26-pe2.md', PLAN('pe2', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
750
|
-
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
751
|
-
w(T, 'notes/other.md', 'x\n');
|
|
752
|
-
gitCommitAll(T, 'docs(x): 演练穿越(L1,无标记提交)');
|
|
753
|
-
const sha = shortSha(T);
|
|
754
|
-
w(T, 'workflow/intents/2026-09-26-pe2.md', INTENT('pe2', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
755
|
-
expectHard('过程证据:无标记无关提交 → 仍拦 证据无关', T, '证据无关');
|
|
756
|
-
rmfix(T);
|
|
757
|
-
}
|
|
758
|
-
{
|
|
759
|
-
const T = mkfix();
|
|
760
|
-
gitInit(T);
|
|
761
|
-
w(T, 'workflow/plans/2026-09-26-pe3.md', PLAN('pe3', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
762
|
-
w(T, 'workflow/intents/2026-09-26-pe3.md', INTENT('pe3', '状态: done\n级别: L1\n日期: 2026-09-26', '\n## 验收标准(可测试)\n- [x] 演练穿越(证据:deadbee)\n'));
|
|
763
|
-
expectHard('过程证据:SHA 不存在 → forged 仍拦(防伪线不放松)', T, '证据伪造');
|
|
764
|
-
rmfix(T);
|
|
765
|
-
}
|
|
766
|
-
// ---- 场景 5v:git 基础设施异常 fail-loud(2026-10-05-gitout-fail-open)----
|
|
767
|
-
// CHECK_LOOP_GIT 指向不存在路径 → spawnSync 跨平台稳定 r.error(ENOENT)→ 响亮出账恰一条(进程去重)
|
|
768
|
-
// + 证据核验降级(exit 语义不变、无业务裁决误报);正常 git 零出账(防误报负例)
|
|
769
|
-
// 注(L2 复核 P2-2):出账断言不构成 :581 接入 spawnGit 的独立钉死——探测门与 :581 共用去重 flag,
|
|
770
|
-
// 变异(:581 改回裸 spawnSync)三断言仍绿;:581 接线由代码走读 + CI 覆盖,瞬时重试路径静态替身不可测
|
|
771
|
-
{
|
|
772
|
-
const T = mkfix();
|
|
773
|
-
gitInit(T);
|
|
774
|
-
w(T, 'workflow/plans/2026-09-26-pe4.md', PLAN('pe4', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
775
|
-
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
776
|
-
w(T, 'notes/other.md', 'x\n');
|
|
777
|
-
gitCommitAll(T, 'docs(x): 演练穿越(L1,无标记提交)');
|
|
778
|
-
const sha = shortSha(T);
|
|
779
|
-
w(T, 'workflow/intents/2026-09-26-pe4.md', INTENT('pe4', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
780
|
-
const r = run(T, { env: { CHECK_LOOP_GIT: 'definitely-not-git-xyz-2026' } });
|
|
781
|
-
const hits = outOf(r).split('git 探测异常').length - 1;
|
|
782
|
-
check('git 基础设施异常:响亮出账恰一条(进程去重)', hits === 1, `hits=${hits}\n${outOf(r)}`);
|
|
783
|
-
check('git 基础设施异常:持续异常仍 fail-closed(伪造拦不放松,防静默降级)', r.status === 1 && outOf(r).includes('证据伪造'), `exit=${r.status}\n${outOf(r)}`);
|
|
784
|
-
check('git 基础设施异常:基础设施异常不冒充业务裁决(无证据无关误报)', !outOf(r).includes('证据无关'), outOf(r));
|
|
785
|
-
rmfix(T);
|
|
786
|
-
}
|
|
787
|
-
{
|
|
788
|
-
// 纯数字短 sha × 通道坏死(2026-10-07 v1.3.0 Release CI ubuntu 实证):fixture 短 sha 恰全数字
|
|
789
|
-
//(`--short` 7 位 ≈4%/run)时,rev-parse 实证分流在 CHECK_LOOP_GIT 整体坏死下不可能——
|
|
790
|
-
// 纯数字 text 豁免曾把 fail-closed 漏成 exit 0。守卫后:通道坏死(spawn 异常)→ forged 拦;
|
|
791
|
-
// 对照:通道健康时纯数字假 sha → rev-parse 正常回答不存在 → text 豁免语义保持(misfire 防线不回归)。
|
|
792
|
-
const T = mkfix();
|
|
793
|
-
gitInit(T);
|
|
794
|
-
w(T, 'workflow/plans/2026-09-26-pe7.md', PLAN('pe7', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
795
|
-
w(T, 'workflow/intents/2026-09-26-pe7.md', INTENT('pe7', '状态: done\n级别: L1\n日期: 2026-09-26', '\n## 验收标准(可测试)\n- [x] 演练穿越(证据:1234567)\n'));
|
|
796
|
-
gitCommitAll(T, 'docs(x): 纯数字证据通道坏死场景');
|
|
797
|
-
const rDead = run(T, { env: { CHECK_LOOP_GIT: 'definitely-not-git-xyz-2026' } });
|
|
798
|
-
check('git 基础设施异常:纯数字 sha × 通道坏死 → forged 拦不豁免(实证不可能即 fail-closed)',
|
|
799
|
-
rDead.status === 1 && outOf(rDead).includes('证据伪造'), `exit=${rDead.status}\n${outOf(rDead)}`);
|
|
800
|
-
const rAlive = run(T);
|
|
801
|
-
check('git 基础设施异常:纯数字假 sha × 通道健康 → text 豁免语义保持(misfire 防线不回归)',
|
|
802
|
-
rAlive.status === 0 && outOf(rAlive).includes('证据豁免 text'), `exit=${rAlive.status}\n${outOf(rAlive)}`);
|
|
803
|
-
rmfix(T);
|
|
804
|
-
}
|
|
805
|
-
{
|
|
806
|
-
const T = mkfix();
|
|
807
|
-
gitInit(T);
|
|
808
|
-
w(T, 'workflow/plans/2026-09-26-pe5.md', PLAN('pe5', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
809
|
-
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
810
|
-
w(T, 'notes/other.md', 'x\n');
|
|
811
|
-
gitCommitAll(T, 'docs(x): 演练穿越(L1,无标记提交)');
|
|
812
|
-
const sha = shortSha(T);
|
|
813
|
-
w(T, 'workflow/intents/2026-09-26-pe5.md', INTENT('pe5', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
814
|
-
const r = run(T);
|
|
815
|
-
check('git 基础设施异常:正常 git 零出账(防误报负例)', !outOf(r).includes('git 探测异常'), outOf(r));
|
|
816
|
-
check('git 基础设施异常:正常 git 行为不变(无标记无关提交仍拦证据无关)', r.status === 1 && outOf(r).includes('证据无关'), `exit=${r.status}\n${outOf(r)}`);
|
|
817
|
-
rmfix(T);
|
|
818
|
-
}
|
|
819
|
-
// ---- 场景 5u:检查 8 全数字证据串——rev-parse 实证分流(2026-10-05-check8-digit-sha-misfire)----
|
|
820
|
-
// 纯数字证据串曾由 /^\d+$/ 形态守卫抢先 text 豁免(全数字短 SHA≈4.4%/夹具 静默漏检);改为实证分流:
|
|
821
|
-
// 能解析成提交(含同名 ref/tag)→ 走真相核验;解析失败 → 维持 text 豁免。构造性钉子:git tag 数字名,不依赖哈希运气
|
|
822
|
-
{
|
|
823
|
-
const T = mkfix();
|
|
824
|
-
gitInit(T);
|
|
825
|
-
w(T, 'workflow/plans/2026-10-04-ev5.md', PLAN('ev5', '状态: done\n级别: L1', '\n## 改动方案\n- src/ghost.js:声明文件(实际不存在,证据必不触及)\n'));
|
|
826
|
-
w(T, 'workflow/intents/2026-10-04-ev5.md', INTENT('ev5', '状态: done\n级别: L1\n日期: 2026-10-04', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:2620913)\n'));
|
|
827
|
-
gitCommitAll(T, 'feat: ev5 fixture');
|
|
828
|
-
gitRetry(['tag', '2620913', 'HEAD'], T);
|
|
829
|
-
const r = run(T);
|
|
830
|
-
check('全数字证据串可解析成提交(同名 ref)→ 按真相核验报证据无关(数字形态不豁免)',
|
|
831
|
-
outOf(r).includes('证据无关'), `exit=${r.status}\n${outOf(r)}`);
|
|
832
|
-
rmfix(T);
|
|
833
|
-
}
|
|
834
|
-
{
|
|
835
|
-
const T = mkfix();
|
|
836
|
-
gitInit(T);
|
|
837
|
-
w(T, 'workflow/plans/2026-10-04-ev6.md', PLAN('ev6', '状态: done\n级别: L1', '\n## 改动方案\n- src/ghost.js:声明文件(实际不存在,证据必不触及)\n'));
|
|
838
|
-
w(T, 'workflow/intents/2026-10-04-ev6.md', INTENT('ev6', '状态: done\n级别: L1\n日期: 2026-10-04', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:20260926)\n'));
|
|
839
|
-
gitCommitAll(T, 'feat: ev6 fixture');
|
|
840
|
-
const r = run(T);
|
|
841
|
-
check('纯数字时间戳证据(无同名 ref)→ 维持文本豁免不误报',
|
|
842
|
-
!outOf(r).includes('证据无关') && !outOf(r).includes('证据伪造'), `exit=${r.status}\n${outOf(r)}`);
|
|
843
|
-
rmfix(T);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// ---- 场景 45:检查 2——真未填占位(标题 <主题>,无 .md)→ 仍报模板未填 ----
|
|
847
|
-
{
|
|
848
|
-
const T = mkfix();
|
|
849
|
-
w(T, 'workflow/intents/2026-09-01-nm2.md', INTENT('<主题>', '状态: done\n级别: L1\n日期: 2026-09-01', '\n正文无占位。\n'));
|
|
850
|
-
w(T, 'workflow/plans/2026-09-01-nm2.md', PLAN('nm2', '状态: done\n级别: L1'));
|
|
851
|
-
const r = run(T);
|
|
852
|
-
check('标题 <主题>(无 .md)→ 仍报模板未填',
|
|
853
|
-
r.status === 0 && outOf(r).includes('模板未填'), `exit=${r.status}\n${outOf(r)}`);
|
|
854
|
-
rmfix(T);
|
|
855
|
-
}
|
|
856
|
-
// ---- 场景 45b:检查 2——plan 确认节模板样板豁免;真实占位仍报(2026-10-03 plan-confirm-boiler)----
|
|
857
|
-
{
|
|
858
|
-
const T = mkfix();
|
|
859
|
-
w(T, 'workflow/intents/2026-09-01-bo1.md', INTENT('bo1', '状态: done\n级别: L1\n日期: 2026-09-01', '\n## 验收标准(可测试)\n- [x] 用例通过\n'));
|
|
860
|
-
w(T, 'workflow/plans/2026-09-01-bo1.md', PLAN('bo1', '状态: done\n级别: L1', '\n## 确认与复核\n\n- 确认结果:approved(YYYY-MM-DD 用户对话内确认);done(YYYY-MM-DD 关单,随入口文档置终态)\n'));
|
|
861
|
-
const r1 = run(T);
|
|
862
|
-
check('检查2 plan 确认节样板句 → 不报模板未填',
|
|
863
|
-
r1.status === 0 && !outOf(r1).includes('模板未填'), `exit=${r1.status}\n${outOf(r1)}`);
|
|
864
|
-
rmfix(T);
|
|
865
|
-
}
|
|
866
|
-
{
|
|
867
|
-
const T = mkfix();
|
|
868
|
-
w(T, 'workflow/intents/2026-09-01-bo2.md', INTENT('bo2', '状态: done\n级别: L1\n日期: 2026-09-01', '\n## 验收标准(可测试)\n- [x] 用例通过\n'));
|
|
869
|
-
w(T, 'workflow/plans/2026-09-01-bo2.md', PLAN('bo2', '状态: done\n级别: L1', '\n## 确认与复核\n\n- 日期: YYYY-MM-DD\n'));
|
|
870
|
-
const r2 = run(T);
|
|
871
|
-
check('检查2 plan 真实未填占位(日期: YYYY-MM-DD)→ 仍报模板未填',
|
|
872
|
-
r2.status === 0 && outOf(r2).includes('模板未填'), `exit=${r2.status}\n${outOf(r2)}`);
|
|
873
|
-
rmfix(T);
|
|
874
|
-
}
|
|
875
|
-
// ---- 场景 45c:检查 2——日期显示格式字面量豁免;纯占位仍报(2026-10-06 check2-datetime-literal-exempt)----
|
|
876
|
-
// 日期取 2026-09-26(< 确认门 v1 锚 2026-09-27,豁免检查 15 存量面——沿 5x 块先例),避免 hard 干扰本断言
|
|
877
|
-
{
|
|
878
|
-
const T = mkfix();
|
|
879
|
-
w(T, 'workflow/intents/2026-09-26-dt1.md', INTENT('dt1', '状态: done\n级别: L1\n日期: 2026-09-26',
|
|
880
|
-
'\n匹配时间列格式简化为 YYYY-MM-DD HH:mm;同款括号形态(YYYY-MM-DD HH:mm)与秒级 YYYY-MM-DD HH:mm:ss 均为格式描述。\n'));
|
|
881
|
-
w(T, 'workflow/plans/2026-09-26-dt1.md', PLAN('dt1', '状态: done\n级别: L1'));
|
|
882
|
-
const r1 = run(T);
|
|
883
|
-
check('检查2 裸写 YYYY-MM-DD HH:mm(:ss) 显示格式描述 → 不报模板未填',
|
|
884
|
-
r1.status === 0 && !outOf(r1).includes('模板未填'), `exit=${r1.status}\n${outOf(r1)}`);
|
|
885
|
-
rmfix(T);
|
|
886
|
-
}
|
|
887
|
-
{
|
|
888
|
-
const T = mkfix();
|
|
889
|
-
w(T, 'workflow/intents/2026-09-26-dt2.md', INTENT('dt2', '状态: done\n级别: L1\n日期: 2026-09-26',
|
|
890
|
-
'\n匹配时间列格式简化为 YYYY-MM-DD HH:mm(合法格式描述);另有真占位 日期: YYYY-MM-DD 在同文档。\n'));
|
|
891
|
-
w(T, 'workflow/plans/2026-09-26-dt2.md', PLAN('dt2', '状态: done\n级别: L1', '\n- 日期: YYYY-MM-DD\n'));
|
|
892
|
-
const r2 = run(T);
|
|
893
|
-
check('检查2 同文档混真占位(无时间后缀)→ 格式描述豁免不吞真占位,仍报',
|
|
894
|
-
r2.status === 0 && outOf(r2).includes('模板未填'), `exit=${r2.status}\n${outOf(r2)}`);
|
|
895
|
-
rmfix(T);
|
|
896
|
-
}
|
|
897
|
-
// ---- 场景 46:检查 6——薄适配正文含 .agents/roles/<role>.md 引用 → 不报 Adapter 断线 ----
|
|
898
|
-
{
|
|
899
|
-
const T = mkfix();
|
|
900
|
-
w(T, 'workflow/intents/2026-09-12-ad1.md', INTENT('ad1', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
901
|
-
w(T, 'workflow/plans/2026-09-12-ad1.md', PLAN('ad1', '状态: done\n级别: L1'));
|
|
902
|
-
fs.mkdirSync(path.join(T, '.agents', 'roles'), { recursive: true });
|
|
903
|
-
fs.mkdirSync(path.join(T, '.zcode', 'agents'), { recursive: true });
|
|
904
|
-
w(T, '.agents/roles/implementer.md', '# role implementer\n');
|
|
905
|
-
w(T, '.zcode/agents/implementer.md', '---\nname: implementer\n---\n> 契约见 `.agents/roles/implementer.md`。\n');
|
|
906
|
-
const r = run(T);
|
|
907
|
-
check('薄适配含角色引用 → 不报 Adapter 断线',
|
|
908
|
-
r.status === 0 && !outOf(r).includes('Adapter 断线'), `exit=${r.status}\n${outOf(r)}`);
|
|
909
|
-
rmfix(T);
|
|
910
|
-
}
|
|
911
|
-
// ---- 场景 47:检查 4——fill-*.mjs 星号通配,目录内有匹配文件 → 不报引用断档;无匹配 → 仍报 ----
|
|
912
|
-
{
|
|
913
|
-
const T = mkfix();
|
|
914
|
-
w(T, 'AGENTS.md', '# AGENTS\n\n起草先跑 `node .agents/scripts/fill-*.mjs`;孤儿引用 `.agents/scripts/ghost-*.mjs`。\n');
|
|
915
|
-
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
916
|
-
for (const n of ['fill-intent', 'fill-spec', 'fill-plan']) w(T, `.agents/scripts/${n}.mjs`, '// stub\n');
|
|
917
|
-
w(T, 'workflow/intents/2026-09-12-gb1.md', INTENT('gb1', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
918
|
-
w(T, 'workflow/plans/2026-09-12-gb1.md', PLAN('gb1', '状态: done\n级别: L1'));
|
|
919
|
-
const r = run(T);
|
|
920
|
-
check('星号通配有匹配 → 不报;无匹配 → 仍报引用断档',
|
|
921
|
-
r.status === 0 && !/引用断档.*fill-\*/.test(outOf(r)) && /引用断档.*ghost-\*\.mjs/.test(outOf(r)), `exit=${r.status}\n${outOf(r)}`);
|
|
922
|
-
rmfix(T);
|
|
923
|
-
}
|
|
924
|
-
// ---- 场景 48:检查 15——委托代录台账行(source/quote 额外字段)照常配对放行(两形态口径,2026-09-27 confirm-gate-delegated)----
|
|
925
|
-
{
|
|
926
|
-
const T = mkfix();
|
|
927
|
-
const i1 = mkConfirmedDoc(T, 'workflow/intents/2026-09-27-del15.md', '状态: approved\n级别: L1\n日期: 2026-09-27');
|
|
928
|
-
const p1 = mkConfirmedDoc(T, 'workflow/plans/2026-09-27-del15.md', '状态: approved\n级别: L1');
|
|
929
|
-
writeLedger(T, [
|
|
930
|
-
{ ts: 'T', doc: i1.rel, stage: 'approved', fingerprint: i1.fp, prev: 'draft', source: 'chat-delegated', quote: '2选2' },
|
|
931
|
-
{ ts: 'T', doc: p1.rel, stage: 'approved', fingerprint: p1.fp, prev: 'draft', source: 'tty' },
|
|
932
|
-
]);
|
|
933
|
-
expectOk('检查15:委托代录台账行(source+quote)照常配对 → exit 0', T);
|
|
934
|
-
rmfix(T);
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// ---- 场景 49-54:检查 15 done 内容绑定(2026-09-27 audit-gate-hardening;同日 p2-batch 改锚台账 ts + 保分隔符复原)----
|
|
938
|
-
// done 形态复刻真实时序:approved 内容(含 approved 指纹)→ done 指纹在其上计算 → 落 done 文档 + 台账 done 行
|
|
939
|
-
// opt.ts:台账 done 行 ts(默认 2026-09-28T02:00Z——生效锚后);opt.sep:状态行键值分隔符(默认单空格,
|
|
940
|
-
// ' ' 复刻两跳间手工改成双空格的非规范格式)
|
|
941
|
-
{
|
|
942
|
-
const mkDoneFixture = (T, date, tamper, opt = {}) => {
|
|
943
|
-
const sep = opt.sep || ' ';
|
|
944
|
-
const ts = opt.ts || '2026-09-28T02:00:00.000Z';
|
|
945
|
-
const body = `\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n`;
|
|
946
|
-
const pre = `---\n状态:${sep}approved\n级别: L1\n日期: ${date}\n确认指纹: ${'a'.repeat(16)}\n---\n# INTENT — bind\n${body}`;
|
|
947
|
-
const fpDone = computeFingerprint(pre);
|
|
948
|
-
// tamper 只污染 done 落盘文本(不进指纹底稿)——复刻「done 确认后篡改正文」
|
|
949
|
-
const doneText = `---\n状态:${sep}done\n级别: L1\n日期: ${date}\n确认指纹: ${fpDone.slice(0, 16)}\n---\n# INTENT — bind\n${body}${tamper || ''}`;
|
|
950
|
-
w(T, 'workflow/intents/' + date + '-bind.md', doneText);
|
|
951
|
-
w(T, 'workflow/plans/' + date + '-bind.md', PLAN('bind', '状态: draft\n级别: L1'));
|
|
952
|
-
return { rel: 'workflow/intents/' + date + '-bind.md', fp: fpDone, ts };
|
|
953
|
-
};
|
|
954
|
-
{
|
|
955
|
-
const T = mkfix();
|
|
956
|
-
const d1 = mkDoneFixture(T, '2026-09-28');
|
|
957
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }]);
|
|
958
|
-
expectOk('检查15:done 内容与台账一致(复原重算=台账指纹)→ exit 0', T);
|
|
959
|
-
rmfix(T);
|
|
960
|
-
}
|
|
961
|
-
{
|
|
962
|
-
const T = mkfix();
|
|
963
|
-
const d1 = mkDoneFixture(T, '2026-09-28', '确认后被篡改的正文行\n');
|
|
964
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }]);
|
|
965
|
-
expectHard('检查15:done 后正文被篡改 → hard 确认内容漂移', T, '确认内容漂移');
|
|
966
|
-
rmfix(T);
|
|
967
|
-
}
|
|
968
|
-
{
|
|
969
|
-
const T = mkfix();
|
|
970
|
-
// 生效锚 = 台账 ts:文档日期晚、但确认发生在锚前(旧关单顺序时代)→ 豁免绑定
|
|
971
|
-
const d1 = mkDoneFixture(T, '2026-09-28', '锚前确认的文档即便日后改了也不绑\n', { ts: '2026-09-27T10:00:00.000Z' });
|
|
972
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }]);
|
|
973
|
-
expectOk('检查15:台账 ts 早于生效锚(2026-09-28)→ 豁免内容绑定', T);
|
|
974
|
-
rmfix(T);
|
|
975
|
-
}
|
|
976
|
-
{
|
|
977
|
-
const T = mkfix();
|
|
978
|
-
// 锚后确认 + 文档日期早(旧日期文档晚关单)+ 篡改 → 绑定照拦(p2-batch 改锚的核心收益)
|
|
979
|
-
const d1 = mkDoneFixture(T, '2026-09-27', '确认后被篡改的正文行\n');
|
|
980
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }]);
|
|
981
|
-
expectHard('检查15:文档日期早但台账 ts 锚后 + 篡改 → hard 确认内容漂移', T, '确认内容漂移');
|
|
982
|
-
rmfix(T);
|
|
983
|
-
}
|
|
984
|
-
{
|
|
985
|
-
const T = mkfix();
|
|
986
|
-
const d1 = mkDoneFixture(T, '2026-09-28');
|
|
987
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: d1.fp }]); // 无 prev(schema 演进前行)
|
|
988
|
-
const r = run(T);
|
|
989
|
-
check('检查15:台账 done 行缺 prev → 降级 WARN 绑定跳过(exit 0 不拦)',
|
|
990
|
-
r.status === 0 && outOf(r).includes('绑定降级'), `exit=${r.status}\n${outOf(r)}`);
|
|
991
|
-
rmfix(T);
|
|
992
|
-
}
|
|
993
|
-
{
|
|
994
|
-
const T = mkfix();
|
|
995
|
-
// 两跳间状态行被手工改成双空格(非规范分隔符)——保分隔符复原不误伤(p2-batch P2-1)
|
|
996
|
-
const d1 = mkDoneFixture(T, '2026-09-28', '', { sep: ' ' });
|
|
997
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }]);
|
|
998
|
-
expectOk('检查15:状态行双空格(非规范分隔符)绑定仍过——保分隔符复原不误伤', T);
|
|
999
|
-
rmfix(T);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
// ---- 场景 55-56:检查 4 引用扫描收窄活跃态(2026-09-27 audit-gate-hardening)----
|
|
1004
|
-
{
|
|
1005
|
-
const T = mkfix();
|
|
1006
|
-
// 日期取生效日前(2026-09-26):superseded 现入 15 配对集(closing-coverage),无台账行 ⇒ 免配对
|
|
1007
|
-
// (2026-09-28 锚改台账 ts 后,豁免理由 = 无台账行),场景焦点保持在「终态退出引用扫描」本身
|
|
1008
|
-
w(T, 'workflow/intents/2026-09-26-refterm.md', INTENT('refterm', '状态: superseded\n级别: L1\n日期: 2026-09-26', '\n引用 .agents/scripts/ghost-gone.mjs\n'));
|
|
1009
|
-
w(T, 'workflow/plans/2026-09-26-refterm.md', PLAN('refterm', '状态: superseded\n级别: L1'));
|
|
1010
|
-
const r = run(T);
|
|
1011
|
-
check('检查4:终态文档引用断链不再报(历史叙述退出扫描)',
|
|
1012
|
-
r.status === 0 && !outOf(r).includes('引用断档'), `exit=${r.status}\n${outOf(r)}`);
|
|
1013
|
-
rmfix(T);
|
|
1014
|
-
}
|
|
1015
|
-
{
|
|
1016
|
-
const T = mkfix();
|
|
1017
|
-
w(T, 'workflow/intents/2026-09-26-reflive.md', INTENT('reflive', '状态: approved\n级别: L1\n日期: 2026-09-26', '\n引用 .agents/scripts/ghost-gone.mjs\n'));
|
|
1018
|
-
w(T, 'workflow/plans/2026-09-26-reflive.md', PLAN('reflive', '状态: draft\n级别: L1'));
|
|
1019
|
-
const r = run(T);
|
|
1020
|
-
check('检查4:活跃文档引用断链仍报(收窄只放终态)',
|
|
1021
|
-
r.status === 0 && outOf(r).includes('引用断档') && outOf(r).includes('ghost-gone.mjs'), `exit=${r.status}\n${outOf(r)}`);
|
|
1022
|
-
rmfix(T);
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
// ---- 场景 57-60:检查 15 incidents 确认门(2026-09-27 gate-coverage,配对自 2026-09-28 起)----
|
|
1026
|
-
// fixed/closed 须指纹+台账配对;closed 与 done 同口径内容绑定(ts 锚 + 保分隔符);open 起草态不加门
|
|
1027
|
-
{
|
|
1028
|
-
const mkIncFixture = (T, st, tamper, opt = {}) => {
|
|
1029
|
-
const ts = opt.ts || '2026-09-28T02:00:00.000Z';
|
|
1030
|
-
const body = 三件套(false);
|
|
1031
|
-
const pre = `---\n状态: fixed\n级别: L1\n发现: 2026-09-28\n确认指纹: ${'a'.repeat(16)}\n---\n# INCIDENT — inc\n${body}`;
|
|
1032
|
-
const fpClosed = computeFingerprint(pre);
|
|
1033
|
-
// tamper 只污染 closed 落盘文本(不进指纹底稿)——复刻「closed 确认后篡改正文」
|
|
1034
|
-
const closedText = `---\n状态: ${st}\n级别: L1\n发现: 2026-09-28\n确认指纹: ${fpClosed.slice(0, 16)}\n---\n# INCIDENT — inc\n${body}${tamper || ''}`;
|
|
1035
|
-
w(T, 'workflow/incidents/2026-09-28-inc.md', closedText);
|
|
1036
|
-
w(T, 'workflow/plans/2026-09-28-inc.md', PLAN('inc', '状态: draft\n级别: L1'));
|
|
1037
|
-
return { rel: 'workflow/incidents/2026-09-28-inc.md', fp: fpClosed, ts };
|
|
1038
|
-
};
|
|
1039
|
-
{
|
|
1040
|
-
// incident closed(生效日起)无指纹无台账 → hard 确认未对账
|
|
1041
|
-
const T = mkfix();
|
|
1042
|
-
w(T, 'workflow/incidents/2026-09-28-inc15.md', `---\n状态: closed\n级别: L1\n发现: 2026-09-28\n---\n# INCIDENT — inc15\n${三件套(false)}`);
|
|
1043
|
-
w(T, 'workflow/plans/2026-09-28-inc15.md', PLAN('inc15', '状态: draft\n级别: L1'));
|
|
1044
|
-
expectHard('检查15:incident closed(生效日起)无指纹无台账 → hard 确认未对账', T, '确认未对账');
|
|
1045
|
-
rmfix(T);
|
|
1046
|
-
}
|
|
1047
|
-
{
|
|
1048
|
-
const T = mkfix();
|
|
1049
|
-
const i1 = mkIncFixture(T, 'closed');
|
|
1050
|
-
writeLedger(T, [{ ts: i1.ts, doc: i1.rel, stage: 'closed', fingerprint: i1.fp, prev: 'fixed', source: 'tty' }]);
|
|
1051
|
-
expectOk('检查15:incident closed 配对+内容绑定一致 → exit 0', T);
|
|
1052
|
-
rmfix(T);
|
|
1053
|
-
}
|
|
1054
|
-
{
|
|
1055
|
-
const T = mkfix();
|
|
1056
|
-
const i1 = mkIncFixture(T, 'closed', 'closed 确认后被篡改的正文行\n');
|
|
1057
|
-
writeLedger(T, [{ ts: i1.ts, doc: i1.rel, stage: 'closed', fingerprint: i1.fp, prev: 'fixed', source: 'tty' }]);
|
|
1058
|
-
expectHard('检查15:incident closed 后正文被篡改 → hard 确认内容漂移', T, '确认内容漂移');
|
|
1059
|
-
rmfix(T);
|
|
1060
|
-
}
|
|
1061
|
-
{
|
|
1062
|
-
// open 起草态不加门(无指纹无台账 → 不报确认未对账)
|
|
1063
|
-
const T = mkfix();
|
|
1064
|
-
w(T, 'workflow/incidents/2026-09-28-incopen.md', `---\n状态: open\n级别: L1\n发现: 2026-09-28\n---\n# INCIDENT — incopen\n${三件套(false)}`);
|
|
1065
|
-
w(T, 'workflow/plans/2026-09-28-incopen.md', PLAN('incopen', '状态: draft\n级别: L1'));
|
|
1066
|
-
const r = run(T);
|
|
1067
|
-
check('检查15:incident open(起草态)不进确认门 → 不报确认未对账',
|
|
1068
|
-
r.status === 0 && !outOf(r).includes('确认未对账'), `exit=${r.status}\n${outOf(r)}`);
|
|
1069
|
-
rmfix(T);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// ---- 场景 61-63:检查 15 放弃态覆盖(2026-09-27 closing-coverage:superseded/cancelled 配对 + 四终态绑定)----
|
|
1074
|
-
{
|
|
1075
|
-
const mkAbandonFixture = (T, st, tamper) => {
|
|
1076
|
-
const ts = '2026-09-28T02:00:00.000Z';
|
|
1077
|
-
const body = '\n正文行\n';
|
|
1078
|
-
const pre = `---\n状态: approved\n级别: L1\n日期: 2026-09-28\n确认指纹: ${'a'.repeat(16)}\n---\n# INTENT — ab\n${body}`;
|
|
1079
|
-
const fp = computeFingerprint(pre);
|
|
1080
|
-
const after = `---\n状态: ${st}\n级别: L1\n日期: 2026-09-28\n确认指纹: ${fp.slice(0, 16)}\n---\n# INTENT — ab\n${body}${tamper || ''}`;
|
|
1081
|
-
w(T, 'workflow/intents/2026-09-28-ab.md', after);
|
|
1082
|
-
w(T, 'workflow/plans/2026-09-28-ab.md', PLAN('ab', '状态: draft\n级别: L1'));
|
|
1083
|
-
return { rel: 'workflow/intents/2026-09-28-ab.md', fp, ts };
|
|
1084
|
-
};
|
|
1085
|
-
{
|
|
1086
|
-
// superseded(生效日起)无指纹无台账 → hard 确认未对账(手改出账的口子收死)
|
|
1087
|
-
const T = mkfix();
|
|
1088
|
-
w(T, 'workflow/intents/2026-09-28-ab1.md', `---\n状态: superseded\n级别: L1\n日期: 2026-09-28\n---\n# INTENT — ab1\n`);
|
|
1089
|
-
w(T, 'workflow/plans/2026-09-28-ab1.md', PLAN('ab1', '状态: draft\n级别: L1'));
|
|
1090
|
-
expectHard('检查15:superseded(生效日起)无指纹无台账 → hard 确认未对账', T, '确认未对账');
|
|
1091
|
-
rmfix(T);
|
|
1092
|
-
}
|
|
1093
|
-
{
|
|
1094
|
-
// superseded + 配对 + 终态绑定:篡改 → hard 确认内容漂移
|
|
1095
|
-
const T = mkfix();
|
|
1096
|
-
const a1 = mkAbandonFixture(T, 'superseded', 'superseded 确认后被篡改的正文行\n');
|
|
1097
|
-
writeLedger(T, [{ ts: a1.ts, doc: a1.rel, stage: 'superseded', fingerprint: a1.fp, prev: 'approved', source: 'tty' }]);
|
|
1098
|
-
expectHard('检查15:superseded 后正文被篡改 → hard 确认内容漂移(四终态绑定)', T, '确认内容漂移');
|
|
1099
|
-
rmfix(T);
|
|
1100
|
-
}
|
|
1101
|
-
{
|
|
1102
|
-
// cancelled + 配对 + 绑定一致 → 过
|
|
1103
|
-
const T = mkfix();
|
|
1104
|
-
const a1 = mkAbandonFixture(T, 'cancelled');
|
|
1105
|
-
writeLedger(T, [{ ts: a1.ts, doc: a1.rel, stage: 'cancelled', fingerprint: a1.fp, prev: 'approved', source: 'chat-delegated', quote: '不做了' }]);
|
|
1106
|
-
expectOk('检查15:cancelled 配对+绑定一致 → exit 0', T);
|
|
1107
|
-
rmfix(T);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
// ---- 场景 64-66:检查 15 并录批次审计(2026-09-28 batch-ledger-audit 改判据:读 batch/of 事实)----
|
|
1112
|
-
// 判据演进:原「同 quote + 相邻 ts 差 < 2s」反推调用次数(已被实证假阳性+假阴性,整段退役);
|
|
1113
|
-
// 现「台账行按 batch 分组、组内 of>1 且含 delegated 行 → warning」。本块场景随之改判定依据,
|
|
1114
|
-
// **断言强度不放宽、场景不删**(同一意图换了机制):
|
|
1115
|
-
// · 原「同 quote 双份 2s 内」→ 现「同 batch、of=2、delegated」→ 报(机制换、意图同:一次调用落两份)
|
|
1116
|
-
// · 原「异 quote / >2s 不聚组」→ 现「不同 batch(或 of=1)→ 不报」;且**新增反向场景**:同 batch
|
|
1117
|
-
// 但换 quote、或人为延迟跨 2s,仍须被拦——这正是旧判据的盲区(旧判据下会漏报)
|
|
1118
|
-
// · 原「revert-draft 注记行不参与」→ 现由 VALID_STAGES 过滤承接(同意图)
|
|
1119
|
-
{
|
|
1120
|
-
const mk2LedgerDocs = (T) => {
|
|
1121
|
-
// 两份全配对绑定的 done 文档(复用 mkDoneFixture 的构造口径)
|
|
1122
|
-
const mkOne = (slug, date) => {
|
|
1123
|
-
const body = `\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n`;
|
|
1124
|
-
const pre = `---\n状态: approved\n级别: L1\n日期: ${date}\n确认指纹: ${'a'.repeat(16)}\n---\n# INTENT — ${slug}\n${body}`;
|
|
1125
|
-
const fp = computeFingerprint(pre);
|
|
1126
|
-
w(T, `workflow/intents/${date}-${slug}.md`, `---\n状态: done\n级别: L1\n日期: ${date}\n确认指纹: ${fp.slice(0, 16)}\n---\n# INTENT — ${slug}\n${body}`);
|
|
1127
|
-
w(T, `workflow/plans/${date}-${slug}.md`, PLAN(slug, '状态: draft\n级别: L1'));
|
|
1128
|
-
return { rel: `workflow/intents/${date}-${slug}.md`, fp };
|
|
1129
|
-
};
|
|
1130
|
-
return [mkOne('ab1', '2026-09-28'), mkOne('ab2', '2026-09-28')];
|
|
1131
|
-
};
|
|
1132
|
-
{
|
|
1133
|
-
// 同一批(batch 同、of=2)delegated → warning 可见(exit 0 不阻断)——原「同 quote 双份」的新机制等价物
|
|
1134
|
-
const T = mkfix();
|
|
1135
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1136
|
-
writeLedger(T, [
|
|
1137
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '两份一起', batch: 'b1', seq: 1, of: 2 },
|
|
1138
|
-
{ ts: '2026-09-28T02:00:00.100Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '两份一起', batch: 'b1', seq: 2, of: 2 },
|
|
1139
|
-
]);
|
|
1140
|
-
const r = run(T);
|
|
1141
|
-
check('检查15:同 batch 且 of=2(delegated)→ WARN 确认并录(exit 0 审计可见性)',
|
|
1142
|
-
r.status === 0 && outOf(r).includes('确认并录') && outOf(r).includes('2 份'),
|
|
1143
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1144
|
-
rmfix(T);
|
|
1145
|
-
}
|
|
1146
|
-
{
|
|
1147
|
-
// 【核心回归·旧判据盲区】同一批但**换 quote**("甲"/"乙")且 ts 相差 5s:
|
|
1148
|
-
// 旧判据(同 quote + 差 < 2s)→ 两个信号都不命中 → **零告警**(漏报真实并录)
|
|
1149
|
-
// 新判据读 batch/of(与 quote、ts 无关)→ 仍须报。此场景在旧判据下必然变红。
|
|
1150
|
-
const T = mkfix();
|
|
1151
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1152
|
-
writeLedger(T, [
|
|
1153
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '甲', batch: 'b2', seq: 1, of: 2 },
|
|
1154
|
-
{ ts: '2026-09-28T02:00:05.000Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '乙', batch: 'b2', seq: 2, of: 2 },
|
|
1155
|
-
]);
|
|
1156
|
-
const r = run(T);
|
|
1157
|
-
check('检查15:同 batch 但换 quote + 隔 5s → 仍报并录(旧判据盲区,本单核心回归)',
|
|
1158
|
-
r.status === 0 && outOf(r).includes('确认并录') && outOf(r).includes('2 份'),
|
|
1159
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1160
|
-
rmfix(T);
|
|
1161
|
-
}
|
|
1162
|
-
{
|
|
1163
|
-
// of=1(逐件调用)复用**同一句** quote 且 ts 极近 → **不报**。
|
|
1164
|
-
// 这是旧判据的假阳性面:用户两次都说「可以」+ 快速连跑,旧判据必然误报;新判据读 of=1 正确放过。
|
|
1165
|
-
const T = mkfix();
|
|
1166
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1167
|
-
writeLedger(T, [
|
|
1168
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '可以', batch: 'b3', seq: 1, of: 1 },
|
|
1169
|
-
{ ts: '2026-09-28T02:00:00.050Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '可以', batch: 'b4', seq: 1, of: 1 },
|
|
1170
|
-
]);
|
|
1171
|
-
const r = run(T);
|
|
1172
|
-
check('检查15:两次逐件调用(of=1)复用同一句 quote 且 ts 极近 → 不报(修旧判据假阳性)',
|
|
1173
|
-
r.status === 0 && !outOf(r).includes('确认并录'),
|
|
1174
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1175
|
-
rmfix(T);
|
|
1176
|
-
}
|
|
1177
|
-
{
|
|
1178
|
-
// TTY 形态 of>1:用户亲手逐份过目键入(天然逐件),不构成违规 → 不报
|
|
1179
|
-
const T = mkfix();
|
|
1180
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1181
|
-
writeLedger(T, [
|
|
1182
|
-
{ ts: '2026-09-28T03:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty', batch: 't1', seq: 1, of: 2 },
|
|
1183
|
-
{ ts: '2026-09-28T03:00:01.000Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'tty', batch: 't1', seq: 2, of: 2 },
|
|
1184
|
-
]);
|
|
1185
|
-
const r = run(T);
|
|
1186
|
-
check('检查15:TTY 形态 of=2(亲手逐份过目)→ 不报并录',
|
|
1187
|
-
r.status === 0 && !outOf(r).includes('确认并录'),
|
|
1188
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1189
|
-
rmfix(T);
|
|
1190
|
-
}
|
|
1191
|
-
{
|
|
1192
|
-
// 无 batch 字段的历史行(schema 演进前)→ 静默跳过,不报、不降级出账
|
|
1193
|
-
// (沿 audit-gate-hardening P3:无判定依据的行不产出不可消除噪声)
|
|
1194
|
-
const T = mkfix();
|
|
1195
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1196
|
-
writeLedger(T, [
|
|
1197
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '两份一起' },
|
|
1198
|
-
{ ts: '2026-09-28T02:00:01.500Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '两份一起' },
|
|
1199
|
-
]);
|
|
1200
|
-
const r = run(T);
|
|
1201
|
-
check('检查15:无 batch 的历史行 → 静默跳过(不报、不降级出账)',
|
|
1202
|
-
r.status === 0 && !outOf(r).includes('确认并录'),
|
|
1203
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1204
|
-
rmfix(T);
|
|
1205
|
-
}
|
|
1206
|
-
{
|
|
1207
|
-
// 不同 batch 各自 of=1;revert-draft 注记行(stage 非法)不参与 → 一条都不报
|
|
1208
|
-
const T = mkfix();
|
|
1209
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1210
|
-
writeLedger(T, [
|
|
1211
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: 'x', batch: 'c1', seq: 1, of: 1 },
|
|
1212
|
-
{ ts: '2026-09-28T02:00:00.400Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: 'x', batch: 'c2', seq: 1, of: 1 },
|
|
1213
|
-
{ ts: '2026-09-28T02:00:00.900Z', doc: 'workflow/plans/2026-09-28-ghost.md', stage: 'revert-draft', fingerprint: 'n/a', prev: 'approved', source: 'chat-delegated', quote: 'x', batch: 'c3', seq: 1, of: 3 },
|
|
1214
|
-
]);
|
|
1215
|
-
const r = run(T);
|
|
1216
|
-
check('检查15:异 batch 各自 of=1 + revert-draft 注记行 → 不报并录(注记行由 VALID_STAGES 过滤)',
|
|
1217
|
-
r.status === 0 && !outOf(r).includes('确认并录') && !outOf(r).includes('ghost'),
|
|
1218
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1219
|
-
rmfix(T);
|
|
1220
|
-
}
|
|
1221
|
-
{
|
|
1222
|
-
// 【复核 P2-3】组内 `of` 不一致(人为构造)时判据不得因**行序**而改变结论:
|
|
1223
|
-
// 原实现取 rows[0].of —— 首行 of=1、次行 of=2 会漏报;现取组内 some(of>1),两个方向都报。
|
|
1224
|
-
// 正常批次由 confirm-doc 写同一个 of=docs.length,组内恒一致;本用例专门钉住顺序无关性。
|
|
1225
|
-
const T = mkfix();
|
|
1226
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1227
|
-
writeLedger(T, [
|
|
1228
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '甲', batch: 'd1', seq: 1, of: 1 },
|
|
1229
|
-
{ ts: '2026-09-28T02:00:01.000Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '乙', batch: 'd1', seq: 2, of: 2 },
|
|
1230
|
-
]);
|
|
1231
|
-
const r = run(T);
|
|
1232
|
-
check('检查15:组内 of 不一致(首行 of=1 / 次行 of=2)→ 仍报(判据顺序无关,复核 P2-3)',
|
|
1233
|
-
r.status === 0 && outOf(r).includes('确认并录'),
|
|
1234
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1235
|
-
rmfix(T);
|
|
1236
|
-
}
|
|
1237
|
-
{
|
|
1238
|
-
// 反向:首行 of=2 / 次行 of=1 —— 与上条同批、仅行序相反,结论必须一致(都报)
|
|
1239
|
-
const T = mkfix();
|
|
1240
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1241
|
-
writeLedger(T, [
|
|
1242
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '甲', batch: 'd2', seq: 1, of: 2 },
|
|
1243
|
-
{ ts: '2026-09-28T02:00:01.000Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '乙', batch: 'd2', seq: 2, of: 1 },
|
|
1244
|
-
]);
|
|
1245
|
-
const r2 = run(T);
|
|
1246
|
-
check('检查15:组内 of 不一致(首行 of=2 / 次行 of=1)→ 仍报(与上条对称,顺序无关)',
|
|
1247
|
-
r2.status === 0 && outOf(r2).includes('确认并录'),
|
|
1248
|
-
`exit=${r2.status}\n${outOf(r2)}`);
|
|
1249
|
-
rmfix(T);
|
|
1250
|
-
}
|
|
1251
|
-
{
|
|
1252
|
-
// 【复核 P2-3 收口】「可报组与 revert-draft 注记行并存 → 恰报 1 次、注记行不参与」
|
|
1253
|
-
// 上一版场景把两行改成异 batch of=1 后,fixture 里已无可报组,只验证了「无可报组时零报」——
|
|
1254
|
-
// 本用例恢复旧版被覆盖的组合(旧断言为 `length===1 && includes('2 份')`,此处以新机制等价重建)。
|
|
1255
|
-
const T = mkfix();
|
|
1256
|
-
const [d1, d2] = mk2LedgerDocs(T);
|
|
1257
|
-
writeLedger(T, [
|
|
1258
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'chat-delegated', quote: '两份一起', batch: 'p1', seq: 1, of: 2 },
|
|
1259
|
-
{ ts: '2026-09-28T02:00:00.400Z', doc: d2.rel, stage: 'done', fingerprint: d2.fp, prev: 'approved', source: 'chat-delegated', quote: '两份一起', batch: 'p1', seq: 2, of: 2 },
|
|
1260
|
-
// 注记行:与上批**同 batch id 但 stage 非法**——若 VALID_STAGES 过滤失效,它会被并入该批
|
|
1261
|
-
{ ts: '2026-09-28T02:00:00.900Z', doc: 'workflow/plans/2026-09-28-ghost.md', stage: 'revert-draft', fingerprint: 'n/a', prev: 'approved', source: 'chat-delegated', quote: '两份一起', batch: 'p1', seq: 3, of: 3 },
|
|
1262
|
-
]);
|
|
1263
|
-
const r = run(T);
|
|
1264
|
-
check('检查15:可报组与 revert-draft 注记行并存 → 恰报 1 次且注记行不参与(复核 P2-4 补回)',
|
|
1265
|
-
r.status === 0 && (outOf(r).match(/确认并录/g) || []).length === 1
|
|
1266
|
-
&& outOf(r).includes('2 份') && !outOf(r).includes('ghost'),
|
|
1267
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1268
|
-
rmfix(T);
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
// ---- 场景 67-71:检查 15 受管准入两条件取或(2026-09-28 incident confirm-gate-effective-date-anchor)----
|
|
1273
|
-
// 换锚前:受管准入仅「自报日期 ≥ 生效日」——新档把日期写早即整段跳过判定(确认门静默不开)。
|
|
1274
|
-
// 换锚后:受管 = ①台账有合法跳转行 ∨ ②自报日期 ≥ 生效日;两者皆不满足才豁免(存量)。
|
|
1275
|
-
// ①独立于自报日期成立 → 堵死「确认过却写早日期逃掉对账」;②保留 → 兜住「新档完全没跑 confirm-doc」。
|
|
1276
|
-
// 本块自包含构造(不复用他块作用域内的 mkDoneFixture,避免跨块引用)。
|
|
1277
|
-
{
|
|
1278
|
-
const mkAnchorFixture = (T, date, tamper, opt = {}) => {
|
|
1279
|
-
const ts = opt.ts || '2026-09-28T02:00:00.000Z';
|
|
1280
|
-
const body = `\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n`;
|
|
1281
|
-
const pre = `---\n状态: approved\n级别: L1\n日期: ${date}\n确认指纹: ${'a'.repeat(16)}\n---\n# INTENT — anchor\n${body}`;
|
|
1282
|
-
const fp = computeFingerprint(pre);
|
|
1283
|
-
// tamper 只污染 done 落盘文本(不进指纹底稿)——复刻「done 确认后篡改正文」
|
|
1284
|
-
w(T, `workflow/intents/${date}-anchor.md`,
|
|
1285
|
-
`---\n状态: done\n级别: L1\n日期: ${date}\n确认指纹: ${fp.slice(0, 16)}\n---\n# INTENT — anchor\n${body}${tamper || ''}`);
|
|
1286
|
-
w(T, `workflow/plans/${date}-anchor.md`, PLAN('anchor', '状态: draft\n级别: L1'));
|
|
1287
|
-
return { rel: `workflow/intents/${date}-anchor.md`, fp, ts };
|
|
1288
|
-
};
|
|
1289
|
-
|
|
1290
|
-
{
|
|
1291
|
-
// 核心回归(本 incident 的触发条件):自报日期早于生效日,但台账 ts 在生效日之后 → 仍须 hard 拦。
|
|
1292
|
-
// 换锚前此场景被 `d < eff` 跳过判定(静默放行);换锚后按台账事实纳入受管。
|
|
1293
|
-
const T = mkfix();
|
|
1294
|
-
const d1 = mkAnchorFixture(T, '2026-09-20'); // 自报日期 2026-09-20(早于任何生效日)
|
|
1295
|
-
writeLedger(T, [{ ts: d1.ts, doc: d1.rel, stage: 'done', fingerprint: 'e'.repeat(64), prev: 'approved', source: 'tty' }]); // 指纹不符
|
|
1296
|
-
expectHard('检查15:自报日期早但台账 ts 锚后 + 指纹不符 → hard 确认未对账(换锚前被静默跳过)', T, '确认未对账');
|
|
1297
|
-
rmfix(T);
|
|
1298
|
-
}
|
|
1299
|
-
{
|
|
1300
|
-
// 存量豁免须**两条件皆不满足**:日期早(2026-09-26 < 生效日)且台账无行 → 免配对。
|
|
1301
|
-
// 注意「日期新但无台账」**不**豁免(那是新档漏走确认门,见上一条核心回归的反面)——
|
|
1302
|
-
// 两条件取或的语义即:任一条命中即受管,故豁免档必须两条都不命中。
|
|
1303
|
-
const T = mkfix();
|
|
1304
|
-
w(T, 'workflow/intents/2026-09-26-noledge.md', INTENT('noledge', '状态: approved\n级别: L1\n日期: 2026-09-26'));
|
|
1305
|
-
w(T, 'workflow/plans/2026-09-26-noledge.md', PLAN('noledge', '状态: draft\n级别: L1'));
|
|
1306
|
-
expectOk('检查15:日期早于生效日且台账无行 → 存量豁免(两条件皆不满足)', T);
|
|
1307
|
-
rmfix(T);
|
|
1308
|
-
}
|
|
1309
|
-
{
|
|
1310
|
-
// 条件①的**独立判别力**(复核 P2-2 更正):日期早(条件②不命中,本会被日期条件豁免)
|
|
1311
|
-
// + 台账有行但**指纹不匹配** → 条件①独立成立 ⇒ 受管 ⇒ 配对失败 ⇒ hard 拦。
|
|
1312
|
-
// 断言必须用 expectHard 而非 expectOk:旧单条件下该 doc 因日期早被整段跳过(exit 0),
|
|
1313
|
-
// 用 expectOk 会「新旧门都通过」而无法证伪条件①被移除(上一版即此缺陷,属无效测试)。
|
|
1314
|
-
// 现版本在忠实变异(还原旧单条件)下会变红——与核心回归场景同向锁定条件①的独立成立。
|
|
1315
|
-
const T = mkfix();
|
|
1316
|
-
const d1 = mkAnchorFixture(T, '2026-09-20');
|
|
1317
|
-
writeLedger(T, [{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: 'e'.repeat(64), prev: 'approved', source: 'tty' }]); // 指纹不符
|
|
1318
|
-
expectHard('检查15:日期早但台账有行(指纹不匹配)→ 受管并 hard 拦(条件①独立于日期成立)', T, '确认未对账');
|
|
1319
|
-
rmfix(T);
|
|
1320
|
-
}
|
|
1321
|
-
{
|
|
1322
|
-
// 重确认:同一 doc+stage 台账多行 → 取末次 ts 判锚(append-only 末次生效;不能因首行早于锚而误豁免)
|
|
1323
|
-
const T = mkfix();
|
|
1324
|
-
const d1 = mkAnchorFixture(T, '2026-09-28', '确认后被篡改的正文行\n');
|
|
1325
|
-
writeLedger(T, [
|
|
1326
|
-
{ ts: '2026-09-27T10:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }, // 早期行(锚前)
|
|
1327
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'done', fingerprint: d1.fp, prev: 'approved', source: 'tty' }, // 末次行(锚后)
|
|
1328
|
-
]);
|
|
1329
|
-
expectHard('检查15:同 doc+stage 台账多行取末次 ts → 篡改仍 hard 确认内容漂移(重确认场景)', T, '确认内容漂移');
|
|
1330
|
-
rmfix(T);
|
|
1331
|
-
}
|
|
1332
|
-
{
|
|
1333
|
-
// 非合法跳转行(revert-open / revert-draft 类回退注记)**不构成本 incident 的核心命题**——
|
|
1334
|
-
// 为把该属性与日期条件隔离,此处取日期早于生效日(条件②不命中),使受管准入**只**取决于台账条件:
|
|
1335
|
-
// · 若注记行被判为「合法跳转行」→ 台账条件命中 → 受管 → 无指纹 → hard 拦(期望:不拦,故失败)
|
|
1336
|
-
// · 期望:注记行被 VALID_STAGES 过滤 → 台账条件不命中 → 两条件皆不满足 → 存量豁免
|
|
1337
|
-
// 注记行不是「确认确实发生过」的证据,不能被当成台账行把文档拖进受管面
|
|
1338
|
-
// (否则回退操作反而制造 hard 拦——本 incident 自己就吃过这个亏,见 incident 备注「落态注记」)。
|
|
1339
|
-
const T = mkfix();
|
|
1340
|
-
const d1 = mkAnchorFixture(T, '2026-09-26');
|
|
1341
|
-
writeLedger(T, [
|
|
1342
|
-
{ ts: '2026-09-28T02:00:00.000Z', doc: d1.rel, stage: 'revert-open', fingerprint: 'n/a', prev: 'approved', source: 'chat-delegated', quote: '回退注记' },
|
|
1343
|
-
]);
|
|
1344
|
-
expectOk('检查15:仅 revert-open 注记行(非合法跳转 stage)→ 不构成台账条件,日期早 → 存量豁免 exit 0', T);
|
|
1345
|
-
rmfix(T);
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
// ---- 场景 72-74:检查 8 生效日锚改 git 首次加入日期(2026-09-28 check8-git-anchor)----
|
|
1350
|
-
// 换锚前锚取**文件名前 10 字符**——项目命名规范本身即 YYYY-MM-DD-<主题>,作者日常手写该前缀,
|
|
1351
|
-
// 「写早」无需任何额外动作、顺手即发生 → 一条 hard 门可被平凡绕过(有未勾验项却 exit 0)。
|
|
1352
|
-
// 换锚后锚取 git 首次加入日期(**日常不可顺手改动**的事实)→ 关掉「改文件名」零成本通道;
|
|
1353
|
-
// **诚实边界(复核 P1 更正)**:该锚取 `%aI` = author date(作者自报时间戳),`git commit --date=<过去>`
|
|
1354
|
-
// 仍可伪造 → **不宣称「通道关闭」**,仅把伪造成本从「改文件名」抬到「主动伪造时间戳」。
|
|
1355
|
-
{
|
|
1356
|
-
{
|
|
1357
|
-
// 【核心回归】文件名日期写早(2026-09-01 < accCutoff 2026-09-12),但**实际是新建档**
|
|
1358
|
-
// (git 首次加入在生效日之后)→ 仍须 hard 拦。换锚前此场景被 `filedate >= accCutoff` 跳过(静默放行)。
|
|
1359
|
-
const T = mkfix();
|
|
1360
|
-
gitInit(T);
|
|
1361
|
-
w(T, 'workflow/intents/2026-09-01-early.md', INTENT('early', '状态: done\n级别: L1\n日期: 2026-09-01', '\n## 验收标准\n- [ ] 未勾项\n'));
|
|
1362
|
-
w(T, 'workflow/plans/2026-09-01-early.md', PLAN('early', '状态: done\n级别: L1'));
|
|
1363
|
-
gitCommitAll(T, 'add'); // 加入时间 = 今天(≥ accCutoff)→ git 事实:新建档
|
|
1364
|
-
expectHard('检查8:文件名日期写早但 git 首次加入够新 + 未勾验 → hard 拦(换锚前被静默跳过)', T, '验收未对账');
|
|
1365
|
-
rmfix(T);
|
|
1366
|
-
}
|
|
1367
|
-
{
|
|
1368
|
-
// 【退化语义】非 git 仓(fixture 常态)→ 不可判定 → 走存量口径,**不误报 hard**。
|
|
1369
|
-
// 钉住「换锚不引入新的误拦」:不可判定时宁可放过(advisory 层),不放 hard。
|
|
1370
|
-
const T = mkfix();
|
|
1371
|
-
w(T, 'workflow/intents/2026-09-15-nogit.md', INTENT('nogit', '状态: done\n级别: L1\n日期: 2026-09-15', '\n## 验收标准\n- [ ] 未勾项\n'));
|
|
1372
|
-
w(T, 'workflow/plans/2026-09-15-nogit.md', PLAN('nogit', '状态: done\n级别: L1'));
|
|
1373
|
-
const r = run(T);
|
|
1374
|
-
check('检查8:非 git 仓 → 不可判定走存量口径(不误报 hard)',
|
|
1375
|
-
r.status === 0 && !outOf(r).includes('验收未对账'), `exit=${r.status}\n${outOf(r)}`);
|
|
1376
|
-
rmfix(T);
|
|
1377
|
-
}
|
|
1378
|
-
{
|
|
1379
|
-
// 【对照】同一内容、仅把**文件名**改为够新(09-15)→ 两锚下都应 hard 拦。
|
|
1380
|
-
// 与核心回归并置,说明「拦不拦由 git 事实决定,不由文件名字段决定」。
|
|
1381
|
-
const T = mkfix();
|
|
1382
|
-
gitInit(T);
|
|
1383
|
-
w(T, 'workflow/intents/2026-09-15-newer.md', INTENT('newer', '状态: done\n级别: L1\n日期: 2026-09-15', '\n## 验收标准\n- [ ] 未勾项\n'));
|
|
1384
|
-
w(T, 'workflow/plans/2026-09-15-newer.md', PLAN('newer', '状态: done\n级别: L1'));
|
|
1385
|
-
gitCommitAll(T, 'add');
|
|
1386
|
-
expectHard('检查8:文件名日期够新 + git 加入够新 + 未勾验 → hard 拦(对照)', T, '验收未对账');
|
|
1387
|
-
rmfix(T);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
// ---- 场景 75-79:检查 16 量化断言指标签名对账(2026-09-28 claim-exceeds-fix)----
|
|
1392
|
-
// 设计要点:本检查**只查显式签名**(`{{小写.点分}}`),不全文扫数字——理由是本仓「N 行/N 条/N 份」
|
|
1393
|
-
// 类表述多为历史叙述(描述某次提交当时的规模),全文扫描会产生不可消退的假阳性(P3 红线)。
|
|
1394
|
-
// 故本组用例的核心断言是**假阳性恒 0**:无签名时零告警;装户模板占位符(全大写)不误报。
|
|
1395
|
-
{
|
|
1396
|
-
const MC = 'ledger.lines = ledger.lines\ndocs.count.plans = docs.count.plans\n';
|
|
1397
|
-
const seedLedger = (T, lines) => w(T, '.agents/confirmations.jsonl', lines.join('\n') + '\n');
|
|
1398
|
-
// T2 口径适配(2026-09-28 check16-inline-debt):scanFiles16 统一走 docFiles(.md ∧ 数字前缀 ∧ tracked),
|
|
1399
|
-
// 非数字前缀件不再进检查 16 扫描面——原 p1/p2/… 无前缀文件名曾靠「旧扫描面收非前缀件」成立。改为
|
|
1400
|
-
// 数字前缀 + 同名 intent(沿场景 80-90 activePlan 先例:缺 intent 会被「配对断裂」hard-block 干扰断言)。
|
|
1401
|
-
const activePlan16 = (T, name, body) => {
|
|
1402
|
-
w(T, `workflow/intents/2026-09-01-${name}.md`, INTENT(name, '状态: draft\n级别: L1'));
|
|
1403
|
-
w(T, `workflow/plans/2026-09-01-${name}.md`, PLAN(name, '状态: draft\n级别: L1', body));
|
|
1404
|
-
};
|
|
1405
|
-
{
|
|
1406
|
-
// 【核心回归】活跃文档留签名未回填 → warning 且提示实时值
|
|
1407
|
-
const T = mkfix();
|
|
1408
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1409
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}', '{"doc":"b","stage":"done"}', '{"doc":"c","stage":"done"}']);
|
|
1410
|
-
activePlan16(T, 'p1', '\n台账共 {{ledger.lines}} 行\n');
|
|
1411
|
-
const r = run(T);
|
|
1412
|
-
const o = outOf(r);
|
|
1413
|
-
check('检查16:活跃文档留签名未回填 → WARN 且给出实时值 3(advisory 不阻断)',
|
|
1414
|
-
r.status === 0 && o.includes('指标待回填') && o.includes('实时值 = 3'), `exit=${r.status}\n${o}`);
|
|
1415
|
-
rmfix(T);
|
|
1416
|
-
}
|
|
1417
|
-
{
|
|
1418
|
-
// 【假阳性恒 0 · 核心】无签名 → 零告警(历史叙述里的裸数字不得被扫)
|
|
1419
|
-
const T = mkfix();
|
|
1420
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1421
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1422
|
-
activePlan16(T, 'p2', '\n台账共 47 行,其中 13 条并录(历史叙述,非签名)\n');
|
|
1423
|
-
const r = run(T);
|
|
1424
|
-
check('检查16:无签名 → 零告警(裸数字是历史叙述,不得产生假阳性)',
|
|
1425
|
-
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
1426
|
-
rmfix(T);
|
|
1427
|
-
}
|
|
1428
|
-
{
|
|
1429
|
-
// 【假阳性恒 0 · 关键边界】装户模板占位符(全大写 SCREAMING_CASE)不误报
|
|
1430
|
-
// ——不收窄形态会对本仓 plans 内 11 处合法模板占位符全部误报(实测量级)。
|
|
1431
|
-
// 注:占位符**运行时拼装**而非字面写入——本仓的占位符残留门禁(doctor §5)会把字面
|
|
1432
|
-
// 全大写 `{{X}}` 判为残留并改写包源副本,导致本用例在包源侧退化为测「<填写> 不误报」
|
|
1433
|
-
// (仍绿但不再证明 SCREAMING_CASE 被排除)。拼装后两侧语义一致、断言强度不退。
|
|
1434
|
-
const SCREAMING = (n) => '{{' + n + '}}';
|
|
1435
|
-
const T = mkfix();
|
|
1436
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1437
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1438
|
-
activePlan16(T, 'p3',
|
|
1439
|
-
'\n构建 = ' + SCREAMING('BUILD_CMD') + ';端口 ' + SCREAMING('BOARD_PORT') + ';项目 ' + SCREAMING('PROJECT_NAME') + '\n');
|
|
1440
|
-
const r = run(T);
|
|
1441
|
-
check('检查16:装户模板占位符(全大写 SCREAMING_CASE)→ 不误报(形态收窄)',
|
|
1442
|
-
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
1443
|
-
rmfix(T);
|
|
1444
|
-
}
|
|
1445
|
-
{
|
|
1446
|
-
// 【fail-loud】引用未登记指标 → 明示(未登记=无从对账,不静默放过)
|
|
1447
|
-
const T = mkfix();
|
|
1448
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1449
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1450
|
-
activePlan16(T, 'p4', '\n引用 {{nope.metric}}\n');
|
|
1451
|
-
const r = run(T);
|
|
1452
|
-
check('检查16:引用未登记指标 → fail-loud 出账(不静默跳过)',
|
|
1453
|
-
outOf(r).includes('指标未登记'), `exit=${r.status}\n${outOf(r)}`);
|
|
1454
|
-
rmfix(T);
|
|
1455
|
-
}
|
|
1456
|
-
{
|
|
1457
|
-
// 【转义】讲语法 / 举例的行加反斜杠 → 不报(否则「文档教怎么用签名」被误判为「忘了回填」)
|
|
1458
|
-
// 该需求由本检查在自己的 spec 上抓到(实现后立刻 5 处误报)。
|
|
1459
|
-
const T = mkfix();
|
|
1460
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1461
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1462
|
-
activePlan16(T, 'p8', '\n写法示例如 `\\{{ledger.lines}}`(本行是讲语法,非断言)\n');
|
|
1463
|
-
const r = run(T);
|
|
1464
|
-
check('检查16:转义 \\{{...}} 的行为示意 → 不报(区分「讲语法」与「真断言」)',
|
|
1465
|
-
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
1466
|
-
rmfix(T);
|
|
1467
|
-
}
|
|
1468
|
-
{
|
|
1469
|
-
// 【守卫】check-loop 自身可加载(防语法错致整条门禁静默失效)
|
|
1470
|
-
// 实证教训:本次实现期把 `continue` 误用在 forEach 回调内 → 模块 SyntaxError →
|
|
1471
|
-
// check-loop 崩溃退出,而「指标告警 0 条」看起来像通过。**假绿比红更危险**,故钉死。
|
|
1472
|
-
const r = run(mkfix());
|
|
1473
|
-
check('检查16:check-loop 可正常加载运行(防语法错致门禁静默失效——假绿比红危险)',
|
|
1474
|
-
r.status === 0 && !/SyntaxError|Illegal continue/.test(outOf(r)), `exit=${r.status}\n${outOf(r)}`);
|
|
1475
|
-
}
|
|
1476
|
-
{
|
|
1477
|
-
// 【fail-loud】登记了指标但无取数器 → 明示「登记了却没查」(防假阴性:登记表写了却不实现)
|
|
1478
|
-
const T = mkfix();
|
|
1479
|
-
w(T, '.agents/metric-claims.txt', MC + 'ghost.metric = ghost.metric\n');
|
|
1480
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1481
|
-
w(T, 'workflow/plans/p5.md', PLAN('p5', '状态: draft\n级别: L1'));
|
|
1482
|
-
const r = run(T);
|
|
1483
|
-
check('检查16:登记无取数器 → fail-loud(登记了却没实现=其实没查)',
|
|
1484
|
-
outOf(r).includes('无对应取数器'), `exit=${r.status}\n${outOf(r)}`);
|
|
1485
|
-
rmfix(T);
|
|
1486
|
-
}
|
|
1487
|
-
{
|
|
1488
|
-
// 【退化语义】登记表不存在 → 静默跳过(未启用该检查的装户不应被噪声打扰)
|
|
1489
|
-
const T = mkfix();
|
|
1490
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1491
|
-
w(T, 'workflow/plans/p6.md', PLAN('p6', '状态: draft\n级别: L1', '\n台账共 {{ledger.lines}} 行\n'));
|
|
1492
|
-
const r = run(T);
|
|
1493
|
-
check('检查16:登记表缺失 → 静默跳过(不误报,未启用装户零噪声)',
|
|
1494
|
-
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
1495
|
-
rmfix(T);
|
|
1496
|
-
}
|
|
1497
|
-
{
|
|
1498
|
-
// 【活跃态尺度】终态文档不扫(沿检查 4 现有尺度:历史数字是历史叙述)
|
|
1499
|
-
const T = mkfix();
|
|
1500
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1501
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1502
|
-
// 终态件(plan done + 同名 intent,数字前缀使其确在 docFiles 扫描面内——断言才针对「终态豁免」而非「无前缀豁免」)
|
|
1503
|
-
w(T, 'workflow/intents/2026-09-01-p7.md', INTENT('p7', '状态: draft\n级别: L1'));
|
|
1504
|
-
w(T, 'workflow/plans/2026-09-01-p7.md', PLAN('p7', '状态: done\n级别: L1', '\n台账共 {{ledger.lines}} 行\n'));
|
|
1505
|
-
const r = run(T);
|
|
1506
|
-
check('检查16:终态文档不扫(历史叙述豁免,沿检查 4 尺度)',
|
|
1507
|
-
r.status === 0 && !outOf(r).includes('指标待回填'), `exit=${r.status}\n${outOf(r)}`);
|
|
1508
|
-
rmfix(T);
|
|
1509
|
-
}
|
|
1510
|
-
{
|
|
1511
|
-
// 【取数正确性】docs.count.plans 应计活跃+终态全部 plans(排除 _TEMPLATE)
|
|
1512
|
-
// 注:取数器与 docFiles() 同口径——须**以数字开头**(命名规范强制 `YYYY-MM-DD-` 前缀),
|
|
1513
|
-
// 故 fixture 文件名必须合规(首版用 a.md/b.md 使取数为 0,暴露的是用例不合规而非判据错)。
|
|
1514
|
-
const T = mkfix();
|
|
1515
|
-
w(T, '.agents/metric-claims.txt', 'docs.count.plans = docs.count.plans\n');
|
|
1516
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1517
|
-
w(T, 'workflow/plans/2026-09-01-a.md', PLAN('a', '状态: draft\n级别: L1'));
|
|
1518
|
-
w(T, 'workflow/plans/2026-09-02-b.md', PLAN('b', '状态: done\n级别: L1'));
|
|
1519
|
-
w(T, 'workflow/plans/_TEMPLATE.md', PLAN('t', '状态: draft\n级别: L1'));
|
|
1520
|
-
w(T, 'workflow/plans/2026-09-03-c.md', PLAN('c', '状态: draft\n级别: L1', '\n共 {{docs.count.plans}} 份\n'));
|
|
1521
|
-
const r = run(T);
|
|
1522
|
-
check('检查16:取数正确——docs.count.plans 计 3 份(排除 _TEMPLATE,含终态)',
|
|
1523
|
-
outOf(r).includes('实时值 = 3'), `exit=${r.status}\n${outOf(r)}`);
|
|
1524
|
-
rmfix(T);
|
|
1525
|
-
}
|
|
1526
|
-
{
|
|
1527
|
-
// 【复核 P1 回归】转义按「逐个出现」而非整行——同一行的真签名不得被转义位藏住
|
|
1528
|
-
// 复核实证:首版「行内任一 `\{{` 即整行跳过」可被这样绕过:
|
|
1529
|
-
// `… \{{a.b}} 示意… {{ledger.lines}} 未回填` → 整行静默放过(0 告警)。
|
|
1530
|
-
const T = mkfix();
|
|
1531
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1532
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1533
|
-
activePlan16(T, 'p9', '\n讲语法 \\{{a.b}} 示意,但本行也有真断言 {{ledger.lines}} 未回填\n');
|
|
1534
|
-
const r = run(T);
|
|
1535
|
-
check('检查16:同行「转义示意 + 真未回填」→ 真签名仍报(转义按逐个出现,不按整行)',
|
|
1536
|
-
outOf(r).includes('指标待回填'), `exit=${r.status}\n${outOf(r)}`);
|
|
1537
|
-
rmfix(T);
|
|
1538
|
-
}
|
|
1539
|
-
{
|
|
1540
|
-
// 【复核 P2 回归】形态不符的签名不得静默漏过(首版只认严格小写点分 → 笔误零告警)
|
|
1541
|
-
const T = mkfix();
|
|
1542
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1543
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1544
|
-
activePlan16(T, 'p10', '\n引用 {{Ledger.Lines}} 笔误\n');
|
|
1545
|
-
const r = run(T);
|
|
1546
|
-
check('检查16:形态不符(大写点分笔误)→ 出账「指标形态」(不静默漏过)',
|
|
1547
|
-
outOf(r).includes('指标形态'), `exit=${r.status}\n${outOf(r)}`);
|
|
1548
|
-
rmfix(T);
|
|
1549
|
-
}
|
|
1550
|
-
{
|
|
1551
|
-
// 【复核 P2 回归】形态检查不得误伤装户模板占位符(全大写 SCREAMING_CASE 仍豁免)
|
|
1552
|
-
const T = mkfix();
|
|
1553
|
-
w(T, '.agents/metric-claims.txt', MC);
|
|
1554
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1555
|
-
const S = (n) => '{{' + n + '}}';
|
|
1556
|
-
w(T, 'workflow/plans/p11.md', PLAN('p11', '状态: draft\n级别: L1',
|
|
1557
|
-
'\n模板 ' + S('BUILD_CMD') + ' 与 ' + S('BOARD_PORT') + '\n'));
|
|
1558
|
-
const r = run(T);
|
|
1559
|
-
check('检查16:形态检查不误伤装户模板占位符(全大写仍豁免)',
|
|
1560
|
-
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
1561
|
-
rmfix(T);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
// ---- 场景 80-90:检查 16 装户侧 derivers 动态载入(2026-09-28 adopter-derivers)----
|
|
1566
|
-
// 本组核心是**三条假绿防线**:装户模块坏掉时必须响亮出账,绝不静默降级成「只用内置指标」。
|
|
1567
|
-
// 对照:模块**缺失**是正常态(零告警)——两组行为必须可区分,否则装户无法判断自己错在哪。
|
|
1568
|
-
{
|
|
1569
|
-
const MC2 = 'ledger.lines = ledger.lines\nmy.custom = my.custom\n';
|
|
1570
|
-
const MCB = 'ledger.lines = ledger.lines\ndocs.count.plans = docs.count.plans\n'; // 仅内置指标
|
|
1571
|
-
const seedLedger = (T, lines) => w(T, '.agents/confirmations.jsonl', lines.join('\n') + '\n');
|
|
1572
|
-
const activePlan = (T, name, body) => {
|
|
1573
|
-
// 同时写同名 intent:否则「配对断裂」hard-block 会干扰本组断言(本组只测取数器分层)
|
|
1574
|
-
w(T, `workflow/intents/2026-09-01-${name}.md`, INTENT(name, '状态: draft\n级别: L1'));
|
|
1575
|
-
w(T, `workflow/plans/2026-09-01-${name}.md`, PLAN(name, '状态: draft\n级别: L1', body));
|
|
1576
|
-
};
|
|
1577
|
-
{
|
|
1578
|
-
// 【正向】装户模块存在 + 自定义指标 → 取数正确
|
|
1579
|
-
const T = mkfix();
|
|
1580
|
-
w(T, '.agents/metric-claims.txt', MC2);
|
|
1581
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1582
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'my.custom': () => 7 } };\n");
|
|
1583
|
-
activePlan(T, 'ok', '\n值 {{my.custom}}\n');
|
|
1584
|
-
const r = run(T);
|
|
1585
|
-
check('检查16装户:模块存在 + 自定义指标 → 实时值 = 7(取数正确)',
|
|
1586
|
-
outOf(r).includes('my.custom') && outOf(r).includes('实时值 = 7'), `exit=${r.status}\n${outOf(r)}`);
|
|
1587
|
-
rmfix(T);
|
|
1588
|
-
}
|
|
1589
|
-
{
|
|
1590
|
-
// 【假绿防线 ①】模块语法错 → 响亮出账 + check-loop 仍正常退出(不崩)
|
|
1591
|
-
const T = mkfix();
|
|
1592
|
-
w(T, '.agents/metric-claims.txt', MC2);
|
|
1593
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1594
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'my.custom': () => { \n"); // 故意不闭合
|
|
1595
|
-
activePlan(T, 'syn', '\n值 {{my.custom}}\n');
|
|
1596
|
-
const r = run(T);
|
|
1597
|
-
const o = outOf(r);
|
|
1598
|
-
// 断言必须用**唯一的 warning 前缀**匹配——「装户取数器载入失败」这串字也出现在
|
|
1599
|
-
// 「无对应取数器」那条的提示语里(「见上方「…载入失败」条目」),只用它匹配会**在真实条目
|
|
1600
|
-
// 消失时仍然通过**(变异自验实测:静默吞掉 emit 行后该断言不翻红)。故用 `- [WARN 装户取数器载入失败]`。
|
|
1601
|
-
check('检查16装户:模块语法错 → 响亮出账「装户取数器载入失败」(独立成条,明示文件)',
|
|
1602
|
-
o.includes('- [WARN 装户取数器载入失败]') && o.includes('metric-derivers.cjs') && !o.includes('未登记该指标'),
|
|
1603
|
-
`exit=${r.status}\n${o}`);
|
|
1604
|
-
check('检查16装户:模块语法错 → 引擎自身不因装户代码崩溃(无裸 SyntaxError 崩栈)',
|
|
1605
|
-
r.status === 0 && !/Illegal|at ModuleLoader|at compileSourceTextModule/.test(o),
|
|
1606
|
-
`exit=${r.status}\n${o}`);
|
|
1607
|
-
rmfix(T);
|
|
1608
|
-
}
|
|
1609
|
-
{
|
|
1610
|
-
// 【假绿防线 ②】取数器抛异常 → 响亮出账(明示错误)
|
|
1611
|
-
const T = mkfix();
|
|
1612
|
-
w(T, '.agents/metric-claims.txt', MC2);
|
|
1613
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1614
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'my.custom': () => { throw new Error('boom-xyz'); } } };\n");
|
|
1615
|
-
activePlan(T, 'thr', '\n值 {{my.custom}}\n');
|
|
1616
|
-
const r = run(T);
|
|
1617
|
-
check('检查16装户:取数器抛异常 → 响亮出账且明示错误',
|
|
1618
|
-
outOf(r).includes('指标取数失败') && outOf(r).includes('boom-xyz'), `exit=${r.status}\n${outOf(r)}`);
|
|
1619
|
-
rmfix(T);
|
|
1620
|
-
}
|
|
1621
|
-
{
|
|
1622
|
-
// 【假绿防线 ③】返回非有限数字(字符串 / NaN / Promise)→ 拦下并明示类型
|
|
1623
|
-
const T = mkfix();
|
|
1624
|
-
w(T, '.agents/metric-claims.txt', 'my.str = my.str\nmy.nan = my.nan\nmy.prom = my.prom\n');
|
|
1625
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1626
|
-
w(T, '.agents/metric-derivers.cjs', [
|
|
1627
|
-
"module.exports = { derivers: {",
|
|
1628
|
-
" 'my.str': () => 'not-a-number',",
|
|
1629
|
-
" 'my.nan': () => NaN,",
|
|
1630
|
-
" 'my.prom': () => Promise.resolve(1),",
|
|
1631
|
-
"} };",
|
|
1632
|
-
'',
|
|
1633
|
-
].join('\n'));
|
|
1634
|
-
activePlan(T, 'types', '\n{{my.str}} {{my.nan}} {{my.prom}}\n');
|
|
1635
|
-
const r = run(T);
|
|
1636
|
-
const o = outOf(r);
|
|
1637
|
-
check('检查16装户:返回字符串 → 取数失败(明示类型 string)',
|
|
1638
|
-
o.includes('不是有限数字') && o.includes('string'), `exit=${r.status}\n${o}`);
|
|
1639
|
-
check('检查16装户:返回 NaN → 取数失败', o.includes('number(NaN)'), `exit=${r.status}\n${o}`);
|
|
1640
|
-
check('检查16装户:返回 Promise(误写 async)→ 取数失败并提示须同步',
|
|
1641
|
-
o.includes('Promise') && o.includes('同步'), `exit=${r.status}\n${o}`);
|
|
1642
|
-
rmfix(T);
|
|
1643
|
-
}
|
|
1644
|
-
{
|
|
1645
|
-
// 【区分性】模块缺失 → 零告警(与「模块坏」必须行为可区分)
|
|
1646
|
-
const T = mkfix();
|
|
1647
|
-
w(T, '.agents/metric-claims.txt', MCB);
|
|
1648
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1649
|
-
activePlan(T, 'none', '');
|
|
1650
|
-
const r = run(T);
|
|
1651
|
-
check('检查16装户:模块缺失 → 零告警(正常态,与「模块坏」可区分)',
|
|
1652
|
-
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
1653
|
-
rmfix(T);
|
|
1654
|
-
}
|
|
1655
|
-
{
|
|
1656
|
-
// 【优先级】装户定义内置同名 → 内置生效 + advisory 明示被忽略
|
|
1657
|
-
const T = mkfix();
|
|
1658
|
-
w(T, '.agents/metric-claims.txt', 'ledger.lines = ledger.lines\n');
|
|
1659
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}', '{"doc":"b","stage":"done"}']);
|
|
1660
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'ledger.lines': () => 999 } };\n");
|
|
1661
|
-
activePlan(T, 'prio', '\n台账 {{ledger.lines}}\n');
|
|
1662
|
-
const r = run(T);
|
|
1663
|
-
const o = outOf(r);
|
|
1664
|
-
check('检查16装户:内置同名 → 内置值生效(2,非装户的 999)',
|
|
1665
|
-
o.includes('实时值 = 2'), `exit=${r.status}\n${o}`);
|
|
1666
|
-
check('检查16装户:内置同名 → advisory 明示装户定义被忽略',
|
|
1667
|
-
o.includes('被忽略'), `exit=${r.status}\n${o}`);
|
|
1668
|
-
rmfix(T);
|
|
1669
|
-
}
|
|
1670
|
-
{
|
|
1671
|
-
// 【未导出】模块存在但无 derivers 导出 → fail-loud
|
|
1672
|
-
const T = mkfix();
|
|
1673
|
-
w(T, '.agents/metric-claims.txt', MC2);
|
|
1674
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1675
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = {};\n");
|
|
1676
|
-
activePlan(T, 'noexp', '\n{{my.custom}}\n');
|
|
1677
|
-
const r = run(T);
|
|
1678
|
-
check('检查16装户:模块无 derivers 导出 → 响亮出账「载入失败」(独立成条,不静默)',
|
|
1679
|
-
outOf(r).includes('- [WARN 装户取数器载入失败]') && outOf(r).includes('未导出'), `exit=${r.status}\n${outOf(r)}`);
|
|
1680
|
-
rmfix(T);
|
|
1681
|
-
}
|
|
1682
|
-
{
|
|
1683
|
-
// 【旧路径迁移提示】残留首版 .mjs 文件时,须明示「已改用 .cjs」——
|
|
1684
|
-
// 否则装户对着「无取数器」查不出所以然(跨版本实测:require(esm) 在 Node 18/22 不可用,
|
|
1685
|
-
// 故首版 .mjs 契约在 CI 目标版本上根本不工作,必须给出可操作的迁移指引)
|
|
1686
|
-
const T = mkfix();
|
|
1687
|
-
w(T, '.agents/metric-claims.txt', MC2);
|
|
1688
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1689
|
-
w(T, '.agents/metric-derivers.mjs', "export const derivers = { 'my.custom': () => 7 };\n"); // 旧路径
|
|
1690
|
-
activePlan(T, 'legacy', '\n{{my.custom}}\n');
|
|
1691
|
-
const r = run(T);
|
|
1692
|
-
const o = outOf(r);
|
|
1693
|
-
check('检查16装户:残留旧 .mjs → 明示「已改用 .cjs」并给出 CJS 写法',
|
|
1694
|
-
o.includes('装户取数器载入失败') && o.includes('metric-derivers.mjs') && o.includes('.cjs') && o.includes('module.exports'),
|
|
1695
|
-
`exit=${r.status}\n${o}`);
|
|
1696
|
-
rmfix(T);
|
|
1697
|
-
}
|
|
1698
|
-
{
|
|
1699
|
-
// 【未登记指标仍走原路】装户定义了函数但没在 registry 登记 → 报「未登记」
|
|
1700
|
-
const T = mkfix();
|
|
1701
|
-
w(T, '.agents/metric-claims.txt', 'ledger.lines = ledger.lines\n');
|
|
1702
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1703
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'my.custom': () => 7 } };\n");
|
|
1704
|
-
activePlan(T, 'unreg', '\n{{my.custom}}\n');
|
|
1705
|
-
const r = run(T);
|
|
1706
|
-
check('检查16装户:函数有但 registry 未登记 → 报「未登记」(两处都要)',
|
|
1707
|
-
outOf(r).includes('指标未登记'), `exit=${r.status}\n${outOf(r)}`);
|
|
1708
|
-
rmfix(T);
|
|
1709
|
-
}
|
|
1710
|
-
{
|
|
1711
|
-
// 【ctx 能力】glob / read 取数正确
|
|
1712
|
-
const T = mkfix();
|
|
1713
|
-
w(T, '.agents/metric-claims.txt', 'my.sql = my.sql\nmy.read = my.read\n');
|
|
1714
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1715
|
-
fs.mkdirSync(path.join(T, 'backend', 'migrations'), { recursive: true }); // w() 不建父目录
|
|
1716
|
-
w(T, 'backend/migrations/001.sql', 'x');
|
|
1717
|
-
w(T, 'backend/migrations/002.sql', 'x');
|
|
1718
|
-
w(T, 'backend/other.txt', 'x');
|
|
1719
|
-
w(T, '.agents/metric-derivers.cjs', [
|
|
1720
|
-
"module.exports = { derivers: {",
|
|
1721
|
-
" 'my.sql': (ctx) => ctx.glob('backend/migrations/*.sql').length,",
|
|
1722
|
-
" 'my.read': (ctx) => ctx.read('backend/migrations/001.sql').length,",
|
|
1723
|
-
"} };",
|
|
1724
|
-
'',
|
|
1725
|
-
].join('\n'));
|
|
1726
|
-
activePlan(T, 'ctx', '\n{{my.sql}} {{my.read}}\n');
|
|
1727
|
-
const r = run(T);
|
|
1728
|
-
const o = outOf(r);
|
|
1729
|
-
check('检查16装户:ctx.glob 计数正确(2 个 sql)', o.includes('my.sql') && o.includes('实时值 = 2'), `exit=${r.status}\n${o}`);
|
|
1730
|
-
check('检查16装户:ctx.read 读取正确(内容 1 字符)', o.includes('my.read') && o.includes('实时值 = 1'), `exit=${r.status}\n${o}`);
|
|
1731
|
-
rmfix(T);
|
|
1732
|
-
}
|
|
1733
|
-
{
|
|
1734
|
-
// 【存量零差异】无装户模块时,内置指标行为与改前一致
|
|
1735
|
-
const T = mkfix();
|
|
1736
|
-
w(T, '.agents/metric-claims.txt', MCB);
|
|
1737
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}', '{"doc":"b","stage":"done"}']);
|
|
1738
|
-
activePlan(T, 'base', '\n台账 {{ledger.lines}};plans {{docs.count.plans}}\n');
|
|
1739
|
-
const r = run(T);
|
|
1740
|
-
check('检查16装户:无模块时内置指标照常取数(零差异)',
|
|
1741
|
-
outOf(r).includes('实时值 = 2') && outOf(r).includes('实时值 = 1'), `exit=${r.status}\n${outOf(r)}`);
|
|
1742
|
-
rmfix(T);
|
|
1743
|
-
}
|
|
1744
|
-
{
|
|
1745
|
-
// 【ctx.glob 语义】自查修正回归:`**/` 须含零层、可跨段;`*` 不跨 `/`
|
|
1746
|
-
// (首版实现用「先转义后 replace 星号」,实测 `src/**/*.ts` 匹配不到 `src/a/b/c.ts`)
|
|
1747
|
-
const T = mkfix();
|
|
1748
|
-
w(T, '.agents/metric-claims.txt', 'g.deep = g.deep\ng.flat = g.flat\ng.skip = g.skip\n');
|
|
1749
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1750
|
-
fs.mkdirSync(path.join(T, 'src', 'a', 'b'), { recursive: true });
|
|
1751
|
-
fs.mkdirSync(path.join(T, 'src', 'nested'), { recursive: true });
|
|
1752
|
-
w(T, 'src/a/b/c.ts', 'x');
|
|
1753
|
-
w(T, 'src/top.ts', 'x');
|
|
1754
|
-
w(T, 'src/nested/deep.ts', 'x');
|
|
1755
|
-
fs.mkdirSync(path.join(T, 'node_modules', 'pkg'), { recursive: true });
|
|
1756
|
-
w(T, 'node_modules/pkg/dep.ts', 'x');
|
|
1757
|
-
w(T, '.agents/metric-derivers.cjs', [
|
|
1758
|
-
"module.exports = { derivers: {",
|
|
1759
|
-
" 'g.deep': (ctx) => ctx.glob('src/**/*.ts').length,",
|
|
1760
|
-
" 'g.flat': (ctx) => ctx.glob('src/*.ts').length,",
|
|
1761
|
-
" 'g.skip': (ctx) => ctx.glob('**/*.ts').length,",
|
|
1762
|
-
"} };",
|
|
1763
|
-
'',
|
|
1764
|
-
].join('\n'));
|
|
1765
|
-
activePlan(T, 'glob', '\n{{g.deep}} {{g.flat}} {{g.skip}}\n');
|
|
1766
|
-
const r = run(T);
|
|
1767
|
-
const o = outOf(r);
|
|
1768
|
-
check('检查16装户:ctx.glob 的 `**/` 含零层且跨段(src/**/*.ts 计 3)',
|
|
1769
|
-
o.includes('g.deep') && o.includes('实时值 = 3'), `exit=${r.status}\n${o}`);
|
|
1770
|
-
check('检查16装户:ctx.glob 的 `*` 不跨 /(src/*.ts 计 1)',
|
|
1771
|
-
o.includes('g.flat') && o.includes('实时值 = 1'), `exit=${r.status}\n${o}`);
|
|
1772
|
-
check('检查16装户:ctx.glob 剪枝 node_modules(**/*.ts 计 3,非 4)',
|
|
1773
|
-
o.includes('g.skip') && o.includes('实时值 = 3'), `exit=${r.status}\n${o}`);
|
|
1774
|
-
rmfix(T);
|
|
1775
|
-
}
|
|
1776
|
-
{
|
|
1777
|
-
// 【ctx.glob 字面转义】点号须为字面(首版漏转义会误匹配)
|
|
1778
|
-
const T = mkfix();
|
|
1779
|
-
w(T, '.agents/metric-claims.txt', 'g.lit = g.lit\n');
|
|
1780
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1781
|
-
w(T, 'a.b.sql', 'x');
|
|
1782
|
-
w(T, 'axb.sql', 'x');
|
|
1783
|
-
w(T, '.agents/metric-derivers.cjs',
|
|
1784
|
-
"module.exports = { derivers: { 'g.lit': (ctx) => ctx.glob('a.b.sql').length } };\n");
|
|
1785
|
-
activePlan(T, 'lit', '\n{{g.lit}}\n');
|
|
1786
|
-
const r = run(T);
|
|
1787
|
-
check('检查16装户:ctx.glob 点号按字面(a.b.sql 计 1,不含 axb.sql)',
|
|
1788
|
-
outOf(r).includes('实时值 = 1'), `exit=${r.status}\n${outOf(r)}`);
|
|
1789
|
-
rmfix(T);
|
|
1790
|
-
}
|
|
1791
|
-
{
|
|
1792
|
-
// 【复核 P2 回归】模块坏但**没有任何装户指标**时,不得平白出账
|
|
1793
|
-
// (首版无条件报「载入失败」——半成品模块会给清白的装户增加不可消除的噪声)
|
|
1794
|
-
const T = mkfix();
|
|
1795
|
-
w(T, '.agents/metric-claims.txt', MCB); // 只有内置指标
|
|
1796
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1797
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'x.y': () => { \n"); // 坏模块
|
|
1798
|
-
activePlan(T, 'noise', '');
|
|
1799
|
-
const r = run(T);
|
|
1800
|
-
check('检查16装户:坏模块 + 无装户指标 → 不出账(无判定依据不产噪声)',
|
|
1801
|
-
r.status === 0 && !outOf(r).includes('装户取数器载入失败'), `exit=${r.status}\n${outOf(r)}`);
|
|
1802
|
-
rmfix(T);
|
|
1803
|
-
}
|
|
1804
|
-
{
|
|
1805
|
-
// 【复核 P2 回归】内置同名 advisory 按**指标**去重(首版按 registry 行重复出账)
|
|
1806
|
-
const T = mkfix();
|
|
1807
|
-
w(T, '.agents/metric-claims.txt', 'ledger.lines = ledger.lines\nledger.lines = ledger.lines\nledger.lines = ledger.lines\n');
|
|
1808
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1809
|
-
w(T, '.agents/metric-derivers.cjs', "module.exports = { derivers: { 'ledger.lines': () => 999 } };\n");
|
|
1810
|
-
activePlan(T, 'dedup', '\n台账 {{ledger.lines}}\n');
|
|
1811
|
-
const r = run(T);
|
|
1812
|
-
const n = (outOf(r).match(/被忽略/g) || []).length;
|
|
1813
|
-
check('检查16装户:内置同名 advisory 按指标去重(3 行登记 → 恰 1 条)', n === 1, `实际 ${n} 条\n${outOf(r)}`);
|
|
1814
|
-
rmfix(T);
|
|
1815
|
-
}
|
|
1816
|
-
{
|
|
1817
|
-
// 【复核 P2 回归】软链接成环 → 不得无界递归(复核实测首版 depth 128、同文件重复 64 份)
|
|
1818
|
-
const T = mkfix();
|
|
1819
|
-
w(T, '.agents/metric-claims.txt', 'g.loop = g.loop\n');
|
|
1820
|
-
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1821
|
-
fs.mkdirSync(path.join(T, 'loopdir', 'inner'), { recursive: true });
|
|
1822
|
-
w(T, 'loopdir/inner/f.txt', 'x');
|
|
1823
|
-
// 用 **dir 型 symlink**(POSIX 语义的目录软链接),而非 junction(Windows 特有、不具代表性)。
|
|
1824
|
-
// 用户 2026-09-28 追问「这里用到 sh 了吗」促使追验:防环走 fs.realpathSync(Node 原生跨平台),
|
|
1825
|
-
// **与 sh 无关**——故本断言在两种链接语义下都应成立。
|
|
1826
|
-
let linked = false;
|
|
1827
|
-
try {
|
|
1828
|
-
fs.symlinkSync(path.join(T, 'loopdir'), path.join(T, 'loopdir', 'inner', 'back'), 'dir');
|
|
1829
|
-
linked = true;
|
|
1830
|
-
} catch { /* 无建链接权限(未开开发者模式)→ 跳过该断言 */ }
|
|
1831
|
-
w(T, '.agents/metric-derivers.cjs',
|
|
1832
|
-
"module.exports = { derivers: { 'g.loop': (ctx) => ctx.glob('loopdir/**/*.txt').length } };\n");
|
|
1833
|
-
activePlan(T, 'loop', '\n{{g.loop}}\n');
|
|
1834
|
-
const r = run(T);
|
|
1835
|
-
const o16 = outOf(r);
|
|
1836
|
-
check(linked
|
|
1837
|
-
// 防环生效 ⇒ 计数为 1(只有 inner/f.txt);无防环会沿环重复到 64
|
|
1838
|
-
? '检查16装户:dir 型 symlink 成环(POSIX 语义)→ 防环生效,计数恰 1'
|
|
1839
|
-
: '检查16装户:软链接成环用例跳过(本机无建链接权限)',
|
|
1840
|
-
linked ? o16.includes('实时值 = 1') : true, `exit=${r.status}\n${o16}`);
|
|
1841
|
-
rmfix(T);
|
|
1842
|
-
}
|
|
1843
|
-
{
|
|
1844
|
-
// 【归属契约】装户取数模块必须归 owned——否则落回 managed,重演本单要修的缺陷本身
|
|
1845
|
-
// (sync 永久报「本地已改」+ doctor WARN + check-loop 因供应链防线静默停摆)
|
|
1846
|
-
//
|
|
1847
|
-
// **本用例不得依赖包源 `src/`**(复核 P1 更正):首版写 `await import('../../../src/profiles.mjs')`,
|
|
1848
|
-
// 该路径在**本仓**解析到 `<repo>/src/`(存在),但在**装户安装**里 `.agents/scripts/` 只往上三层
|
|
1849
|
-
// 即 `<install>/../src/`——不存在 → `ERR_MODULE_NOT_FOUND` → **整个套件崩掉**。而本测试文件是
|
|
1850
|
-
// managed/shipped,故每个装户拿到的测试套件都是坏的;`npm test` 又只跑 `templates/` 副本
|
|
1851
|
-
// (见 src/run-tests.mjs),所以 CI 看不见。修法:优先动态 import,失败则**降级为读源码断言**
|
|
1852
|
-
// (isOwned 的判定是纯字面量列表,读源即可判,且包源/装副本都在同一路径下)。
|
|
1853
|
-
let isOwned = null;
|
|
1854
|
-
try {
|
|
1855
|
-
({ isOwned } = await import('../../../src/profiles.mjs'));
|
|
1856
|
-
} catch { /* 装户环境无包源 src/ → 走下面的降级断言 */ }
|
|
1857
|
-
if (typeof isOwned === 'function') {
|
|
1858
|
-
check('检查16装户:isOwned(.agents/metric-derivers.cjs) 为真(与登记表同归 owned)',
|
|
1859
|
-
isOwned('.agents/metric-derivers.cjs') === true);
|
|
1860
|
-
check('检查16装户:isOwned(.agents/metric-claims.txt) 仍为真(未被本单破坏)',
|
|
1861
|
-
isOwned('.agents/metric-claims.txt') === true);
|
|
1862
|
-
check('检查16装户:check-loop.mjs 仍归 managed(引擎不被误划入 owned)',
|
|
1863
|
-
isOwned('.agents/scripts/check-loop.mjs') === false);
|
|
1864
|
-
} else {
|
|
1865
|
-
// **装户环境降级断言**(复核 P1 更正):装户没有包源 `src/profiles.mjs`,故改验**可观测事实**——
|
|
1866
|
-
// 若 `metric-derivers.cjs` 已存在于盘上,它必须**不**记在 kit.json 的 managed 侧(在 managed 即为错,
|
|
1867
|
-
// 那正是本单要修的缺陷形态)。未安装该模块时跳过(无判定依据的行不产出噪声)。
|
|
1868
|
-
// 路径:本文件在 `<root>/.agents/scripts/`,故 kit.json 在 `../kit.json`(= `<root>/.agents/kit.json`)。
|
|
1869
|
-
let verdict = null;
|
|
1870
|
-
let detail = '';
|
|
1871
|
-
try {
|
|
1872
|
-
const kit = JSON.parse(fs.readFileSync(new URL('../kit.json', import.meta.url), 'utf8'));
|
|
1873
|
-
const rel = '.agents/metric-derivers.cjs';
|
|
1874
|
-
const inManaged = (kit.managed || []).some((f) => f.rel === rel);
|
|
1875
|
-
const inOwned = (kit.owned || []).some((f) => f.rel === rel);
|
|
1876
|
-
verdict = !inManaged;
|
|
1877
|
-
detail = `managed=${inManaged} owned=${inOwned}`;
|
|
1878
|
-
} catch (e) {
|
|
1879
|
-
verdict = null; detail = String(e.message).slice(0, 60);
|
|
1880
|
-
}
|
|
1881
|
-
if (verdict === null) {
|
|
1882
|
-
check('检查16装户:装户环境无法判定归属(无 kit.json)→ 跳过(不产噪声)', true);
|
|
1883
|
-
} else {
|
|
1884
|
-
check('检查16装户:装户环境——metric-derivers.cjs 不在 managed 侧(不在即正确)',
|
|
1885
|
-
verdict === true, detail);
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
// ---- 场景 91:检查 16 tracked-only 口径双向(2026-09-28 check16-inline-debt T2)----
|
|
1892
|
-
// scanFiles16 与取数器统一走 docFiles(.md ∧ 数字前缀 ∧ tracked):并行会话的未跟踪半成品即便含
|
|
1893
|
-
// 未回填签名也不进扫描面(别人的草稿不拦我的 push,沿检查 1-15 仓库模式「只扫已提交(HEAD)」口径);
|
|
1894
|
-
// 提交进 HEAD 后照常出账。跑法沿场景 23/24 先例({git:true} + cwd 注入真 git 模式)。
|
|
1895
|
-
{
|
|
1896
|
-
const T = mkfix();
|
|
1897
|
-
gitInit(T);
|
|
1898
|
-
w(T, '.agents/metric-claims.txt', 'ledger.lines = ledger.lines\n');
|
|
1899
|
-
w(T, '.agents/confirmations.jsonl', '{"doc":"a","stage":"done"}\n');
|
|
1900
|
-
w(T, 'workflow/intents/2026-09-12-base16.md', INTENT('base16', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
1901
|
-
w(T, 'workflow/plans/2026-09-12-base16.md', PLAN('base16', '状态: done\n级别: L1'));
|
|
1902
|
-
gitCommitAll(T, 'base');
|
|
1903
|
-
// 未跟踪半成品:留签名未回填 → 不出账(tracked-only:不进扫描面)
|
|
1904
|
-
w(T, 'workflow/intents/2026-09-28-wip16.md', INTENT('wip16', '状态: draft\n级别: L1\n日期: 2026-09-28'));
|
|
1905
|
-
w(T, 'workflow/plans/2026-09-28-wip16.md', PLAN('wip16', '状态: draft\n级别: L1', '\n台账共 {{ledger.lines}} 行\n'));
|
|
1906
|
-
// 根级 workflow/*.md 同口径钉住(2026-09-29 复核 P2-1:根级 tracked 过滤曾无测试承重——变异去除后全绿)
|
|
1907
|
-
w(T, 'workflow/root-wip16.md', '---\n状态: draft\n级别: L1\n日期: 2026-09-28\n---\n# ROOT-WIP16\n\n根级草稿台账 {{ledger.lines}} 行\n');
|
|
1908
|
-
let r = run(T, { git: true });
|
|
1909
|
-
check('检查16 tracked-only:未跟踪档含未回填签名 → 不出账(并行半成品不进扫描面;含根级)',
|
|
1910
|
-
r.status === 0 && !outOf(r).includes('指标待回填') && !outOf(r).includes('wip16') && !outOf(r).includes('root-wip16'),
|
|
1911
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1912
|
-
gitCommitAll(T, 'wip');
|
|
1913
|
-
r = run(T, { git: true });
|
|
1914
|
-
check('检查16 tracked-only:提交进 HEAD 后 → 照常出账(WARN 指标待回填;含根级)',
|
|
1915
|
-
r.status === 0 && outOf(r).includes('指标待回填') && outOf(r).includes('wip16') && outOf(r).includes('root-wip16'),
|
|
1916
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
1917
|
-
rmfix(T);
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
// ---- audit 档:false 只留阻断;缺省仍出卫生警告 ----
|
|
1921
|
-
{
|
|
1922
|
-
const T = mkfix();
|
|
1923
|
-
w(T, '.agents/kit.json', '{"audit":false,"policyVersion":1}\n');
|
|
1924
|
-
w(T, 'workflow/intents/2026-09-12-ph.md', INTENT('ph', '状态: draft\n级别: L1\n日期: YYYY-MM-DD'));
|
|
1925
|
-
w(T, 'workflow/plans/2026-09-12-ph.md', PLAN('ph', '状态: draft\n级别: L1'));
|
|
1926
|
-
const r = run(T);
|
|
1927
|
-
check('audit false:占位符不出账且 exit 0', r.status === 0 && !outOf(r).includes('模板未填'), `exit=${r.status}\n${outOf(r)}`);
|
|
1928
|
-
rmfix(T);
|
|
1929
|
-
}
|
|
1930
|
-
{
|
|
1931
|
-
const T = mkfix();
|
|
1932
|
-
w(T, '.agents/kit.json', '{"audit":false,"policyVersion":1}\n');
|
|
1933
|
-
w(T, 'workflow/intents/2026-09-12-np.md', INTENT('np', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1934
|
-
expectHard('audit false:缺 plan 仍阻断', T, '配对断裂');
|
|
1935
|
-
rmfix(T);
|
|
1936
|
-
}
|
|
1937
|
-
{
|
|
1938
|
-
const T = mkfix();
|
|
1939
|
-
w(T, 'workflow/intents/2026-09-12-ph2.md', INTENT('ph2', '状态: draft\n级别: L1\n日期: YYYY-MM-DD'));
|
|
1940
|
-
w(T, 'workflow/plans/2026-09-12-ph2.md', PLAN('ph2', '状态: draft\n级别: L1'));
|
|
1941
|
-
const r = run(T);
|
|
1942
|
-
check('audit 缺省:占位符仍警告', r.status === 0 && outOf(r).includes('模板未填'), `exit=${r.status}\n${outOf(r)}`);
|
|
1943
|
-
rmfix(T);
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
// ---- 检查 17:发版提交树上的 draft 仍 draft 才阻断;发版之后新建的不拦 ----
|
|
1947
|
-
{
|
|
1948
|
-
const T = mkfix();
|
|
1949
|
-
gitInit(T);
|
|
1950
|
-
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
1951
|
-
w(T, 'workflow/intents/2026-09-12-old.md', INTENT('old', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
1952
|
-
w(T, 'workflow/plans/2026-09-12-old.md', PLAN('old', '状态: draft\n级别: L1'));
|
|
1953
|
-
gitCommitAll(T, 'v0.1.0');
|
|
1954
|
-
let r = run(T, { git: true });
|
|
1955
|
-
check('检查17 发版树上的 draft 仍为 draft → hard', r.status === 1 && outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1956
|
-
w(T, 'workflow/intents/2026-09-12-old.md', INTENT('old', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1957
|
-
w(T, 'workflow/plans/2026-09-12-old.md', PLAN('old', '状态: approved\n级别: L1'));
|
|
1958
|
-
w(T, 'package.json', '{"name":"t","version":"0.2.0"}\n');
|
|
1959
|
-
gitCommitAll(T, 'v0.2.0');
|
|
1960
|
-
w(T, 'workflow/intents/2026-09-29-after.md', INTENT('after', '状态: draft\n级别: L1\n日期: 2026-09-29'));
|
|
1961
|
-
w(T, 'workflow/plans/2026-09-29-after.md', PLAN('after', '状态: draft\n级别: L1'));
|
|
1962
|
-
gitCommitAll(T, 'draft after release');
|
|
1963
|
-
r = run(T, { git: true });
|
|
1964
|
-
check('检查17 发版之后新建的 draft 不拦', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1965
|
-
rmfix(T);
|
|
1966
|
-
}
|
|
1967
|
-
{
|
|
1968
|
-
const T = mkfix();
|
|
1969
|
-
gitInit(T);
|
|
1970
|
-
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
1971
|
-
w(T, 'workflow/incidents/2026-09-12-inc17.md', `---\n状态: open\n级别: L1\n发现: 2026-09-12\n模块: pipeline\n---\n# INCIDENT — inc17\n\n${三件套(false)}`);
|
|
1972
|
-
w(T, 'workflow/plans/2026-09-12-inc17.md', PLAN('inc17', '状态: draft\n级别: L1\n模块: pipeline'));
|
|
1973
|
-
gitCommitAll(T, 'v0.1.0');
|
|
1974
|
-
const r = run(T, { git: true });
|
|
1975
|
-
check('检查17 发版树上的 open incident 不参与(阻断由 plan 触发且不含 incident 路径)', r.status === 1 && outOf(r).includes('发版草稿') && !outOf(r).includes('workflow/incidents/2026-09-12-inc17.md'), `exit=${r.status}\n${outOf(r)}`);
|
|
1976
|
-
rmfix(T);
|
|
1977
|
-
}
|
|
1978
|
-
{
|
|
1979
|
-
const T = mkfix();
|
|
1980
|
-
gitInit(T);
|
|
1981
|
-
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
1982
|
-
w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
1983
|
-
w(T, 'workflow/plans/2026-09-12-deep.md', PLAN('deep', '状态: draft\n级别: L1'));
|
|
1984
|
-
gitCommitAll(T, 'v0.1.0');
|
|
1985
|
-
for (let i = 1; i <= 41; i++) {
|
|
1986
|
-
w(T, 'package.json', `{"name":"t","version":"0.1.0","n":${i}}\n`);
|
|
1987
|
-
gitCommitAll(T, `touch ${i}`);
|
|
1988
|
-
}
|
|
1989
|
-
const r = run(T, { git: true });
|
|
1990
|
-
check('检查17 版本变更早于 40 次 package.json 触碰仍阻断', r.status === 1 && outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1991
|
-
rmfix(T);
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
// ---- 检查 17:approved 锚在 policy 版本 2;draft 改为 approved 不看锚 ----
|
|
1995
|
-
{
|
|
1996
|
-
const sameKeys = ['moduleSince', 'confirmDocsEffective', 'confirmIncidentsEffective', 'bindingTs'];
|
|
1997
|
-
check('policy 版本 2 的四个共享日期与版本 1 相同',
|
|
1998
|
-
sameKeys.every((k) => POLICIES[1][k] === POLICIES[2][k]));
|
|
1999
|
-
check('policy 版本 2 check14Since 后移至 2026-09-26(确认门实际上线日)',
|
|
2000
|
-
POLICIES[2].check14Since === '2026-09-26' && POLICIES[1].check14Since === '2026-09-23');
|
|
2001
|
-
check('policy 版本 2 新增 check17UnclosedAfter,版本 1 无此键',
|
|
2002
|
-
POLICIES[2].check17UnclosedAfter === '0.8.0' && POLICIES[1].check17UnclosedAfter === undefined);
|
|
2003
|
-
const T = mkfix();
|
|
2004
|
-
w(T, '.agents/kit.json', '{"policyVersion":99}\n');
|
|
2005
|
-
const p = loadKitPolicy(T);
|
|
2006
|
-
check('未知 policyVersion 回退版本 1', p.policyVersion === 1 && p.check17UnclosedAfter === undefined);
|
|
2007
|
-
rmfix(T);
|
|
2008
|
-
}
|
|
2009
|
-
{
|
|
2010
|
-
const T = mkfix();
|
|
2011
|
-
gitInit(T);
|
|
2012
|
-
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
2013
|
-
w(T, 'workflow/intents/2026-09-12-move.md', INTENT('move', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
2014
|
-
w(T, 'workflow/plans/2026-09-12-move.md', PLAN('move', '状态: draft\n级别: L1'));
|
|
2015
|
-
gitCommitAll(T, 'v0.1.0');
|
|
2016
|
-
w(T, 'workflow/intents/2026-09-12-move.md', INTENT('move', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2017
|
-
w(T, 'workflow/plans/2026-09-12-move.md', PLAN('move', '状态: approved\n级别: L1'));
|
|
2018
|
-
gitCommitAll(T, 'leave draft');
|
|
2019
|
-
const r = run(T, { git: true });
|
|
2020
|
-
check('检查17 发版树上是 draft、现在是 approved → hard',
|
|
2021
|
-
r.status === 1 && outOf(r).includes('发版草稿') && outOf(r).includes('已是 draft') && outOf(r).includes('仍是 approved'),
|
|
2022
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2023
|
-
rmfix(T);
|
|
2024
|
-
}
|
|
2025
|
-
{
|
|
2026
|
-
const doneBody = '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n';
|
|
2027
|
-
const T = mkfix();
|
|
2028
|
-
gitInit(T);
|
|
2029
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2030
|
-
w(T, 'package.json', '{"name":"t","version":"0.8.0"}\n');
|
|
2031
|
-
gitCommitAll(T, 'v0.8.0');
|
|
2032
|
-
w(T, 'package.json', '{"name":"t","version":"0.9.0"}\n');
|
|
2033
|
-
w(T, 'workflow/intents/2026-09-12-stay.md', INTENT('stay', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2034
|
-
w(T, 'workflow/plans/2026-09-12-stay.md', PLAN('stay', '状态: approved\n级别: L1'));
|
|
2035
|
-
gitCommitAll(T, 'v0.9.0');
|
|
2036
|
-
let r = run(T, { git: true });
|
|
2037
|
-
check('检查17 锚之后 approved 仍是 approved → hard',
|
|
2038
|
-
r.status === 1 && outOf(r).includes('发版草稿') && outOf(r).includes('version=0.9.0'),
|
|
2039
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2040
|
-
w(T, 'workflow/intents/2026-09-12-stay.md', INTENT('stay', '状态: done\n级别: L1\n日期: 2026-09-12', doneBody));
|
|
2041
|
-
w(T, 'workflow/plans/2026-09-12-stay.md', PLAN('stay', '状态: done\n级别: L1'));
|
|
2042
|
-
gitCommitAll(T, 'close');
|
|
2043
|
-
r = run(T, { git: true });
|
|
2044
|
-
check('检查17 同一文件已是 done → 不拦', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
2045
|
-
rmfix(T);
|
|
2046
|
-
}
|
|
2047
|
-
{
|
|
2048
|
-
const T = mkfix();
|
|
2049
|
-
gitInit(T);
|
|
2050
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2051
|
-
w(T, 'package.json', '{"name":"t","version":"0.8.0"}\n');
|
|
2052
|
-
w(T, 'workflow/intents/2026-09-12-oldrel.md', INTENT('oldrel', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2053
|
-
w(T, 'workflow/plans/2026-09-12-oldrel.md', PLAN('oldrel', '状态: approved\n级别: L1'));
|
|
2054
|
-
gitCommitAll(T, 'v0.8.0');
|
|
2055
|
-
const r = run(T, { git: true });
|
|
2056
|
-
check('检查17 锚之前 approved 仍是 approved → 不拦', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
2057
|
-
rmfix(T);
|
|
2058
|
-
}
|
|
2059
|
-
{
|
|
2060
|
-
const T = mkfix();
|
|
2061
|
-
gitInit(T);
|
|
2062
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2063
|
-
w(T, 'package.json', '{"name":"t","version":"0.10.0"}\n');
|
|
2064
|
-
w(T, 'workflow/intents/2026-09-12-ten.md', INTENT('ten', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2065
|
-
w(T, 'workflow/plans/2026-09-12-ten.md', PLAN('ten', '状态: approved\n级别: L1'));
|
|
2066
|
-
gitCommitAll(T, 'v0.10.0');
|
|
2067
|
-
const r = run(T, { git: true });
|
|
2068
|
-
check('检查17 0.10.0 按整数比较大于 0.8.0 → hard',
|
|
2069
|
-
r.status === 1 && outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
2070
|
-
rmfix(T);
|
|
2071
|
-
}
|
|
2072
|
-
{
|
|
2073
|
-
const T = mkfix();
|
|
2074
|
-
gitInit(T);
|
|
2075
|
-
w(T, '.agents/kit.json', '{"policyVersion":1}\n');
|
|
2076
|
-
w(T, 'package.json', '{"name":"t","version":"0.9.0"}\n');
|
|
2077
|
-
w(T, 'workflow/intents/2026-09-12-v1.md', INTENT('v1', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2078
|
-
w(T, 'workflow/plans/2026-09-12-v1.md', PLAN('v1', '状态: approved\n级别: L1'));
|
|
2079
|
-
gitCommitAll(T, 'v0.9.0');
|
|
2080
|
-
const r = run(T, { git: true });
|
|
2081
|
-
check('检查17 版本 1 不启用 approved 锚', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
2082
|
-
rmfix(T);
|
|
2083
|
-
}
|
|
2084
|
-
{
|
|
2085
|
-
const T = mkfix();
|
|
2086
|
-
gitInit(T);
|
|
2087
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2088
|
-
w(T, 'package.json', '{"name":"t","version":"0.9"}\n');
|
|
2089
|
-
w(T, 'workflow/intents/2026-09-12-badver.md', INTENT('badver', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2090
|
-
w(T, 'workflow/plans/2026-09-12-badver.md', PLAN('badver', '状态: approved\n级别: L1'));
|
|
2091
|
-
gitCommitAll(T, 'badver');
|
|
2092
|
-
const r = run(T, { git: true });
|
|
2093
|
-
check('检查17 发版版本拆不成三段整数时不触发 approved 规则',
|
|
2094
|
-
r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
2095
|
-
rmfix(T);
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
// ---- 检查 18:委派台账对账 [warning],退出码仍为 0 ----
|
|
2099
|
-
{
|
|
2100
|
-
const T = mkfix();
|
|
2101
|
-
const gap = '2026-09-12-gap.md';
|
|
2102
|
-
w(T, `workflow/intents/${gap}`, INTENT('gap', '状态: done\n级别: L2\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
2103
|
-
w(T, `workflow/specs/${gap}`, SPEC('gap', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2104
|
-
w(T, `workflow/plans/${gap}`, PLAN('gap', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2105
|
-
w(T, 'workflow/intents/2026-09-12-low.md', INTENT('low', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
2106
|
-
w(T, 'workflow/plans/2026-09-12-low.md', PLAN('low', '状态: done\n级别: L1\n日期: 2026-09-12'));
|
|
2107
|
-
w(T, 'workflow/intents/2026-09-12-stay.md', INTENT('stay', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2108
|
-
w(T, 'workflow/specs/2026-09-12-stay.md', SPEC('stay', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2109
|
-
w(T, 'workflow/plans/2026-09-12-stay.md', PLAN('stay', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2110
|
-
const ledgerOf = (r) => outOf(r).split('\n').filter((l) => l.includes('委派台账'));
|
|
2111
|
-
let r = run(T);
|
|
2112
|
-
let lines = ledgerOf(r);
|
|
2113
|
-
check('检查18 委派表不存在:L2 done 警告且 exit 0;L1 done 与 L2 approved 不警告',
|
|
2114
|
-
r.status === 0
|
|
2115
|
-
&& lines.some((l) => l.includes(`intents/${gap}`) && l.includes('2026-09-12'))
|
|
2116
|
-
&& lines.every((l) => !l.includes('low.md') && !l.includes('stay.md')),
|
|
2117
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2118
|
-
|
|
2119
|
-
const head = '## 委派结果\n\n| 日期 | 被委派方 | 任务一句话 | 结果 | 备注 |\n|------|----------|------------|------|------|\n';
|
|
2120
|
-
w(T, 'workflow/delegations.md', `${head}| 2026-09-11 | x | y | 一次通过 | ${gap} |\n`);
|
|
2121
|
-
r = run(T);
|
|
2122
|
-
check('检查18 行日期早于文档日期:警告仍在',
|
|
2123
|
-
r.status === 0 && ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
2124
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2125
|
-
|
|
2126
|
-
w(T, 'workflow/delegations.md', `${head}| 2026-09-12 | x | y | 一次通过 | 2026-09-12-gap |\n`);
|
|
2127
|
-
r = run(T);
|
|
2128
|
-
check('检查18 去掉扩展名的主题名也命中:警告消失',
|
|
2129
|
-
r.status === 0 && !ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
2130
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2131
|
-
|
|
2132
|
-
w(T, 'workflow/delegations.md', `${head}\n## 自做任务结果\n\n| 日期 | 任务一句话 | 结果 | 备注 |\n|------|------------|------|------|\n| 2026-09-12 | x | 一次通过 | ${gap} |\n`);
|
|
2133
|
-
r = run(T);
|
|
2134
|
-
check('检查18 文件名在自做任务结果表:警告消失',
|
|
2135
|
-
r.status === 0 && !ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
2136
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2137
|
-
|
|
2138
|
-
// 补缺(2026-09-30 F2):自做表行**不带 .md**(台账自做表实际通写形态)——此前矩阵只覆盖三格:
|
|
2139
|
-
// 委派表带/不带 .md、自做表带 .md 均有例,唯独「自做表 + 不带 .md」从未穿越(夹具 gap 恒带 .md,
|
|
2140
|
-
// 走的是 includes(base) 分支)。本用例专走 includes(stripped) 分支,矩阵补成 4/4。
|
|
2141
|
-
w(T, 'workflow/delegations.md', `${head}\n## 自做任务结果\n\n| 日期 | 任务一句话 | 结果 | 备注 |\n|------|------------|------|------|\n| 2026-09-12 | x | 一次通过 | 2026-09-12-gap |\n`);
|
|
2142
|
-
r = run(T);
|
|
2143
|
-
check('检查18 自做表行不带 .md(台账实际通写形态):警告消失',
|
|
2144
|
-
r.status === 0 && !ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
2145
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2146
|
-
|
|
2147
|
-
w(T, 'workflow/delegations.md', `${head}| 2026-09-12 | x | y | 一次通过 | ${gap} |\n`);
|
|
2148
|
-
r = run(T);
|
|
2149
|
-
lines = ledgerOf(r);
|
|
2150
|
-
check('检查18 补上不早于文档日期且含文件名的一行:该警告消失',
|
|
2151
|
-
r.status === 0 && !lines.some((l) => l.includes(gap)),
|
|
2152
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2153
|
-
|
|
2154
|
-
// 文件名仅出现在非台账节(既非委派结果也非自做任务结果):不命中
|
|
2155
|
-
w(T, 'workflow/delegations.md', `${head}| 2026-09-12 | x | y | 一次通过 | 其他任务 |\n\n## 历史记录\n| ${gap} 已关单 |\n`);
|
|
2156
|
-
r = run(T);
|
|
2157
|
-
check('检查18 文件名仅出现在非台账节:警告仍在',
|
|
2158
|
-
r.status === 0 && ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
2159
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2160
|
-
rmfix(T);
|
|
2161
|
-
}
|
|
2162
|
-
{
|
|
2163
|
-
// 检查 18 delegationSince 生效日豁免(policy v4,2026-10-06 loop-audit-remediation)。
|
|
2164
|
-
// 「缺键回退原行为」由上方既有场景群天然覆盖(fixture 不写 kit.json → loadKitPolicy 回退 v1 无该键)。
|
|
2165
|
-
const T = mkfix();
|
|
2166
|
-
const pre = '2026-09-12-pre.md';
|
|
2167
|
-
w(T, '.agents/kit.json', '{"policyVersion":4}\n');
|
|
2168
|
-
w(T, `workflow/intents/${pre}`, INTENT('pre', '状态: done\n级别: L2\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
2169
|
-
w(T, `workflow/specs/${pre}`, SPEC('pre', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2170
|
-
w(T, `workflow/plans/${pre}`, PLAN('pre', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2171
|
-
let r = run(T);
|
|
2172
|
-
check('检查18 v4:文档日期早于 delegationSince → 存量豁免不出账(同面在 v1 下照报,见上方首场景)',
|
|
2173
|
-
r.status === 0 && !outOf(r).includes('委派台账'),
|
|
2174
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2175
|
-
|
|
2176
|
-
const post = '2026-10-06-post.md';
|
|
2177
|
-
w(T, `workflow/intents/${post}`, INTENT('post', '状态: done\n级别: L2\n日期: 2026-10-06', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
2178
|
-
w(T, `workflow/specs/${post}`, SPEC('post', '状态: approved\n级别: L2\n日期: 2026-10-06'));
|
|
2179
|
-
w(T, `workflow/plans/${post}`, PLAN('post', '状态: approved\n级别: L2\n日期: 2026-10-06'));
|
|
2180
|
-
r = run(T);
|
|
2181
|
-
// post 日期 ≥ 确认门生效日(v4 confirmDocsEffective=2026-10-06)且无台账行 → 检查 15 hard 同场出账(预期);
|
|
2182
|
-
// 本场景只断言检查 18 对受管日期照报——豁免通道不得吞掉生效日后的真漏点。
|
|
2183
|
-
check('检查18 v4:文档日期 ≥ delegationSince 且台账无文件名 → 照报(exit 1 属检查15 同场,非本检查升级)',
|
|
2184
|
-
r.status === 1 && outOf(r).includes('确认未对账')
|
|
2185
|
-
&& outOf(r).split('\n').some((l) => l.includes('委派台账') && l.includes(`intents/${post}`)),
|
|
2186
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2187
|
-
rmfix(T);
|
|
2188
|
-
}
|
|
2189
|
-
{
|
|
2190
|
-
const T = mkfix();
|
|
2191
|
-
const body = 三件套(false);
|
|
2192
|
-
w(T, 'workflow/incidents/2026-09-12-closed.md', `---\n状态: closed\n级别: L2\n发现: 2026-09-12\n---\n# INCIDENT — closed\n\n${body}`);
|
|
2193
|
-
w(T, 'workflow/specs/2026-09-12-closed.md', SPEC('closed', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2194
|
-
w(T, 'workflow/plans/2026-09-12-closed.md', PLAN('closed', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2195
|
-
w(T, 'workflow/incidents/2026-09-12-l1c.md', `---\n状态: closed\n级别: L1\n发现: 2026-09-12\n---\n# INCIDENT — l1c\n\n${body}`);
|
|
2196
|
-
w(T, 'workflow/plans/2026-09-12-l1c.md', PLAN('l1c', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
2197
|
-
w(T, 'workflow/incidents/2026-09-12-open.md', `---\n状态: open\n级别: L2\n发现: 2026-09-12\n---\n# INCIDENT — open\n\n${body}`);
|
|
2198
|
-
w(T, 'workflow/specs/2026-09-12-open.md', SPEC('open', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2199
|
-
w(T, 'workflow/plans/2026-09-12-open.md', PLAN('open', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2200
|
-
const r = run(T);
|
|
2201
|
-
const lines = outOf(r).split('\n').filter((l) => l.includes('委派台账'));
|
|
2202
|
-
check('检查18 L2 closed 出警告且 exit 0;L1 closed 与 L2 open 不出',
|
|
2203
|
-
r.status === 0
|
|
2204
|
-
&& lines.some((l) => l.includes('incidents/2026-09-12-closed.md'))
|
|
2205
|
-
&& lines.every((l) => !l.includes('l1c.md') && !l.includes('open.md')),
|
|
2206
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2207
|
-
rmfix(T);
|
|
2208
|
-
}
|
|
2209
|
-
{
|
|
2210
|
-
const T = mkfix();
|
|
2211
|
-
w(T, 'workflow/intents/2026-09-12-nodate.md', INTENT('nodate', '状态: done\n级别: L2'));
|
|
2212
|
-
w(T, 'workflow/specs/2026-09-12-nodate.md', SPEC('nodate', '状态: approved\n级别: L2'));
|
|
2213
|
-
w(T, 'workflow/plans/2026-09-12-nodate.md', PLAN('nodate', '状态: approved\n级别: L2'));
|
|
2214
|
-
const r = run(T);
|
|
2215
|
-
check('检查18 范围内但缺日期:不警告',
|
|
2216
|
-
r.status === 0 && !outOf(r).includes('委派台账'),
|
|
2217
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2218
|
-
rmfix(T);
|
|
2219
|
-
}
|
|
2220
|
-
{
|
|
2221
|
-
const T = mkfix();
|
|
2222
|
-
w(T, '.agents/kit.json', '{"audit":false,"policyVersion":1}\n');
|
|
2223
|
-
w(T, 'workflow/intents/2026-09-12-quiet.md', INTENT('quiet', '状态: done\n级别: L2\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
2224
|
-
w(T, 'workflow/specs/2026-09-12-quiet.md', SPEC('quiet', '状态: done\n级别: L2\n日期: 2026-09-12'));
|
|
2225
|
-
w(T, 'workflow/plans/2026-09-12-quiet.md', PLAN('quiet', '状态: done\n级别: L2\n日期: 2026-09-12'));
|
|
2226
|
-
const r = run(T);
|
|
2227
|
-
check('检查18 audit false:委派台账不输出且 exit 0',
|
|
2228
|
-
r.status === 0 && !outOf(r).includes('委派台账'),
|
|
2229
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2230
|
-
rmfix(T);
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
// ---- 检查 19:逐阶段审计 [warning](2026-09-30 stage-gate-machine)----
|
|
2234
|
-
{
|
|
2235
|
-
// 判据 A:spec/plan 为 draft 且日期 ≥ stageGateSince,入口未确认 → 告警(spec 与 plan 各一条)
|
|
2236
|
-
const T = mkfix();
|
|
2237
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2238
|
-
w(T, 'workflow/intents/2026-09-30-pair.md', INTENT('pair', '状态: draft\n级别: L2\n日期: 2026-09-30\n模块: pipeline'));
|
|
2239
|
-
w(T, 'workflow/specs/2026-09-30-pair.md', SPEC('pair', '状态: draft\n级别: L2\n日期: 2026-09-30\n模块: pipeline'));
|
|
2240
|
-
w(T, 'workflow/plans/2026-09-30-pair.md', PLAN('pair', '状态: draft\n级别: L2\n模块: pipeline'));
|
|
2241
|
-
const r = run(T);
|
|
2242
|
-
const lines = outOf(r).split('\n').filter((l) => l.includes('逐阶段'));
|
|
2243
|
-
check('检查19-A 入口未确认:spec 出「起草先于入口确认」、plan 同步告警,exit 0',
|
|
2244
|
-
r.status === 0
|
|
2245
|
-
&& lines.some((l) => l.includes('specs/2026-09-30-pair.md') && l.includes('起草先于入口确认'))
|
|
2246
|
-
&& lines.some((l) => l.includes('plans/2026-09-30-pair.md')),
|
|
2247
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2248
|
-
rmfix(T);
|
|
2249
|
-
}
|
|
2250
|
-
{
|
|
2251
|
-
// 判据 A 日期门:draft 日期早于 stageGateSince → 静默(历史豁免)
|
|
2252
|
-
const T = mkfix();
|
|
2253
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2254
|
-
w(T, 'workflow/intents/2026-09-20-old.md', INTENT('old', '状态: draft\n级别: L1\n日期: 2026-09-20'));
|
|
2255
|
-
w(T, 'workflow/plans/2026-09-20-old.md', PLAN('old', '状态: draft\n级别: L1'));
|
|
2256
|
-
const r = run(T);
|
|
2257
|
-
check('检查19-A 日期门:早于 stageGateSince 的 draft → 零逐阶段告警',
|
|
2258
|
-
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
2259
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2260
|
-
rmfix(T);
|
|
2261
|
-
}
|
|
2262
|
-
{
|
|
2263
|
-
// 判据 A 放行侧:入口已确认(台账行 + 指纹配对)→ plan draft 静默(L1 免 spec 档)
|
|
2264
|
-
const T = mkfix();
|
|
2265
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2266
|
-
const fp = 'a'.repeat(64);
|
|
2267
|
-
w(T, 'workflow/intents/2026-09-30-ok.md', INTENT('ok', `状态: approved\n级别: L1\n日期: 2026-09-30\n确认指纹: ${fp.slice(0, 16)}`));
|
|
2268
|
-
w(T, 'workflow/plans/2026-09-30-ok.md', PLAN('ok', '状态: draft\n级别: L1'));
|
|
2269
|
-
w(T, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-30T01:00:00.000Z', doc: 'workflow/intents/2026-09-30-ok.md', stage: 'approved', fingerprint: fp, prev: 'draft', source: 'chat-delegated', batch: 't1', seq: 1, of: 1 }) + '\n');
|
|
2270
|
-
const r = run(T);
|
|
2271
|
-
check('检查19-A 放行侧:入口 approved + 台账行 → plan draft 静默(L1)',
|
|
2272
|
-
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
2273
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2274
|
-
rmfix(T);
|
|
2275
|
-
}
|
|
2276
|
-
{
|
|
2277
|
-
// 判据 B:台账顺序倒置(spec 早于 intent)→ 告警;行无对应文件也可判(读台账事实)
|
|
2278
|
-
const T = mkfix();
|
|
2279
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2280
|
-
const row = (doc, ts) => JSON.stringify({ ts, doc, stage: 'approved', fingerprint: 'b'.repeat(64), prev: 'draft' }) + '\n';
|
|
2281
|
-
w(T, '.agents/confirmations.jsonl',
|
|
2282
|
-
row('workflow/intents/2026-09-29-ord.md', '2026-09-29T10:00:00.000Z')
|
|
2283
|
-
+ row('workflow/specs/2026-09-29-ord.md', '2026-09-29T09:00:00.000Z')
|
|
2284
|
-
+ row('workflow/plans/2026-09-29-ord.md', '2026-09-29T11:00:00.000Z'));
|
|
2285
|
-
const r = run(T);
|
|
2286
|
-
check('检查19-B 顺序倒置 → 告警且 exit 0',
|
|
2287
|
-
r.status === 0 && outOf(r).includes('逐阶段') && outOf(r).includes('倒置') && outOf(r).includes('2026-09-29-ord'),
|
|
2288
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2289
|
-
rmfix(T);
|
|
2290
|
-
}
|
|
2291
|
-
{
|
|
2292
|
-
// 判据 B:顺序合规 → 静默;组内最早 ts 早于 stageGateSince → 整组跳过
|
|
2293
|
-
const T1 = mkfix();
|
|
2294
|
-
w(T1, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2295
|
-
const row1 = (doc, ts) => JSON.stringify({ ts, doc, stage: 'approved', fingerprint: 'c'.repeat(64), prev: 'draft' }) + '\n';
|
|
2296
|
-
w(T1, '.agents/confirmations.jsonl',
|
|
2297
|
-
row1('workflow/intents/2026-09-29-ok2.md', '2026-09-29T09:00:00.000Z')
|
|
2298
|
-
+ row1('workflow/specs/2026-09-29-ok2.md', '2026-09-29T10:00:00.000Z')
|
|
2299
|
-
+ row1('workflow/plans/2026-09-29-ok2.md', '2026-09-29T11:00:00.000Z'));
|
|
2300
|
-
const r1 = run(T1);
|
|
2301
|
-
check('检查19-B 顺序合规 → 静默',
|
|
2302
|
-
r1.status === 0 && !outOf(r1).includes('逐阶段'),
|
|
2303
|
-
`exit=${r1.status}\n${outOf(r1)}`);
|
|
2304
|
-
rmfix(T1);
|
|
2305
|
-
|
|
2306
|
-
const T2 = mkfix();
|
|
2307
|
-
w(T2, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2308
|
-
const row2 = (doc, ts) => JSON.stringify({ ts, doc, stage: 'approved', fingerprint: 'd'.repeat(64), prev: 'draft' }) + '\n';
|
|
2309
|
-
w(T2, '.agents/confirmations.jsonl',
|
|
2310
|
-
row2('workflow/intents/2026-09-01-h.md', '2026-09-01T10:00:00.000Z')
|
|
2311
|
-
+ row2('workflow/specs/2026-09-01-h.md', '2026-09-01T09:00:00.000Z'));
|
|
2312
|
-
const r2 = run(T2);
|
|
2313
|
-
check('检查19-B 日期门:组内最早 ts 早于 stageGateSince → 整组静默(历史豁免)',
|
|
2314
|
-
r2.status === 0 && !outOf(r2).includes('逐阶段'),
|
|
2315
|
-
`exit=${r2.status}\n${outOf(r2)}`);
|
|
2316
|
-
rmfix(T2);
|
|
2317
|
-
}
|
|
2318
|
-
{
|
|
2319
|
-
// policy v1(无 stageGateSince 键)→ 检查 19 整体跳过
|
|
2320
|
-
const T = mkfix();
|
|
2321
|
-
w(T, '.agents/kit.json', '{"policyVersion":1}\n');
|
|
2322
|
-
const fp = 'e'.repeat(64);
|
|
2323
|
-
w(T, 'workflow/intents/2026-09-30-v1.md', INTENT('v1', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2324
|
-
w(T, 'workflow/plans/2026-09-30-v1.md', PLAN('v1', '状态: draft\n级别: L1'));
|
|
2325
|
-
w(T, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-29T10:00:00.000Z', doc: 'workflow/specs/2026-09-30-v1.md', stage: 'approved', fingerprint: fp, prev: 'draft' }) + '\n');
|
|
2326
|
-
const r = run(T);
|
|
2327
|
-
check('检查19 policy v1(无键)→ 整体跳过(零逐阶段告警)',
|
|
2328
|
-
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
2329
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2330
|
-
rmfix(T);
|
|
2331
|
-
}
|
|
2332
|
-
|
|
2333
|
-
{
|
|
2334
|
-
// 判据 A 的 plan→spec 分支(复核 P2-4②):入口 L2 已确认 + spec 为 draft → plan 出「起草先于 spec 确认」
|
|
2335
|
-
const T = mkfix();
|
|
2336
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2337
|
-
const fp3 = '9'.repeat(64);
|
|
2338
|
-
w(T, 'workflow/intents/2026-09-30-ps.md', INTENT('ps', `状态: approved\n级别: L2\n日期: 2026-09-30\n确认指纹: ${fp3.slice(0, 16)}`));
|
|
2339
|
-
w(T, 'workflow/specs/2026-09-30-ps.md', SPEC('ps', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2340
|
-
w(T, 'workflow/plans/2026-09-30-ps.md', PLAN('ps', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2341
|
-
w(T, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-30T03:00:00.000Z', doc: 'workflow/intents/2026-09-30-ps.md', stage: 'approved', fingerprint: fp3, prev: 'draft', source: 'chat-delegated', batch: 'ps', seq: 1, of: 1 }) + '\n');
|
|
2342
|
-
const r = run(T);
|
|
2343
|
-
check('检查19-A plan→spec 分支:入口确认但 spec 未确认 → 「起草先于 spec 确认」',
|
|
2344
|
-
r.status === 0 && outOf(r).includes('起草先于 spec 确认'),
|
|
2345
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2346
|
-
rmfix(T);
|
|
2347
|
-
}
|
|
2348
|
-
{
|
|
2349
|
-
// 交叉一致性(复核 P2-8):stage-gates.entryConfirmed 与检查 19 内联判据同口径——反例:
|
|
2350
|
-
// approved 但无台账行且日期 ≥ 生效日 → 两处皆判「未确认」(check15 亦会 hard-block,断言只取逐阶段警示)
|
|
2351
|
-
const T = mkfix();
|
|
2352
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2353
|
-
w(T, 'workflow/intents/2026-09-30-x1.md', INTENT('x1', '状态: approved\n级别: L1\n日期: 2026-09-30'));
|
|
2354
|
-
w(T, 'workflow/plans/2026-09-30-x1.md', PLAN('x1', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2355
|
-
const e1 = entryConfirmed(T, '2026-09-30-x1');
|
|
2356
|
-
const r1 = run(T);
|
|
2357
|
-
check('交叉一致性①:入口 approved 无台账行 → stage-gates 判未确认 ∧ 检查 19 出「起草先于入口确认」',
|
|
2358
|
-
e1.ok === false && outOf(r1).includes('起草先于入口确认'),
|
|
2359
|
-
`sg.ok=${e1.ok}\n${outOf(r1)}`);
|
|
2360
|
-
rmfix(T);
|
|
2361
|
-
|
|
2362
|
-
// 正例:approved + 台账行 → 两处皆判「已确认」(检查 19 零「逐阶段」)
|
|
2363
|
-
const T2 = mkfix();
|
|
2364
|
-
w(T2, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2365
|
-
const fp2 = 'f'.repeat(64);
|
|
2366
|
-
w(T2, 'workflow/intents/2026-09-30-x2.md', INTENT('x2', `状态: approved\n级别: L1\n日期: 2026-09-30\n确认指纹: ${fp2.slice(0, 16)}`));
|
|
2367
|
-
w(T2, 'workflow/plans/2026-09-30-x2.md', PLAN('x2', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2368
|
-
w(T2, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-30T02:30:00.000Z', doc: 'workflow/intents/2026-09-30-x2.md', stage: 'approved', fingerprint: fp2, prev: 'draft', source: 'chat-delegated', batch: 'x2', seq: 1, of: 1 }) + '\n');
|
|
2369
|
-
const e2 = entryConfirmed(T2, '2026-09-30-x2');
|
|
2370
|
-
const r2 = run(T2);
|
|
2371
|
-
check('交叉一致性②:入口 approved + 台账行 → stage-gates 判已确认 ∧ 检查 19 零「逐阶段」',
|
|
2372
|
-
e2.ok === true && !outOf(r2).includes('逐阶段'),
|
|
2373
|
-
`sg.ok=${e2.ok}\n${outOf(r2)}`);
|
|
2374
|
-
rmfix(T2);
|
|
2375
|
-
}
|
|
2376
|
-
|
|
2377
|
-
{
|
|
2378
|
-
// N1 存量短路(检查 19 同口径):legacy 入口(无级别)+ plan draft → 零「逐阶段」告警
|
|
2379
|
-
const T = mkfix();
|
|
2380
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2381
|
-
w(T, 'workflow/intents/2026-09-30-leg19.md', INTENT('leg19', '状态: approved\n日期: 2026-01-01\n流程: legacy'));
|
|
2382
|
-
w(T, 'workflow/plans/2026-09-30-leg19.md', PLAN('leg19', '状态: draft\n流程: legacy'));
|
|
2383
|
-
const r = run(T);
|
|
2384
|
-
check('检查19 存量短路:legacy 入口无级别 → plan draft 零「逐阶段」告警',
|
|
2385
|
-
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
2386
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2387
|
-
rmfix(T);
|
|
2388
|
-
}
|
|
2389
|
-
|
|
2390
|
-
{
|
|
2391
|
-
// 检查 19 判据 A 的 **incident 入口**分支(第五轮复核 P2:该分支此前零形态覆盖——
|
|
2392
|
-
// 注入验证证明:把 MARK_RE19 改为恒 false,套件仍全绿 → 正则改动不会被拦下)
|
|
2393
|
-
{
|
|
2394
|
-
const T = mkfix();
|
|
2395
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2396
|
-
// 正例:incident(open + 时间线「用户确认」条目)+ spec draft → 零「逐阶段」告警
|
|
2397
|
-
// 注:incident 亦受配对门约束(须有同名 plan),故夹具须补 plan,否则 exit 1 遮住本判据
|
|
2398
|
-
w(T, 'workflow/incidents/2026-09-30-ic.md', '---\n状态: open\n级别: L2\n发现: 2026-09-30\n---\n# INCIDENT\n\n## 时间线\n- 用户确认:草稿过目通过(2026-09-30)\n');
|
|
2399
|
-
w(T, 'workflow/specs/2026-09-30-ic.md', SPEC('ic', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2400
|
-
w(T, 'workflow/plans/2026-09-30-ic.md', PLAN('ic', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2401
|
-
const r1 = run(T);
|
|
2402
|
-
check('检查19-A incident 入口(时间线留痕)→ 不告警「起草先于入口确认」',
|
|
2403
|
-
r1.status === 0 && !outOf(r1).includes('起草先于入口确认'),
|
|
2404
|
-
`exit=${r1.status}\n${outOf(r1)}`);
|
|
2405
|
-
rmfix(T);
|
|
2406
|
-
|
|
2407
|
-
// 负例:叙述句不算留痕 → 出「起草先于入口确认」
|
|
2408
|
-
const T2 = mkfix();
|
|
2409
|
-
w(T2, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2410
|
-
w(T2, 'workflow/incidents/2026-09-30-ic2.md', '---\n状态: open\n级别: L2\n发现: 2026-09-30\n---\n# INCIDENT\n\n## 时间线\n- 2026-09-30 用户确认了方案(叙述)\n');
|
|
2411
|
-
w(T2, 'workflow/specs/2026-09-30-ic2.md', SPEC('ic2', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2412
|
-
w(T2, 'workflow/plans/2026-09-30-ic2.md', PLAN('ic2', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2413
|
-
const r2 = run(T2);
|
|
2414
|
-
check('检查19-A incident 入口(叙述句不算)→ 出「起草先于入口确认」',
|
|
2415
|
-
r2.status === 0 && outOf(r2).includes('起草先于入口确认'),
|
|
2416
|
-
`exit=${r2.status}\n${outOf(r2)}`);
|
|
2417
|
-
rmfix(T2);
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
|
|
2421
|
-
// ---- 风险泳道(2026-09-30 hybrid-governance-risk-lanes):红线判低 ----
|
|
2422
|
-
// (2026-10-13 L0 退役,自源仓收敛系列 1 回灌:原「L0 配对豁免」场景随级别退役删除。存量 intent 仍须 plan;L1 plan 可独立成入口。)
|
|
2423
|
-
{
|
|
2424
|
-
// L1 intent 仍须 plan(协作道不豁免 L1——防倒退回归)
|
|
2425
|
-
const T1 = mkfix();
|
|
2426
|
-
w(T1, 'workflow/intents/2026-09-12-l1.md', INTENT('l1', '状态: approved\n级别: L1\nrisk_level: L1\n日期: 2026-09-12'));
|
|
2427
|
-
expectHard('风险泳道:L1 intent 缺 plan 仍 hard 配对断裂', T1, '配对断裂');
|
|
2428
|
-
rmfix(T1);
|
|
2429
|
-
|
|
2430
|
-
// L1 勾触达红线 → hard「红线判低」(级别与 risk_level 任一判定皆拦)
|
|
2431
|
-
const T2 = mkfix();
|
|
2432
|
-
w(T2, 'workflow/intents/2026-09-12-rl.md', INTENT('rl', '状态: approved\n级别: L1\nrisk_level: L1\n日期: 2026-09-12',
|
|
2433
|
-
'\n## 触达红线(对照 AGENTS.md)\n- [x] 规则 / 契约变更(接口签名)→ 级别至少 L2\n'));
|
|
2434
|
-
const r2 = run(T2);
|
|
2435
|
-
check('风险泳道:L1 勾触达红线 → hard 红线判低',
|
|
2436
|
-
r2.status === 1 && outOf(r2).includes('红线判低'),
|
|
2437
|
-
`exit=${r2.status}\n${outOf(r2)}`);
|
|
2438
|
-
rmfix(T2);
|
|
2439
|
-
|
|
2440
|
-
const T3 = mkfix();
|
|
2441
|
-
w(T3, 'workflow/intents/2026-09-12-rl2.md', INTENT('rl2', '状态: approved\n级别: L2\nrisk_level: L2\n日期: 2026-09-12',
|
|
2442
|
-
'\n## 触达红线(对照 AGENTS.md)\n- [x] 规则 / 契约变更(接口签名)→ 级别至少 L2\n'));
|
|
2443
|
-
w(T3, 'workflow/specs/2026-09-12-rl2.md', SPEC('rl2', '状态: approved\n级别: L2'));
|
|
2444
|
-
w(T3, 'workflow/plans/2026-09-12-rl2.md', PLAN('rl2', '状态: approved\n级别: L2'));
|
|
2445
|
-
const r3 = run(T3);
|
|
2446
|
-
check('风险泳道:L2 勾红线不判低(防御道受理)',
|
|
2447
|
-
r3.status === 0 && !outOf(r3).includes('红线判低'),
|
|
2448
|
-
`exit=${r3.status}\n${outOf(r3)}`);
|
|
2449
|
-
rmfix(T3);
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
// ---- 检查 15 并录豁免:--batch sanctioned 批次(delegated of>1 + 全行 brief:true)→ 零「确认并录」----
|
|
2453
|
-
{
|
|
2454
|
-
const T = mkfix();
|
|
2455
|
-
const fpI = 'a'.repeat(64);
|
|
2456
|
-
const fpP = 'b'.repeat(64);
|
|
2457
|
-
w(T, 'workflow/intents/2026-09-12-b1.md', INTENT('b1', `状态: approved\n级别: L1\n日期: 2026-09-12\n确认指纹: ${fpI.slice(0, 16)}`));
|
|
2458
|
-
w(T, 'workflow/plans/2026-09-12-b1.md', PLAN('b1', `状态: approved\n级别: L1\n日期: 2026-09-12\n确认指纹: ${fpP.slice(0, 16)}`));
|
|
2459
|
-
const row = (doc, fp, seq) => JSON.stringify({ ts: '2026-09-29T02:00:00.000Z', doc, stage: 'approved', fingerprint: fp, prev: 'draft', source: 'chat-delegated', quote: '批量放行', batch: 'bt1', seq, of: 2, brief: true }) + '\n';
|
|
2460
|
-
w(T, '.agents/confirmations.jsonl',
|
|
2461
|
-
row('workflow/intents/2026-09-12-b1.md', fpI, 1)
|
|
2462
|
-
+ row('workflow/plans/2026-09-12-b1.md', fpP, 2));
|
|
2463
|
-
const r = run(T);
|
|
2464
|
-
check('检查15 --batch sanctioned:全 brief 批次 → 零「确认并录」告警',
|
|
2465
|
-
r.status === 0 && !outOf(r).includes('确认并录'),
|
|
2466
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2467
|
-
rmfix(T);
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2470
|
-
// ---- 检查 19 协作道豁免(2026-09-30 hybrid-governance-risk-lanes)----
|
|
2471
|
-
{
|
|
2472
|
-
// L1 intent draft + plan draft(日期 ≥ stageGateSince)→ 「先动手后确认」是泳道语义,零告警
|
|
2473
|
-
const T = mkfix();
|
|
2474
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2475
|
-
w(T, 'workflow/intents/2026-09-30-lane.md', INTENT('lane', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2476
|
-
w(T, 'workflow/plans/2026-09-30-lane.md', PLAN('lane', '状态: draft\n级别: L1'));
|
|
2477
|
-
const r = run(T);
|
|
2478
|
-
check('检查19 协作道豁免:L1 intent draft + plan draft → 零「起草先于入口确认」',
|
|
2479
|
-
r.status === 0 && !outOf(r).includes('起草先于入口确认'),
|
|
2480
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2481
|
-
rmfix(T);
|
|
2482
|
-
}
|
|
2483
|
-
{
|
|
2484
|
-
// 防倒退:L1 intent approved 却无台账行(非 draft)→ 仍告警(豁免仅限 draft 态,防手改状态冒充确认)
|
|
2485
|
-
const T = mkfix();
|
|
2486
|
-
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2487
|
-
w(T, 'workflow/intents/2026-09-30-lane2.md', INTENT('lane2', '状态: approved\n级别: L1\n日期: 2026-09-30'));
|
|
2488
|
-
w(T, 'workflow/plans/2026-09-30-lane2.md', PLAN('lane2', '状态: draft\n级别: L1'));
|
|
2489
|
-
const r = run(T);
|
|
2490
|
-
check('检查19 L1 approved 无台账行 → 仍出「起草先于入口确认」(豁免仅限 draft)',
|
|
2491
|
-
outOf(r).includes('起草先于入口确认'),
|
|
2492
|
-
outOf(r));
|
|
2493
|
-
rmfix(T);
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
// ---- 加固门(2026-09-30 hybrid-governance-explore-hardening;仅 --hardening 启用,不占 1-20 编号)----
|
|
2497
|
-
{
|
|
2498
|
-
// H1:未标记 exploring 的 draft 件 → 带/不带 --hardening 均不拦(本门只管「已标记未收口」,
|
|
2499
|
-
// 常规流程的 draft 中间态不受影响——intent approved 后 plan 起草属正常在途)
|
|
2500
|
-
const T = mkfix();
|
|
2501
|
-
w(T, 'workflow/intents/2026-09-30-h0.md', INTENT('h0', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2502
|
-
w(T, 'workflow/plans/2026-09-30-h0.md', PLAN('h0', '状态: draft\n级别: L1'));
|
|
2503
|
-
const rOff = run(T);
|
|
2504
|
-
const rOn = run(T, { args: ['--hardening'] });
|
|
2505
|
-
check('加固门 H1:未标记 exploring 的 draft 件 → 带/不带 --hardening 均 exit 0',
|
|
2506
|
-
rOff.status === 0 && rOn.status === 0, `off=${rOff.status}\non=${outOf(rOn)}`);
|
|
2507
|
-
rmfix(T);
|
|
2508
|
-
}
|
|
2509
|
-
{
|
|
2510
|
-
// H2:已标记 exploring、intent 仍 draft(配同名 plan 防 check 1 配对拦)→ --hardening hard 拦;
|
|
2511
|
-
// 不带旗标 exit 0(泳道内推送/日常扫描不受影响)
|
|
2512
|
-
const T = mkfix();
|
|
2513
|
-
w(T, 'workflow/intents/2026-09-30-h1.md', INTENT('h1', '状态: draft\n级别: L1\nrisk_level: L1\n阶段: exploring\n日期: 2026-09-30'));
|
|
2514
|
-
w(T, 'workflow/plans/2026-09-30-h1.md', PLAN('h1', '状态: draft\n级别: L1'));
|
|
2515
|
-
const rOn = run(T, { args: ['--hardening'] });
|
|
2516
|
-
const rOff = run(T);
|
|
2517
|
-
check('加固门 H2:exploring 件 intent 仍 draft → --hardening exit 1「加固未过」;不带旗标 exit 0',
|
|
2518
|
-
rOn.status === 1 && outOf(rOn).includes('加固未过') && rOff.status === 0, outOf(rOn));
|
|
2519
|
-
rmfix(T);
|
|
2520
|
-
}
|
|
2521
|
-
{
|
|
2522
|
-
// H3:已收口(intent approved + plan approved,均带真实指纹 + 台账配对防检查 15 先拦)→ 放行;L1 不索 spec
|
|
2523
|
-
const T = mkfix();
|
|
2524
|
-
const ent = mkConfirmedDoc(T, 'workflow/intents/2026-09-30-h2.md', '状态: approved\n级别: L1\nrisk_level: L1\n阶段: exploring\n日期: 2026-09-30');
|
|
2525
|
-
const plan = mkConfirmedDoc(T, 'workflow/plans/2026-09-30-h2.md', '状态: approved\n级别: L1\n阶段: exploring');
|
|
2526
|
-
writeLedger(T, [
|
|
2527
|
-
{ ts: '2026-09-30T01:00:00.000Z', doc: ent.rel, stage: 'approved', fingerprint: ent.fp, prev: 'draft', source: 'tty' },
|
|
2528
|
-
{ ts: '2026-09-30T02:00:00.000Z', doc: plan.rel, stage: 'approved', fingerprint: plan.fp, prev: 'draft', source: 'tty' },
|
|
2529
|
-
]);
|
|
2530
|
-
const r = run(T, { args: ['--hardening'] });
|
|
2531
|
-
check('加固门 H3:exploring 件已收口(intent/plan approved + 台账配对)→ --hardening exit 0',
|
|
2532
|
-
r.status === 0, outOf(r));
|
|
2533
|
-
rmfix(T);
|
|
2534
|
-
}
|
|
2535
|
-
{
|
|
2536
|
-
// H4:intent 已收口但 plan 仍 draft → hard「加固未过」(plan 未收口;L2 缺 spec 由检查 1 同步出账)
|
|
2537
|
-
const T = mkfix();
|
|
2538
|
-
const ent = mkConfirmedDoc(T, 'workflow/intents/2026-09-30-h3.md', '状态: approved\n级别: L2\n阶段: exploring\n日期: 2026-09-30');
|
|
2539
|
-
writeLedger(T, [{ ts: '2026-09-30T01:00:00.000Z', doc: ent.rel, stage: 'approved', fingerprint: ent.fp, prev: 'draft', source: 'tty' }]);
|
|
2540
|
-
w(T, 'workflow/plans/2026-09-30-h3.md', PLAN('h3', '状态: draft\n级别: L2'));
|
|
2541
|
-
const r = run(T, { args: ['--hardening'] });
|
|
2542
|
-
check('加固门 H4:exploring 件 plan 未收口(draft)→ --hardening exit 1「加固未过」',
|
|
2543
|
-
r.status === 1 && outOf(r).includes('加固未过'), outOf(r));
|
|
2544
|
-
rmfix(T);
|
|
2545
|
-
}
|
|
2546
|
-
{
|
|
2547
|
-
// H5:放弃态出列——intent 已 superseded 的 exploring 件 → --hardening 不拦(探索作废留档,无代码随行)。
|
|
2548
|
-
// 台账指纹按 confirm-doc 前向语义复刻:fp = 跳转前(approved 态)内容的指纹,当前盘面 superseded + 指纹行
|
|
2549
|
-
// ——检查 15 的内容绑定(superseded 受绑)按 prev=approved 复原重算须能对上。
|
|
2550
|
-
const T = mkfix();
|
|
2551
|
-
const rel = 'workflow/intents/2026-09-30-h4.md';
|
|
2552
|
-
const prevText = '---\n状态: approved\n级别: L1\n阶段: exploring\n日期: 2026-09-30\n---\n# DOC\n';
|
|
2553
|
-
const fp = computeFingerprint(prevText);
|
|
2554
|
-
fs.writeFileSync(path.join(T, rel), `---\n状态: superseded\n级别: L1\n阶段: exploring\n日期: 2026-09-30\n确认指纹: ${fp.slice(0, 16)}\n---\n# DOC\n`);
|
|
2555
|
-
w(T, 'workflow/plans/2026-09-30-h4.md', PLAN('h4', '状态: draft\n级别: L1')); // 防 check 1 配对拦(L1 须 plan;draft 仅 warning 不碍 exit 0)
|
|
2556
|
-
writeLedger(T, [{ ts: '2026-09-30T03:00:00.000Z', doc: rel, stage: 'superseded', fingerprint: fp, prev: 'approved', source: 'tty' }]);
|
|
2557
|
-
const r = run(T, { args: ['--hardening'] });
|
|
2558
|
-
check('加固门 H5:exploring 件显式放弃(superseded + 台账绑定)→ --hardening exit 0(出列不加固)',
|
|
2559
|
-
r.status === 0, outOf(r));
|
|
2560
|
-
rmfix(T);
|
|
2561
|
-
}
|
|
2562
|
-
{
|
|
2563
|
-
// H6:未知参数 → 用法提示 exit 1(arg 解析收紧后的防倒退)
|
|
2564
|
-
const T = mkfix();
|
|
2565
|
-
const r = run(T, { args: ['--bogus'] });
|
|
2566
|
-
check('加固门 H6:未知 CLI 参数 → exit 1 用法提示', r.status === 1 && /用法/.test(outOf(r)), outOf(r));
|
|
2567
|
-
rmfix(T);
|
|
2568
|
-
}
|
|
2569
|
-
{
|
|
2570
|
-
// H6b:--all 与 --hygiene 互斥组合 → 用法提示 exit 1(2026-10-13 分道,源仓 takeOpts 口径)
|
|
2571
|
-
const T = mkfix();
|
|
2572
|
-
const r = run(T, { args: ['--all', '--hygiene'] });
|
|
2573
|
-
check('分道 H6b:--all 与 --hygiene 同给 → exit 1 用法提示', r.status === 1 && /用法/.test(outOf(r)), outOf(r));
|
|
2574
|
-
rmfix(T);
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
// ---- 默认入口 vs 卫生入口 vs 全量(2026-10-13 B6b-1 分道;源仓 check-loop.test.mjs 同名场景移植)----
|
|
2578
|
-
{
|
|
2579
|
-
// 卫生违例(模板占位符残留,检查 2 hygiene 面)无硬违例:默认(裸跑=硬门)不出卫生面且 exit 0;
|
|
2580
|
-
// --hygiene 出卫生面且 exit 0;--all 两段都跑 = 分道前的裸跑全量。
|
|
2581
|
-
// (夹具给配对齐全的 intent+plan——kit 检查 1 对 plan 落单即拦配对断裂,无源仓的 L1 豁免。)
|
|
2582
|
-
const T = mkfix();
|
|
2583
|
-
w(T, 'workflow/intents/2026-10-13-ph.md', INTENT('ph', '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
2584
|
-
w(T, 'workflow/plans/2026-10-13-ph.md', PLAN('ph', '状态: draft\n级别: L1', '\n## 确认与复核\n\n- 日期: YYYY-MM-DD\n'));
|
|
2585
|
-
const bare = run(T, { args: [] });
|
|
2586
|
-
check('分道:默认入口不跑卫生段——占位符不出「模板未填」且 exit 0',
|
|
2587
|
-
bare.status === 0 && !outOf(bare).includes('模板未填'), `exit=${bare.status}\n${outOf(bare)}`);
|
|
2588
|
-
const hy = run(T, { args: ['--hygiene'] });
|
|
2589
|
-
check('分道:--hygiene 只跑卫生段——同一占位符出「模板未填」且不报配对断裂',
|
|
2590
|
-
hy.status === 0 && outOf(hy).includes('模板未填') && !outOf(hy).includes('配对断裂'), `exit=${hy.status}\n${outOf(hy)}`);
|
|
2591
|
-
const all = run(T, { args: ['--all'] });
|
|
2592
|
-
check('分道:--all 两段都跑(等价分道前裸跑全量)——出「模板未填」且 exit 0',
|
|
2593
|
-
all.status === 0 && outOf(all).includes('模板未填'), `exit=${all.status}\n${outOf(all)}`);
|
|
2594
|
-
rmfix(T);
|
|
2595
|
-
// 硬违例(L1 approved intent 缺同名 plan = 配对断裂,检查 1 hard 面):默认入口与 --all 都拦;
|
|
2596
|
-
// --hygiene 只跑卫生段、不产 blocker(源仓同款语义:卫生段不进关单)。
|
|
2597
|
-
const T2 = mkfix();
|
|
2598
|
-
w(T2, 'workflow/intents/2026-10-13-gap.md', INTENT('gap', '状态: approved\n级别: L1\n日期: 2026-10-13'));
|
|
2599
|
-
const gapBare = run(T2, { args: [] });
|
|
2600
|
-
check('分道:默认入口仍拦配对断裂', gapBare.status === 1 && outOf(gapBare).includes('配对断裂'), `exit=${gapBare.status}\n${outOf(gapBare)}`);
|
|
2601
|
-
const gapAll = run(T2, { args: ['--all'] });
|
|
2602
|
-
check('分道:--all 同样拦配对断裂', gapAll.status === 1 && outOf(gapAll).includes('配对断裂'), `exit=${gapAll.status}\n${outOf(gapAll)}`);
|
|
2603
|
-
const gapHy = run(T2, { args: ['--hygiene'] });
|
|
2604
|
-
check('分道:--hygiene 不跑硬门——配对断裂不出、exit 0(卫生段不进关单)',
|
|
2605
|
-
gapHy.status === 0 && !outOf(gapHy).includes('配对断裂'), `exit=${gapHy.status}\n${outOf(gapHy)}`);
|
|
2606
|
-
rmfix(T2);
|
|
2607
|
-
}
|
|
2608
|
-
|
|
2609
|
-
// --- 20. 引擎脚本测试覆盖 [warning](2026-10-01 gate-script-test-coverage) ---
|
|
2610
|
-
{
|
|
2611
|
-
// C20-1:缺测试报 warning、有测试与豁免登记不报(包源环境)
|
|
2612
|
-
const T = mkfix();
|
|
2613
|
-
const sd = path.join(T, 'templates', '_agents', 'scripts');
|
|
2614
|
-
fs.mkdirSync(sd, { recursive: true });
|
|
2615
|
-
fs.writeFileSync(path.join(sd, 'foo.mjs'), 'export {};\n');
|
|
2616
|
-
fs.writeFileSync(path.join(sd, 'foo.test.mjs'), '// ok\n');
|
|
2617
|
-
fs.writeFileSync(path.join(sd, 'bar.mjs'), 'export {};\n');
|
|
2618
|
-
fs.writeFileSync(path.join(sd, 'baz.mjs'), 'export {};\n');
|
|
2619
|
-
fs.writeFileSync(path.join(T, '.agents', 'scripts-test-exempt.txt'), 'baz.mjs | 库件 | 无\n');
|
|
2620
|
-
const r1 = run(T);
|
|
2621
|
-
const o1 = outOf(r1);
|
|
2622
|
-
check('检查20 C20-1 缺测试报 warning、有测试与豁免不报',
|
|
2623
|
-
r1.status === 0 && o1.includes('[WARN 脚本测试缺失] bar.mjs') && !o1.includes('foo.mjs') && !o1.includes('baz.mjs'), o1);
|
|
2624
|
-
rmfix(T);
|
|
2625
|
-
}
|
|
2626
|
-
{
|
|
2627
|
-
// C20-2:装户环境(无 templates/)整体跳过
|
|
2628
|
-
const T = mkfix();
|
|
2629
|
-
const r2 = run(T);
|
|
2630
|
-
check('检查20 C20-2 装户(无 templates/)跳过', !outOf(r2).includes('[WARN 脚本测试缺失]'), outOf(r2));
|
|
2631
|
-
rmfix(T);
|
|
2632
|
-
}
|
|
2633
|
-
{
|
|
2634
|
-
// C20-3:豁免文件缺失时全量列出(无豁免兜底)
|
|
2635
|
-
const T = mkfix();
|
|
2636
|
-
const sd = path.join(T, 'templates', '_agents', 'scripts');
|
|
2637
|
-
fs.mkdirSync(sd, { recursive: true });
|
|
2638
|
-
fs.writeFileSync(path.join(sd, 'zeta.mjs'), 'export {};\n');
|
|
2639
|
-
const r3 = run(T);
|
|
2640
|
-
check('检查20 C20-3 无豁免文件时缺测试脚本被列出', outOf(r3).includes('[WARN 脚本测试缺失] zeta.mjs'), outOf(r3));
|
|
2641
|
-
rmfix(T);
|
|
2642
|
-
}
|
|
2643
|
-
|
|
2644
|
-
// --- 方案 C:laneOfEntry 单源三态 + 检查 1 suspect 红线 + 检查 2 样例豁免(2026-10-02 caliber-convergence) ---
|
|
2645
|
-
{
|
|
2646
|
-
check('laneOfEntry 一致 low / 一致 high',
|
|
2647
|
-
laneOfEntry('L1', 'L1', '2026-10-01', '2026-10-02') === 'low' && laneOfEntry('L2', 'L2', '2026-10-01', '2026-10-02') === 'high');
|
|
2648
|
-
check('laneOfEntry 协议期:分歧 / risk 缺失 / 级别缺失 → suspect',
|
|
2649
|
-
laneOfEntry('L2', 'L1', '2026-10-01', '2026-10-02') === 'suspect'
|
|
2650
|
-
&& laneOfEntry('L2', '', '2026-10-01', '2026-10-02') === 'suspect'
|
|
2651
|
-
&& laneOfEntry('', 'L2', '2026-10-01', '2026-10-02') === 'suspect');
|
|
2652
|
-
check('laneOfEntry 协议前:单字段按级别 / 两值俱在分歧仍 suspect',
|
|
2653
|
-
laneOfEntry('L2', '', '2026-10-01', '2026-09-28') === 'high'
|
|
2654
|
-
&& laneOfEntry('L2', 'L1', '2026-10-01', '2026-09-28') === 'suspect');
|
|
2655
|
-
check('laneOfDoc 单字段三态', laneOfDoc('L1') === 'low' && laneOfDoc('L3') === 'high' && laneOfDoc('') === 'suspect');
|
|
2656
|
-
}
|
|
2657
|
-
{
|
|
2658
|
-
// 检查 1:协议期分歧件(L2/L1)勾红线 → suspect 按 low 拦红线(hard)
|
|
2659
|
-
const T = mkfix();
|
|
2660
|
-
fs.writeFileSync(path.join(T, 'workflow', 'intents', '2026-10-02-x.md'),
|
|
2661
|
-
INTENT('x', '状态: approved\n级别: L2\nrisk_level: L1\n日期: 2026-10-02\n模块: pipeline',
|
|
2662
|
-
'\n## 触达红线\n- [x] 规则 / 契约变更(→ 级别至少 L2)\n'));
|
|
2663
|
-
fs.writeFileSync(path.join(T, 'workflow', 'plans', '2026-10-02-x.md'), '# PLAN\n');
|
|
2664
|
-
const r = run(T);
|
|
2665
|
-
check('检查1 分歧件勾红线 → hard-block(suspect 拦红线)', r.status === 1 && outOf(r).includes('[红线判低]') && outOf(r).includes('两字段对齐'), outOf(r));
|
|
2666
|
-
rmfix(T);
|
|
2667
|
-
}
|
|
2668
|
-
{
|
|
2669
|
-
// 检查 1 对照:协议前存量(级别 L2 无 risk_level)勾红线 → 不拦(单字段 high)
|
|
2670
|
-
const T = mkfix();
|
|
2671
|
-
fs.writeFileSync(path.join(T, 'workflow', 'intents', '2026-09-28-y.md'),
|
|
2672
|
-
INTENT('y', '状态: approved\n级别: L2\n日期: 2026-09-28\n模块: pipeline',
|
|
2673
|
-
'\n## 触达红线\n- [x] 规则 / 契约变更(→ 级别至少 L2)\n'));
|
|
2674
|
-
fs.writeFileSync(path.join(T, 'workflow', 'plans', '2026-09-28-y.md'), '# PLAN\n');
|
|
2675
|
-
const r = run(T);
|
|
2676
|
-
check('检查1 协议前 L2 单字段勾红线 → 不拦(存量零新增损害)', !outOf(r).includes('[红线判低]'), outOf(r));
|
|
2677
|
-
rmfix(T);
|
|
2678
|
-
}
|
|
2679
|
-
{
|
|
2680
|
-
// 检查 2:样例豁免三态——反引号/围栏块内不报,正文裸占位符照报
|
|
2681
|
-
const T = mkfix();
|
|
2682
|
-
fs.writeFileSync(path.join(T, 'workflow', 'intents', '2026-10-02-z.md'),
|
|
2683
|
-
INTENT('z', '状态: approved\n级别: L2\nrisk_level: L2\n日期: 2026-10-02\n模块: pipeline',
|
|
2684
|
-
'\n行内样例 `YYYY-MM-DD` 与 `<主题>` 不报。\n\n```\n日期: YYYY-MM-DD\n```\n\n裸占位:日期: YYYY-MM-DD\n'));
|
|
2685
|
-
fs.writeFileSync(path.join(T, 'workflow', 'plans', '2026-10-02-z.md'), '# PLAN\n');
|
|
2686
|
-
const r = run(T);
|
|
2687
|
-
const o = outOf(r);
|
|
2688
|
-
const zWarn = (o.match(/2026-10-02-z\.md 含模板占位符:[\s\S]*?(?=\n- |\n\n|$)/) || [''])[0];
|
|
2689
|
-
check('检查2 反引号/围栏样例不报、裸占位符照报',
|
|
2690
|
-
zWarn.includes('裸占位') && !zWarn.includes('行内样例') && !zWarn.includes('```'), o);
|
|
2691
|
-
rmfix(T);
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
{
|
|
2695
|
-
// H7(2026-10-02 caliber-convergence 复核 P1-1):suspect(级别 L2/risk_level L1 分歧,协议期)转正
|
|
2696
|
-
// → 与 high 同样另须同名 spec——缺 spec 即拦(只增不松:suspect 不得逃 spec 前置)。
|
|
2697
|
-
const T = mkfix();
|
|
2698
|
-
w(T, 'workflow/intents/2026-10-02-h7.md',
|
|
2699
|
-
INTENT('h7', '状态: approved\n级别: L2\nrisk_level: L1\n日期: 2026-10-02\n模块: pipeline\n阶段: exploring'));
|
|
2700
|
-
w(T, 'workflow/plans/2026-10-02-h7.md', PLAN('h7', '状态: approved\n级别: L2'));
|
|
2701
|
-
const r = run(T, { args: ['--hardening'] });
|
|
2702
|
-
check('加固门 H7:suspect(L2/L1 分歧)转正 → 缺同名 spec 即拦',
|
|
2703
|
-
r.status === 1 && outOf(r).includes('缺同名 spec'), outOf(r));
|
|
2704
|
-
rmfix(T);
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
// ---- 场景:检查 8 记录型提交豁免——docs 系证据 + 实现证据在场 → 豁免;仅 docs 证据 → hard(2026-10-06-v114-backflow-batch)----
|
|
2708
|
-
{
|
|
2709
|
-
const T = mkfix();
|
|
2710
|
-
gitInit(T);
|
|
2711
|
-
w(T, 'workflow/plans/2026-09-12-rec.md', PLAN('rec', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 改动面\n- src/impl.txt: 实现改动\n'));
|
|
2712
|
-
fs.mkdirSync(path.join(T, 'src'), { recursive: true });
|
|
2713
|
-
w(T, 'src/impl.txt', 'impl\n');
|
|
2714
|
-
gitCommitAll(T, 'feat: impl change');
|
|
2715
|
-
const implSha = shortSha(T);
|
|
2716
|
-
w(T, 'record-note.md', 'record\n');
|
|
2717
|
-
gitCommitAll(T, 'docs: 记录留痕');
|
|
2718
|
-
const docsSha = shortSha(T);
|
|
2719
|
-
w(T, 'workflow/intents/2026-09-12-rec.md', INTENT('rec', '状态: done\n级别: L1\n日期: 2026-09-12', `\n## 验收标准(可测试)\n- [x] 实现(证据:commit ${implSha})\n- [x] 记录(证据:commit ${docsSha})\n`));
|
|
2720
|
-
let r = run(T);
|
|
2721
|
-
check('检查 8 记录型豁免:docs 证据 + 实现证据在场 → 豁免且 exit 0', r.status === 0 && outOf(r).includes('证据豁免 record'), outOf(r));
|
|
2722
|
-
w(T, 'workflow/intents/2026-09-12-rec.md', INTENT('rec', '状态: done\n级别: L1\n日期: 2026-09-12', `\n## 验收标准(可测试)\n- [x] 记录(证据:commit ${docsSha})\n`));
|
|
2723
|
-
r = run(T);
|
|
2724
|
-
check('检查 8 记录型豁免:仅 docs 证据无实现证据 → 仍 hard 证据无关', r.status === 1 && outOf(r).includes('证据无关'), outOf(r));
|
|
2725
|
-
rmfix(T);
|
|
2726
|
-
}
|
|
2727
|
-
|
|
2728
|
-
// ---- 场景:检查 19 判据 B——台账 approved 行在、入口 intent 缺件(incident 闭环主题)→ 无 ENOENT 出账,顺序判定不放松(2026-10-06-check19-entry-enoent)----
|
|
2729
|
-
{
|
|
2730
|
-
const ledgerRows = (specTs, planTs) => [
|
|
2731
|
-
{ doc: 'workflow/specs/2026-10-06-noent.md', stage: 'approved', ts: specTs },
|
|
2732
|
-
{ doc: 'workflow/plans/2026-10-06-noent.md', stage: 'approved', ts: planTs },
|
|
2733
|
-
];
|
|
2734
|
-
// ① 顺序合规(specs 先于 plans):exit 0,且裸 fixture 全程无「文档读取异常(ENOENT)」出账——
|
|
2735
|
-
// 修复前「读空判级」惯用法在 fail-loud fs 读下对缺件主题每次响亮出账(实仓 12 条/推送);
|
|
2736
|
-
// 同类读空点(AGENTS.md / new-task.md,检查 6)一并被裸 fixture 钉住
|
|
2737
|
-
const T = mkfix();
|
|
2738
|
-
w(T, '.agents/kit.json', JSON.stringify({ policyVersion: 2 }));
|
|
2739
|
-
writeLedger(T, ledgerRows('2026-10-06T10:00:00Z', '2026-10-06T11:00:00Z'));
|
|
2740
|
-
let r = run(T);
|
|
2741
|
-
check('检查 19 判据 B:入口 intent 缺件 → 无 ENOENT 噪声且 exit 0',
|
|
2742
|
-
r.status === 0 && !outOf(r).includes('文档读取异常'), `exit=${r.status}\n${outOf(r)}`);
|
|
2743
|
-
rmfix(T);
|
|
2744
|
-
// ② 顺序倒置(plans 先于 specs):入口缺件不豁免,照报「审批顺序倒置」——守卫不放松判定
|
|
2745
|
-
const T2 = mkfix();
|
|
2746
|
-
w(T2, '.agents/kit.json', JSON.stringify({ policyVersion: 2 }));
|
|
2747
|
-
writeLedger(T2, ledgerRows('2026-10-06T11:00:00Z', '2026-10-06T10:00:00Z'));
|
|
2748
|
-
r = run(T2);
|
|
2749
|
-
check('检查 19 判据 B:入口 intent 缺件 → 顺序倒置照报(守卫不放松判定)',
|
|
2750
|
-
r.status === 0 && outOf(r).includes('审批顺序倒置'), `exit=${r.status}\n${outOf(r)}`);
|
|
2751
|
-
rmfix(T2);
|
|
2752
|
-
}
|
|
2753
|
-
|
|
2754
|
-
// ---- 场景:检查 8 测试绿声明对账(2026-10-07 verify-evidence)——v5 锚后无 SHA「测试绿」声明须有凭证 ----
|
|
2755
|
-
{
|
|
2756
|
-
// 配对构造(检查 15 须过):approved 态全文算指纹 → done 态落盘 + 台账 done 行(prev=approved,
|
|
2757
|
-
// ts=今天 ≥ bindingTs)——与 bindingSha256 复原口径对称
|
|
2758
|
-
const mkVe = (withLedger) => {
|
|
2759
|
-
const T = mkfix();
|
|
2760
|
-
gitInit(T);
|
|
2761
|
-
w(T, '.agents/kit.json', JSON.stringify({ policyVersion: 5 }));
|
|
2762
|
-
if (withLedger) {
|
|
2763
|
-
w(T, '.agents/verifications.jsonl', `${JSON.stringify({ ts: new Date().toISOString(), exitCode: 0, suite: 'npm test' })}\n`);
|
|
2764
|
-
}
|
|
2765
|
-
const iBody = '# INTENT — ve\n\n## 验收标准(可测试)\n- [x] 用例通过(证据:测试全绿)\n';
|
|
2766
|
-
const pBody = '# PLAN — ve\n';
|
|
2767
|
-
const fpI = computeFingerprint(`---\n状态: approved\n级别: L1\n日期: 2026-10-07\n---\n${iBody}`);
|
|
2768
|
-
const fpP = computeFingerprint(`---\n状态: approved\n级别: L1\n---\n${pBody}`);
|
|
2769
|
-
w(T, 'workflow/intents/2026-10-07-ve.md', `---\n状态: done\n级别: L1\n日期: 2026-10-07\n确认指纹: ${fpI.slice(0, 16)}\n---\n${iBody}`);
|
|
2770
|
-
w(T, 'workflow/plans/2026-10-07-ve.md', `---\n状态: done\n级别: L1\n确认指纹: ${fpP.slice(0, 16)}\n---\n${pBody}`);
|
|
2771
|
-
const now = new Date().toISOString();
|
|
2772
|
-
writeLedger(T, [
|
|
2773
|
-
{ ts: now, doc: 'workflow/intents/2026-10-07-ve.md', stage: 'done', fingerprint: fpI, prev: 'approved' },
|
|
2774
|
-
{ ts: now, doc: 'workflow/plans/2026-10-07-ve.md', stage: 'done', fingerprint: fpP, prev: 'approved' },
|
|
2775
|
-
]);
|
|
2776
|
-
gitCommitAll(T, 'feat: ve fixture');
|
|
2777
|
-
return T;
|
|
2778
|
-
};
|
|
2779
|
-
// ① 无凭证 → warning(advisory,不拦 exit)
|
|
2780
|
-
const T1 = mkVe(false);
|
|
2781
|
-
let r = run(T1);
|
|
2782
|
-
check('检查 8 凭证对账:锚后「测试绿」声明无凭证 → warning 且不 hard',
|
|
2783
|
-
r.status === 0 && outOf(r).includes('测试绿缺凭证') && !outOf(r).includes('证据伪造') && !outOf(r).includes('证据无关'),
|
|
2784
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2785
|
-
rmfix(T1);
|
|
2786
|
-
// ② 有近期绿行 → verify 型豁免出账、零 warning
|
|
2787
|
-
const T2 = mkVe(true);
|
|
2788
|
-
r = run(T2);
|
|
2789
|
-
check('检查 8 凭证对账:24h 内有绿行 → verify 豁免出账且无 warning',
|
|
2790
|
-
r.status === 0 && outOf(r).includes('证据豁免 verify') && !outOf(r).includes('测试绿缺凭证'),
|
|
2791
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2792
|
-
rmfix(T2);
|
|
2793
|
-
}
|
|
2794
|
-
|
|
2795
|
-
// ---- 检查 8 测试绿缺凭证聚合口径(2026-10-18 告警聚合批):判据不变、只改输出形态 ----
|
|
2796
|
-
// ① 单文档多「测试绿」证据项 → 首跑只出一份明细(per-doc 去重,不再逐项刷屏);
|
|
2797
|
-
// ② 同一夹具当日第二次运行 → 明细静默 + 聚合计数行点名(跨运行当日去重——不是不报,是不复读;
|
|
2798
|
-
// 静默必须被计数行点名,防「没输出」被误读为「已修」)。
|
|
2799
|
-
{
|
|
2800
|
-
const mkVeAgg = () => {
|
|
2801
|
-
const T = mkfix();
|
|
2802
|
-
gitInit(T);
|
|
2803
|
-
w(T, '.agents/kit.json', JSON.stringify({ policyVersion: 5 }));
|
|
2804
|
-
const iBody = '# INTENT — vea\n\n## 验收标准(可测试)\n- [x] 用例A通过(证据:测试全绿)\n- [x] 用例B通过(证据:测试全绿)\n';
|
|
2805
|
-
const pBody = '# PLAN — vea\n';
|
|
2806
|
-
const fpI = computeFingerprint(`---\n状态: approved\n级别: L1\n日期: 2026-10-18\n---\n${iBody}`);
|
|
2807
|
-
const fpP = computeFingerprint(`---\n状态: approved\n级别: L1\n---\n${pBody}`);
|
|
2808
|
-
w(T, 'workflow/intents/2026-10-18-vea.md', `---\n状态: done\n级别: L1\n日期: 2026-10-18\n确认指纹: ${fpI.slice(0, 16)}\n---\n${iBody}`);
|
|
2809
|
-
w(T, 'workflow/plans/2026-10-18-vea.md', `---\n状态: done\n级别: L1\n确认指纹: ${fpP.slice(0, 16)}\n---\n${pBody}`);
|
|
2810
|
-
const now = new Date().toISOString();
|
|
2811
|
-
writeLedger(T, [
|
|
2812
|
-
{ ts: now, doc: 'workflow/intents/2026-10-18-vea.md', stage: 'done', fingerprint: fpI, prev: 'approved' },
|
|
2813
|
-
{ ts: now, doc: 'workflow/plans/2026-10-18-vea.md', stage: 'done', fingerprint: fpP, prev: 'approved' },
|
|
2814
|
-
]);
|
|
2815
|
-
gitCommitAll(T, 'feat: vea fixture');
|
|
2816
|
-
return T;
|
|
2817
|
-
};
|
|
2818
|
-
const T = mkVeAgg();
|
|
2819
|
-
let r = run(T);
|
|
2820
|
-
const first = outOf(r).split('\n').filter((l) => l.includes('测试绿缺凭证'));
|
|
2821
|
-
check('检查 8 聚合口径:单文档多「测试绿」项 → 首跑只出一份明细',
|
|
2822
|
-
r.status === 0 && first.length === 1 && first[0].includes('2026-10-18-vea.md'),
|
|
2823
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2824
|
-
r = run(T);
|
|
2825
|
-
const second = outOf(r).split('\n').filter((l) => l.includes('测试绿缺凭证'));
|
|
2826
|
-
check('检查 8 聚合口径:同日二跑 → 明细静默 + 聚合计数行(去重不吞债)',
|
|
2827
|
-
r.status === 0 && second.length === 1 && second[0].includes('另有 1 份在飞文档同日已点名') && second[0].includes('2026-10-18-vea.md'),
|
|
2828
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2829
|
-
rmfix(T);
|
|
2830
|
-
}
|
|
2831
|
-
|
|
2832
|
-
// ---- 检查 8 凭证按 doc 绑定(2026-10-08 verify-doc-binding):v6 收紧 / v5 不变 ----
|
|
2833
|
-
// 漏洞本体:凭证行不带 doc 字段 + 判定只看「全仓任意一份 24h 内绿行」→ 跑一次 verify 就能给全仓
|
|
2834
|
-
// 任意一单的「测试绿」声明背书。本组三条:① v6 + 只有他单绿行 → 仍出 WARN(漏洞已堵);
|
|
2835
|
-
// ② v6 + 本单绿行 → 豁免零 WARN;③ **v5 + 只有他单绿行 → 无 WARN**(兼容反例,收紧只对 v6+ 生效)。
|
|
2836
|
-
{
|
|
2837
|
-
const mkVeDoc = (pv, docField) => {
|
|
2838
|
-
const T = mkfix();
|
|
2839
|
-
gitInit(T);
|
|
2840
|
-
w(T, '.agents/kit.json', JSON.stringify({ policyVersion: pv }));
|
|
2841
|
-
const green = { ts: new Date().toISOString(), exitCode: 0, suite: 'npm test' };
|
|
2842
|
-
if (docField) green.doc = docField;
|
|
2843
|
-
w(T, '.agents/verifications.jsonl', `${JSON.stringify(green)}\n`);
|
|
2844
|
-
const iBody = '# INTENT — ve\n\n## 验收标准(可测试)\n- [x] 用例通过(证据:测试全绿)\n';
|
|
2845
|
-
const pBody = '# PLAN — ve\n';
|
|
2846
|
-
const fpI = computeFingerprint(`---\n状态: approved\n级别: L1\n日期: 2026-10-07\n---\n${iBody}`);
|
|
2847
|
-
const fpP = computeFingerprint(`---\n状态: approved\n级别: L1\n---\n${pBody}`);
|
|
2848
|
-
w(T, 'workflow/intents/2026-10-07-ve.md', `---\n状态: done\n级别: L1\n日期: 2026-10-07\n确认指纹: ${fpI.slice(0, 16)}\n---\n${iBody}`);
|
|
2849
|
-
w(T, 'workflow/plans/2026-10-07-ve.md', `---\n状态: done\n级别: L1\n确认指纹: ${fpP.slice(0, 16)}\n---\n${pBody}`);
|
|
2850
|
-
const now = new Date().toISOString();
|
|
2851
|
-
writeLedger(T, [
|
|
2852
|
-
{ ts: now, doc: 'workflow/intents/2026-10-07-ve.md', stage: 'done', fingerprint: fpI, prev: 'approved' },
|
|
2853
|
-
{ ts: now, doc: 'workflow/plans/2026-10-07-ve.md', stage: 'done', fingerprint: fpP, prev: 'approved' },
|
|
2854
|
-
]);
|
|
2855
|
-
gitCommitAll(T, 'feat: ve doc-binding fixture');
|
|
2856
|
-
return T;
|
|
2857
|
-
};
|
|
2858
|
-
const MINE = 'workflow/intents/2026-10-07-ve.md';
|
|
2859
|
-
const OTHER = 'workflow/intents/2026-10-07-someone-else.md';
|
|
2860
|
-
|
|
2861
|
-
const D1 = mkVeDoc(6, OTHER);
|
|
2862
|
-
let r = run(D1);
|
|
2863
|
-
check('检查8 凭证绑定:v6 + 只有**他单**绿行 → 仍出 WARN(跨单背书漏洞已堵)',
|
|
2864
|
-
r.status === 0 && outOf(r).includes('测试绿缺凭证') && !outOf(r).includes('证据豁免 verify'), `exit=${r.status}\n${outOf(r)}`);
|
|
2865
|
-
rmfix(D1);
|
|
2866
|
-
|
|
2867
|
-
const D2 = mkVeDoc(6, MINE);
|
|
2868
|
-
r = run(D2);
|
|
2869
|
-
check('检查8 凭证绑定:v6 + 本单绿行 → verify 豁免零 WARN(绑定成立)',
|
|
2870
|
-
r.status === 0 && outOf(r).includes('证据豁免 verify') && !outOf(r).includes('测试绿缺凭证'), `exit=${r.status}\n${outOf(r)}`);
|
|
2871
|
-
rmfix(D2);
|
|
2872
|
-
|
|
2873
|
-
const D3 = mkVeDoc(5, OTHER);
|
|
2874
|
-
r = run(D3);
|
|
2875
|
-
check('检查8 凭证绑定:v5 + 只有他单绿行 → **无 WARN**(兼容反例:收紧只对 v6+ 生效)',
|
|
2876
|
-
r.status === 0 && !outOf(r).includes('测试绿缺凭证') && outOf(r).includes('证据豁免 verify'), `exit=${r.status}\n${outOf(r)}`);
|
|
2877
|
-
rmfix(D3);
|
|
2878
|
-
}
|
|
2879
|
-
|
|
2880
|
-
// ---- 检查 18 表头签名识别(2026-10-08 papercuts-cleanup-batch 修 2):节标题漂移不再静默停解析 ----
|
|
2881
|
-
// 修前按 `## 委派结果|自做任务结果` 精确节标题切段,装户把标题写成「委派结果表」即静默停解析该表
|
|
2882
|
-
// → L2/L3 done 回流无告警;修后按表头签名(与 agg-delegations.cjs splitTables 同口径)识别。
|
|
2883
|
-
{
|
|
2884
|
-
const T = mkfix();
|
|
2885
|
-
const gap = '2026-09-12-drift.md';
|
|
2886
|
-
w(T, `workflow/intents/${gap}`, INTENT('drift', '状态: done\n级别: L2\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
2887
|
-
w(T, `workflow/specs/${gap}`, SPEC('drift', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2888
|
-
w(T, `workflow/plans/${gap}`, PLAN('drift', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
2889
|
-
const ledgerWarn = (r) => outOf(r).split('\n').some((l) => l.includes('委派台账') && l.includes(`intents/${gap}`));
|
|
2890
|
-
// 正例:节标题漂移(「委派结果表」≠精确节名)而表头不动 → 仍按表头签名解析,警告消失
|
|
2891
|
-
w(T, 'workflow/delegations.md', '## 委派结果表\n\n| 日期 | 被委派方 | 任务一句话 | 结果 | 备注 |\n|------|----------|------------|------|------|\n| 2026-09-12 | x | y | 一次通过 | 2026-09-12-drift |\n');
|
|
2892
|
-
let r = run(T);
|
|
2893
|
-
check('检查18 节标题漂移而表头不动 → 仍按表头签名解析,警告消失(修 2 核心场景)',
|
|
2894
|
-
r.status === 0 && !ledgerWarn(r), `exit=${r.status}\n${outOf(r)}`);
|
|
2895
|
-
// 负例:表头也漂移(新格式无签名列)→ 数据行不收集 → 警告仍在(识别不过宽)
|
|
2896
|
-
w(T, 'workflow/delegations.md', '## 委派结果表\n\n| 日期 | 承办 | 说明 |\n|------|------|------|\n| 2026-09-12 | x | 2026-09-12-drift |\n');
|
|
2897
|
-
r = run(T);
|
|
2898
|
-
check('检查18 表头也漂移(无签名列)→ 不收集 → 警告仍在(识别不过宽负例)',
|
|
2899
|
-
r.status === 0 && ledgerWarn(r), `exit=${r.status}\n${outOf(r)}`);
|
|
2900
|
-
rmfix(T);
|
|
2901
|
-
}
|
|
2902
|
-
|
|
2903
|
-
// ---- 检查 14 台账 OR(2026-10-08 papercuts-cleanup-batch 修 3):git 历史无 approved 行而台账有 → 不再误报 ----
|
|
2904
|
-
// 两跳确认同批提交时 git 历史不出现行首「状态: approved」(2026-09-28-adopter-derivers 实证)——
|
|
2905
|
-
// confirmations.jsonl 已证明两跳均走;判据改 OR 并集(git 判据保留,台账为第二通道)。
|
|
2906
|
-
{
|
|
2907
|
-
const T = mkfix();
|
|
2908
|
-
gitInit(T);
|
|
2909
|
-
const base = '2026-09-23-confirm-ledger';
|
|
2910
|
-
w(T, `workflow/incidents/${base}.md`, `---\n状态: closed\n级别: L1\n发现: 2026-09-23\n模块: material\n---\n# INCIDENT — confirm ledger\n\n${三件套(false)}`);
|
|
2911
|
-
w(T, `workflow/plans/${base}.md`, PLAN('confirm ledger', '状态: done\n级别: L1\n模块: material'));
|
|
2912
|
-
gitCommitAll(T, 'test'); // 两跳同批:git 历史无行首 approved(修前必 WARN)
|
|
2913
|
-
w(T, '.agents/confirmations.jsonl', `${JSON.stringify({ ts: '2026-09-23T00:00:00Z', doc: `workflow/plans/${base}.md`, stage: 'approved', fingerprint: 'x', prev: 'draft', source: 'tty' })}\n`);
|
|
2914
|
-
let r = run(T);
|
|
2915
|
-
// exit 1 属检查 15 指纹 hard 同场(台账有该 doc 行即受管,fixture 无真实指纹必拦——预期);
|
|
2916
|
-
// 本场景只断言检查 14 的 WARN 确认态缺失不再出账(台账 OR 兜住)
|
|
2917
|
-
check('检查14 git 历史无 approved 行而台账有 → 台账 OR 兜住不误报(修 3 核心场景)',
|
|
2918
|
-
!outOf(r).includes('WARN 确认态缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
2919
|
-
// 台账行 doc 不指向该文档 → 仍 WARN(OR 不弱化原判据)
|
|
2920
|
-
w(T, '.agents/confirmations.jsonl', `${JSON.stringify({ ts: '2026-09-23T00:00:00Z', doc: 'workflow/plans/other.md', stage: 'approved', fingerprint: 'x', prev: 'draft', source: 'tty' })}\n`);
|
|
2921
|
-
r = run(T);
|
|
2922
|
-
check('检查14 台账无该文档 approved 行 → 仍 WARN(git 判据未被弱化)',
|
|
2923
|
-
r.status === 0 && /WARN 确认态缺失.*2026-09-23-confirm-ledger\.md/.test(outOf(r)), outOf(r));
|
|
2924
|
-
rmfix(T);
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
// ---- 检查 4 全角逗号(2026-10-08 papercuts-cleanup-batch 修 4):路径引用后跟「,」不再吞入引用串 ----
|
|
2928
|
-
// 中文文档「`.agents/...`,以及…」写法曾把「,」并入引用 → 引用断档误报(p0-gate-noise-batch 实证 2 条)。
|
|
2929
|
-
{
|
|
2930
|
-
const T = mkfix();
|
|
2931
|
-
fs.mkdirSync(path.join(T, '.agents', 'commands'), { recursive: true });
|
|
2932
|
-
w(T, '.agents/commands/plan.md', '# 小命令\n');
|
|
2933
|
-
w(T, 'workflow/intents/2026-09-12-ref-comma.md', INTENT('ref comma', '状态: approved\n级别: L1\n日期: 2026-09-12\n模块: pipeline', '\n入口 `.agents/commands/plan.md`,以及后续步骤。\n'));
|
|
2934
|
-
w(T, 'workflow/plans/2026-09-12-ref-comma.md', PLAN('ref comma', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
2935
|
-
let r = run(T);
|
|
2936
|
-
check('检查4 路径后跟全角逗号 → 引用串不吞「,」,真实路径不误报断档',
|
|
2937
|
-
r.status === 0 && !outOf(r).includes('引用断档'), `exit=${r.status}\n${outOf(r)}`);
|
|
2938
|
-
w(T, 'workflow/intents/2026-09-12-ref-comma2.md', INTENT('ref comma2', '状态: approved\n级别: L1\n日期: 2026-09-12\n模块: pipeline', '\n入口 `.agents/commands/missing-broken.md`,后续。\n'));
|
|
2939
|
-
w(T, 'workflow/plans/2026-09-12-ref-comma2.md', PLAN('ref comma2', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
2940
|
-
r = run(T);
|
|
2941
|
-
// 负例用 ASCII 断档名:refRe 字符类本就只含 ASCII 路径字符(中文名路径不在扫描范围)
|
|
2942
|
-
check('检查4 全角逗号后的真断档 → 仍拦(判据不放松)',
|
|
2943
|
-
r.status === 0 && /引用断档.*missing-broken\.md/.test(outOf(r)), `exit=${r.status}\n${outOf(r)}`);
|
|
2944
|
-
rmfix(T);
|
|
2945
|
-
}
|
|
2946
|
-
|
|
2947
|
-
// ---- 门禁 ROI 插桩(2026-10-08 gate-roi-metrics)----
|
|
2948
|
-
// 断言两件事:① 默认路径不落盘、stderr 无 gate-stats 行(输出契约零污染);
|
|
2949
|
-
// ② --gate-stats 落一行 jsonl 且段覆盖全部检查(判据不漏插)。
|
|
2950
|
-
// 既有 223 条断言零改动本身即最强钉子——任何 push 到 warnings 的插桩副作用都会把它们打红。
|
|
2951
|
-
{
|
|
2952
|
-
const T = mkfix();
|
|
2953
|
-
const statsRel = path.join('.agents', 'cache', 'gate-stats.jsonl');
|
|
2954
|
-
const plain = run(T);
|
|
2955
|
-
check('门禁ROI:默认路径不落 gate-stats.jsonl(统计默认静默)',
|
|
2956
|
-
!fs.existsSync(path.join(T, statsRel)), outOf(plain));
|
|
2957
|
-
check('门禁ROI:默认路径 stderr 无 [gate-stats] 摘要行(稳定输出契约零污染)',
|
|
2958
|
-
!outOf(plain).includes('[gate-stats]'), outOf(plain));
|
|
2959
|
-
const before = fs.existsSync(path.join(T, statsRel)) ? fs.readFileSync(path.join(T, statsRel), 'utf8') : '';
|
|
2960
|
-
|
|
2961
|
-
// --all 显式带上(2026-10-13 分道:ROI 采集要全段成本面;裸 --gate-stats 只记硬门段)
|
|
2962
|
-
const st = run(T, { args: ['--gate-stats', '--all'] });
|
|
2963
|
-
check('门禁ROI:--gate-stats → 落盘 + stderr 摘要行', fs.existsSync(path.join(T, statsRel)) && outOf(st).includes('[gate-stats]'), outOf(st));
|
|
2964
|
-
const lines = fs.readFileSync(path.join(T, statsRel), 'utf8').split('\n').filter(Boolean);
|
|
2965
|
-
check('门禁ROI:--gate-stats 只 append 一行(每跑一次门禁 = 一条记录)',
|
|
2966
|
-
lines.length === 1 && !before, `lines=${lines.length} before=${before.length}`);
|
|
2967
|
-
let row = null;
|
|
2968
|
-
try { row = JSON.parse(lines[0]); } catch { /* 留给下一条断言报错 */ }
|
|
2969
|
-
const ids = row && Array.isArray(row.segs) ? row.segs.map((s) => String(s.id)) : [];
|
|
2970
|
-
const uniq = new Set(ids);
|
|
2971
|
-
check('门禁ROI:段 id 唯一且覆盖 26 个插桩点(14 主流程 + 5 hygiene 模块 + 3x/21-26 六段),无重复插桩',
|
|
2972
|
-
ids.length === 26 && uniq.size === 26, `ids=${ids.join(',')}`);
|
|
2973
|
-
const expected = ['1','2','3','3x','4','5','6','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26'];
|
|
2974
|
-
check('门禁ROI:段 id 集合 == 现有检查编号+3x(不新增/不遗漏检查项编号)',
|
|
2975
|
-
expected.every((e) => uniq.has(e)) && uniq.size === expected.length, `got=${[...uniq].sort().join(',')}`);
|
|
2976
|
-
check('门禁ROI:每段都有 label + 非负 ms(成本度量的地基)',
|
|
2977
|
-
row && row.segs.every((s) => s.label && Number(s.ms) >= 0 && Number.isInteger(Number(s.ms))), JSON.stringify(row && row.segs.slice(0, 3)));
|
|
2978
|
-
check('门禁ROI:行顶层含 ts / root / totalMs(聚合器的窗口过滤依赖 ts)',
|
|
2979
|
-
row && typeof row.ts === 'string' && typeof row.root === 'string' && Number(row.totalMs) >= 0, JSON.stringify({ ts: row && row.ts, totalMs: row && row.totalMs }));
|
|
2980
|
-
rmfix(T);
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
// ---- 场景 21:台账哈希链(2026-10-09 engine-quality-round2 D)——篡改必抓 / 存量零告警 ----
|
|
2984
|
-
{
|
|
2985
|
-
// 21a:连续带 hash 行中间被篡改(改 quote 不动链字段)→ hard「台账链断裂」
|
|
2986
|
-
const T = mkfix();
|
|
2987
|
-
const { ledgerChainHash } = await import('./policy.mjs');
|
|
2988
|
-
const chainRow = (prevHash, fields) => {
|
|
2989
|
-
const row = { ...fields, prevHash };
|
|
2990
|
-
return { ...row, hash: ledgerChainHash(fields, prevHash) };
|
|
2991
|
-
};
|
|
2992
|
-
const row1 = chainRow('', { ts: '2026-10-09T01:00:00Z', doc: 'workflow/intents/2026-10-09-a.md', stage: 'approved', fingerprint: 'a'.repeat(64), prev: 'draft', source: 'chat-delegated', quote: '真原话一' });
|
|
2993
|
-
const row2 = chainRow(row1.hash, { ts: '2026-10-09T02:00:00Z', doc: 'workflow/intents/2026-10-09-a.md', stage: 'done', fingerprint: 'b'.repeat(64), prev: 'approved', source: 'chat-delegated', quote: '真原话二' });
|
|
2994
|
-
// 篡改第一行 quote(链字段不动 → hash 失配)
|
|
2995
|
-
const tampered = { ...row1, quote: '篡改后的假原话' };
|
|
2996
|
-
writeLedger(T, [tampered, row2]);
|
|
2997
|
-
let r = run(T);
|
|
2998
|
-
let out = outOf(r);
|
|
2999
|
-
// 定位措辞随 2026-10-12 ledger-shard-write 由「第 N 行(物理)」改为 `rel:行号`——
|
|
3000
|
-
// 台账视图含主台账 + 分片后,单一行号跨来源无法定位,必须带来源。首断行号仍须精确指向第 1 行。
|
|
3001
|
-
check('场景 21a 链断裂:中间行篡改 → hard 台账链断裂(指首断行号)',
|
|
3002
|
-
r.status !== 0 && out.includes('台账链断裂') && /\.agents\/confirmations\.jsonl:1\b/.test(out),
|
|
3003
|
-
`exit=${r.status}\n${out.slice(-400)}`);
|
|
3004
|
-
// 反证还原:篡改行恢复原 quote → 链复通(断言非恒绿)
|
|
3005
|
-
writeLedger(T, [row1, row2]);
|
|
3006
|
-
r = run(T);
|
|
3007
|
-
check('场景 21b 反证还原:篡改修复 → 链校验通过', !outOf(r).includes('台账链断裂'), outOf(r).slice(-300));
|
|
3008
|
-
rmfix(T);
|
|
3009
|
-
}
|
|
3010
|
-
{
|
|
3011
|
-
// 21c:全无 hash 的存量台账(v1.5.0 前形态)→ 零链告警(向后兼容)
|
|
3012
|
-
const T = mkfix();
|
|
3013
|
-
writeLedger(T, [
|
|
3014
|
-
{ ts: '2026-09-20T01:00:00Z', doc: 'workflow/intents/2026-09-20-legacy.md', stage: 'approved', fingerprint: 'c'.repeat(64), prev: 'draft' },
|
|
3015
|
-
{ ts: '2026-09-20T02:00:00Z', doc: 'workflow/intents/2026-09-20-legacy.md', stage: 'done', fingerprint: 'd'.repeat(64), prev: 'approved' },
|
|
3016
|
-
]);
|
|
3017
|
-
const r = run(T);
|
|
3018
|
-
check('场景 21c 存量无 hash 行:零链告警(不追溯)', !outOf(r).includes('台账链断裂'), outOf(r).slice(-300));
|
|
3019
|
-
rmfix(T);
|
|
3020
|
-
}
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
// ---- 场景 23:台账 scope 子项(2026-10-09 审查 A1 干净版)——缺 scope 出账 / 摘要失配 hard / 存量与旧 policy 零告警 ----
|
|
3024
|
-
// 背景:委托代录的授权凭据原是「用户原话」(自由文本、AI 可零成本复用)。干净版改记机器派生的
|
|
3025
|
-
// scope{files,sha256}(AI 无输入面),本子项校验受管行带该字段且摘要自洽。
|
|
3026
|
-
{
|
|
3027
|
-
const AFTER = '2026-10-09T12:00:00Z'; // ≥ policy v7 scopeSince(生效时刻锚)
|
|
3028
|
-
const BEFORE = '2026-10-01T00:00:00Z'; // 存量(生效前):豁免、不追溯
|
|
3029
|
-
const { scopeDigest } = await import('./policy.mjs');
|
|
3030
|
-
|
|
3031
|
-
// 23a:受管行缺 scope → advisory 出账(灰度第一档,不阻断)
|
|
3032
|
-
const Ta = mkfix();
|
|
3033
|
-
w(Ta, '.agents/kit.json', '{"policyVersion":7}\n');
|
|
3034
|
-
writeLedger(Ta, [{ ts: AFTER, doc: 'workflow/intents/2026-10-09-a.md', stage: 'approved', fingerprint: 'a'.repeat(64), prev: 'draft', source: 'chat-delegated', quote: '可以' }]);
|
|
3035
|
-
const ra = run(Ta);
|
|
3036
|
-
check('场景 23a 缺机器改动面:受管行无 scope → WARN 出账且不阻断',
|
|
3037
|
-
ra.status === 0 && outOf(ra).includes('缺机器改动面'), `exit=${ra.status}\n${outOf(ra).slice(-400)}`);
|
|
3038
|
-
rmfix(Ta);
|
|
3039
|
-
|
|
3040
|
-
// 23b:scope 存在但摘要与 files 重算不符(「改了 files 没改摘要」)→ hard 阻断
|
|
3041
|
-
const Tb = mkfix();
|
|
3042
|
-
w(Tb, '.agents/kit.json', '{"policyVersion":7}\n');
|
|
3043
|
-
const good = scopeDigest(['workflow/intents/2026-10-09-b.md', 'src/x.mjs']);
|
|
3044
|
-
writeLedger(Tb, [{ ts: AFTER, doc: 'workflow/intents/2026-10-09-b.md', stage: 'approved', fingerprint: 'b'.repeat(64), prev: 'draft', source: 'chat-delegated', quote: '可以', scope: { ...good, files: [...good.files, 'src/悄悄加的.mjs'] } }]);
|
|
3045
|
-
const rb = run(Tb);
|
|
3046
|
-
check('场景 23b scope 摘要失配:files 被改未同步 sha256 → hard 阻断',
|
|
3047
|
-
rb.status !== 0 && outOf(rb).includes('台账 scope 失配'), `exit=${rb.status}\n${outOf(rb).slice(-400)}`);
|
|
3048
|
-
rmfix(Tb);
|
|
3049
|
-
|
|
3050
|
-
// 23c:scope 完整自洽 → 零 scope 告警(断言非恒红)
|
|
3051
|
-
const Tc = mkfix();
|
|
3052
|
-
w(Tc, '.agents/kit.json', '{"policyVersion":7}\n');
|
|
3053
|
-
writeLedger(Tc, [{ ts: AFTER, doc: 'workflow/intents/2026-10-09-c.md', stage: 'approved', fingerprint: 'c'.repeat(64), prev: 'draft', source: 'chat-delegated', quote: '可以', scope: scopeDigest(['workflow/intents/2026-10-09-c.md']) }]);
|
|
3054
|
-
const rc = run(Tc);
|
|
3055
|
-
check('场景 23c scope 自洽:零 scope 告警(断言非恒红)',
|
|
3056
|
-
!outOf(rc).includes('缺机器改动面') && !outOf(rc).includes('台账 scope 失配'), outOf(rc).slice(-300));
|
|
3057
|
-
rmfix(Tc);
|
|
3058
|
-
|
|
3059
|
-
// 23d:存量行(ts < scopeSince)缺 scope → 零告警(生效日锚不追溯,不产不可消除噪声)
|
|
3060
|
-
const Td = mkfix();
|
|
3061
|
-
w(Td, '.agents/kit.json', '{"policyVersion":7}\n');
|
|
3062
|
-
writeLedger(Td, [{ ts: BEFORE, doc: 'workflow/intents/2026-10-01-d.md', stage: 'approved', fingerprint: 'd'.repeat(64), prev: 'draft', source: 'chat-delegated', quote: '可以' }]);
|
|
3063
|
-
const rd = run(Td);
|
|
3064
|
-
check('场景 23d 存量豁免:ts < scopeSince 的行缺 scope → 零告警(不追溯)',
|
|
3065
|
-
!outOf(rd).includes('缺机器改动面'), outOf(rd).slice(-300));
|
|
3066
|
-
rmfix(Td);
|
|
3067
|
-
|
|
3068
|
-
// 23e:旧 policy(v6 无 scopeSince 键)→ 子项整体跳过,存量 406 行零新增告警
|
|
3069
|
-
const Te = mkfix();
|
|
3070
|
-
w(Te, '.agents/kit.json', '{"policyVersion":6}\n');
|
|
3071
|
-
writeLedger(Te, [{ ts: AFTER, doc: 'workflow/intents/2026-10-09-e.md', stage: 'approved', fingerprint: 'e'.repeat(64), prev: 'draft', source: 'chat-delegated', quote: '可以' }]);
|
|
3072
|
-
const re = run(Te);
|
|
3073
|
-
check('场景 23e 旧 policy 跳过:v6 无 scopeSince 键 → 子项整体跳过(装户零新增告警)',
|
|
3074
|
-
!outOf(re).includes('缺机器改动面') && !outOf(re).includes('台账 scope 失配'), outOf(re).slice(-300));
|
|
3075
|
-
rmfix(Te);
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
// ---- 场景 22:addedDates 持久缓存(2026-10-09 engine-quality-round3 W1)----
|
|
3079
|
-
{
|
|
3080
|
-
const log = '@2026-10-02T10:00:00+08:00\nworkflow/intents/a.md\nworkflow/plans/a.md\n@2026-10-01T09:00:00+08:00\nworkflow/intents/a.md\n';
|
|
3081
|
-
const m = parseAddedDatesLog(log);
|
|
3082
|
-
check('场景 22a 提取:新→旧序首遇 = 最新一次加入(原 IIFE 语义零复刻;注释「最早」为历史误述)', m.get('workflow/intents/a.md') === '2026-10-02T10:00:00+08:00' && m.get('workflow/plans/a.md') === '2026-10-02T10:00:00+08:00', JSON.stringify([...m]));
|
|
3083
|
-
}
|
|
3084
|
-
{
|
|
3085
|
-
const cacheP = path.join(os.tmpdir(), 'adc-' + Date.now() + '.json');
|
|
3086
|
-
let gitLogCalls = 0;
|
|
3087
|
-
const gitLog = () => { gitLogCalls++; return '@2026-10-01T09:00:00+08:00\nworkflow/intents/a.md\n'; };
|
|
3088
|
-
const head = 'head-sha-1';
|
|
3089
|
-
const m1 = addedDatesWithCache({ head, gitLog, cachePath: cacheP });
|
|
3090
|
-
const m2 = addedDatesWithCache({ head, gitLog, cachePath: cacheP });
|
|
3091
|
-
check('场景 22b 缓存命中:同 HEAD 二次调用 gitLog 仅 1 次且结果一致',
|
|
3092
|
-
gitLogCalls === 1 && m1.get('workflow/intents/a.md') === m2.get('workflow/intents/a.md'), 'calls=' + gitLogCalls);
|
|
3093
|
-
const m3 = addedDatesWithCache({ head: 'head-sha-2', gitLog, cachePath: cacheP });
|
|
3094
|
-
check('场景 22c HEAD 变更:重算(gitLog 计 2 次)', gitLogCalls === 2 && m3.get('workflow/intents/a.md') === '2026-10-01T09:00:00+08:00', 'calls=' + gitLogCalls);
|
|
3095
|
-
fs.writeFileSync(cacheP, '{broken json');
|
|
3096
|
-
const before = gitLogCalls;
|
|
3097
|
-
const m4 = addedDatesWithCache({ head: 'head-sha-2', gitLog, cachePath: cacheP });
|
|
3098
|
-
check('场景 22d 缓存损坏:fail-open 全算(gitLog 再调 1 次)且结果正确',
|
|
3099
|
-
gitLogCalls === before + 1 && m4.get('workflow/intents/a.md') === '2026-10-01T09:00:00+08:00', 'calls=' + gitLogCalls);
|
|
3100
|
-
const before2 = gitLogCalls;
|
|
3101
|
-
addedDatesWithCache({ head: null, gitLog, cachePath: cacheP });
|
|
3102
|
-
check('场景 22e 无 head:不读缓存直接全算(gitLog 再调 1 次)', gitLogCalls === before2 + 1, 'calls=' + gitLogCalls);
|
|
3103
|
-
fs.rmSync(cacheP, { force: true });
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3106
|
-
// ---- 场景 23:场景覆盖子判据(2026-10-10 scenario-coverage,灰度 warning)----
|
|
3107
|
-
// scenarioIdsOf 纯函数 + 端到端(spec 有「验收场景」节 → done intent 须逐场景引用;缺 = warning 不阻断)
|
|
3108
|
-
{
|
|
3109
|
-
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'scen-ids-'));
|
|
3110
|
-
fs.writeFileSync(path.join(d, 's1.md'),
|
|
3111
|
-
'---\n状态: draft\n---\n# SPEC\n## 功能行为\n### S0 不该被提取(节外)\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n- WHEN y\n- THEN z\n### S2 退款\n- GIVEN a\n## 数据流\n### S3 也不提取(节外)\n');
|
|
3112
|
-
const ids = scenarioIdsOf(path.join(d, 's1.md'));
|
|
3113
|
-
check('场景 24a scenarioIdsOf:只提「验收场景」节内 ID,按序去重', ids.join(',') === 'S1,S2', JSON.stringify(ids));
|
|
3114
|
-
fs.writeFileSync(path.join(d, 's2.md'), '---\n状态: draft\n---\n# SPEC\n## 功能行为\n无场景节\n');
|
|
3115
|
-
check('场景 24b scenarioIdsOf:无「验收场景」节 → 空数组', scenarioIdsOf(path.join(d, 's2.md')).length === 0);
|
|
3116
|
-
check('场景 24c scenarioIdsOf:文件不可读 → 空数组(fail-open)', scenarioIdsOf(path.join(d, 'nope.md')).length === 0);
|
|
3117
|
-
fs.rmSync(d, { recursive: true, force: true });
|
|
3118
|
-
}
|
|
3119
|
-
{
|
|
3120
|
-
// 端到端:spec 两场景 + intent 全引用 → exit 0 且无「场景未覆盖」warning
|
|
3121
|
-
const T = mkfix();
|
|
3122
|
-
w(T, 'workflow/specs/2026-09-12-sc.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — sc\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n- WHEN y\n- THEN z\n### S2 退款\n- GIVEN a\n');
|
|
3123
|
-
w(T, 'workflow/intents/2026-09-12-sc.md', INTENT('sc', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3124
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1 折扣生效(证据:冒烟 checkout.json)\n- [x] 场景:S2 退款到账(证据:refund.test.js)\n'));
|
|
3125
|
-
w(T, 'workflow/plans/2026-09-12-sc.md', PLAN('sc', '状态: done\n级别: L1'));
|
|
3126
|
-
const r = run(T);
|
|
3127
|
-
check('场景 24d spec 有场景且 intent 全引用 → exit 0 无告警', r.status === 0 && !outOf(r).includes('场景未覆盖'), `exit=${r.status}\n${outOf(r).slice(0, 400)}`);
|
|
3128
|
-
}
|
|
3129
|
-
{
|
|
3130
|
-
// 端到端:缺 S2 引用 → warning(灰度,不阻断:exit 仍 0)
|
|
3131
|
-
const T = mkfix();
|
|
3132
|
-
w(T, 'workflow/specs/2026-09-12-sc.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — sc\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n### S2 退款\n- GIVEN a\n');
|
|
3133
|
-
w(T, 'workflow/intents/2026-09-12-sc.md', INTENT('sc', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3134
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1 折扣生效(证据:冒烟 checkout.json)\n'));
|
|
3135
|
-
w(T, 'workflow/plans/2026-09-12-sc.md', PLAN('sc', '状态: done\n级别: L1'));
|
|
3136
|
-
const r = run(T);
|
|
3137
|
-
check('场景 24e 缺场景引用 → warning 且不阻断(exit 0)',
|
|
3138
|
-
r.status === 0 && outOf(r).includes('场景未覆盖') && outOf(r).includes('缺 S2'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3139
|
-
}
|
|
3140
|
-
{
|
|
3141
|
-
// 端到端:spec 无场景节 → 零变化(不出现「场景未覆盖」)
|
|
3142
|
-
const T = mkfix();
|
|
3143
|
-
w(T, 'workflow/specs/2026-09-12-sc.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — sc\n## 功能行为\n普通 spec\n');
|
|
3144
|
-
w(T, 'workflow/intents/2026-09-12-sc.md', INTENT('sc', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3145
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n'));
|
|
3146
|
-
w(T, 'workflow/plans/2026-09-12-sc.md', PLAN('sc', '状态: done\n级别: L1'));
|
|
3147
|
-
const r = run(T);
|
|
3148
|
-
check('场景 24f spec 无场景节 → 零变化(无场景告警,exit 0)', r.status === 0 && !outOf(r).includes('场景未覆盖'), `exit=${r.status}\n${outOf(r).slice(0, 400)}`);
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
// ---- 场景 25:沉淀追踪子判据(2026-10-10 sediment-tracking,灰度 warning)----
|
|
3152
|
-
// sectionHasBody 纯函数 + 端到端(done intent 含影响面/触达红线 且 frontmatter 无「沉淀」→ WARN 未沉淀;
|
|
3153
|
-
// incident closed 且三件套齐全 → 同款;「沉淀: 无」豁免;路径断档 → WARN 沉淀断档)
|
|
3154
|
-
{
|
|
3155
|
-
check('场景 25a sectionHasBody:标题后有实质行 → true',
|
|
3156
|
-
sectionHasBody(['## 影响面', '- 页面 A', ''], '影响面') === true);
|
|
3157
|
-
check('场景 25b sectionHasBody:光标题(模板未填)→ false',
|
|
3158
|
-
sectionHasBody(['## 影响面', '<页面 / 接口 / 数据范围>', ''], '影响面') === false);
|
|
3159
|
-
check('场景 25c sectionHasBody:无该节 → false',
|
|
3160
|
-
sectionHasBody(['## 目标', '做 X'], '影响面') === false);
|
|
3161
|
-
check('场景 25d sectionHasBody:前缀匹配 + 节外有同名行不算',
|
|
3162
|
-
sectionHasBody(['## 目标', '提影响面', '## 影响面与边界', '- 内容'], '影响面') === true);
|
|
3163
|
-
}
|
|
3164
|
-
{
|
|
3165
|
-
// intent 侧:done + 影响面有内容 + 无沉淀字段 → WARN 未沉淀(不阻断)
|
|
3166
|
-
const T = mkfix();
|
|
3167
|
-
w(T, 'workflow/intents/2026-09-12-sed.md', INTENT('sed', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3168
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n## 影响面\n- 页面 A / 接口 B\n'));
|
|
3169
|
-
w(T, 'workflow/plans/2026-09-12-sed.md', PLAN('sed', '状态: done\n级别: L1'));
|
|
3170
|
-
const r = run(T);
|
|
3171
|
-
check('场景 25e done intent 高价值无沉淀 → WARN 未沉淀且不阻断',
|
|
3172
|
-
r.status === 0 && outOf(r).includes('未沉淀') && outOf(r).includes('2026-09-12-sed.md'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3173
|
-
}
|
|
3174
|
-
{
|
|
3175
|
-
// 「沉淀: 无」显式豁免 → 无未沉淀告警
|
|
3176
|
-
const T = mkfix();
|
|
3177
|
-
w(T, 'workflow/intents/2026-09-12-sed.md', INTENT('sed', '状态: done\n级别: L1\n日期: 2026-09-12\n沉淀: 无',
|
|
3178
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n## 影响面\n- 页面 A\n'));
|
|
3179
|
-
w(T, 'workflow/plans/2026-09-12-sed.md', PLAN('sed', '状态: done\n级别: L1'));
|
|
3180
|
-
const r = run(T);
|
|
3181
|
-
check('场景 25f 「沉淀: 无」显式豁免 → 无未沉淀告警',
|
|
3182
|
-
r.status === 0 && !outOf(r).includes('未沉淀'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3183
|
-
}
|
|
3184
|
-
{
|
|
3185
|
-
// 沉淀字段指向不存在的文件 → WARN 沉淀断档
|
|
3186
|
-
const T = mkfix();
|
|
3187
|
-
w(T, 'workflow/intents/2026-09-12-sed.md', INTENT('sed', '状态: done\n级别: L1\n日期: 2026-09-12\n沉淀: wiki/项目规范/不存在.md',
|
|
3188
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n## 影响面\n- 页面 A\n'));
|
|
3189
|
-
w(T, 'workflow/plans/2026-09-12-sed.md', PLAN('sed', '状态: done\n级别: L1'));
|
|
3190
|
-
const r = run(T);
|
|
3191
|
-
check('场景 25g 沉淀字段指向缺失文件 → WARN 沉淀断档',
|
|
3192
|
-
r.status === 0 && outOf(r).includes('沉淀断档') && !outOf(r).includes('未沉淀'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3193
|
-
}
|
|
3194
|
-
{
|
|
3195
|
-
// intent 无影响面/触达红线 → 零变化(不出现未沉淀告警)
|
|
3196
|
-
const T = mkfix();
|
|
3197
|
-
w(T, 'workflow/intents/2026-09-12-sed.md', INTENT('sed', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3198
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n'));
|
|
3199
|
-
w(T, 'workflow/plans/2026-09-12-sed.md', PLAN('sed', '状态: done\n级别: L1'));
|
|
3200
|
-
const r = run(T);
|
|
3201
|
-
check('场景 25h intent 无高价值节 → 零变化', r.status === 0 && !outOf(r).includes('未沉淀'), `exit=${r.status}\n${outOf(r).slice(0, 400)}`);
|
|
3202
|
-
}
|
|
3203
|
-
{
|
|
3204
|
-
// incident 侧:closed 且三件套齐全 且无沉淀 → WARN 未沉淀
|
|
3205
|
-
const T = mkfix();
|
|
3206
|
-
w(T, 'workflow/incidents/2026-09-12-inc.md',
|
|
3207
|
-
`---\n状态: closed\n级别: L1\n发现: 2026-09-12\n---\n# INCIDENT — inc\n## 时间线\n- 用户确认:草稿过目通过(2026-09-12)\n## 复盘三件套(缺一不可)\n\n${三件套(false)}`);
|
|
3208
|
-
w(T, 'workflow/plans/2026-09-12-inc.md', PLAN('inc', '状态: done\n级别: L1'));
|
|
3209
|
-
const r = run(T);
|
|
3210
|
-
check('场景 25i closed incident 三件套齐全无沉淀 → WARN 未沉淀',
|
|
3211
|
-
r.status === 0 && outOf(r).includes('未沉淀') && outOf(r).includes('2026-09-12-inc.md'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3212
|
-
}
|
|
3213
|
-
{
|
|
3214
|
-
// incident 三件套不全 → 沉淀子判据不触发(不出未沉淀,只出三件套不全)
|
|
3215
|
-
const T = mkfix();
|
|
3216
|
-
w(T, 'workflow/incidents/2026-09-12-inc.md',
|
|
3217
|
-
`---\n状态: closed\n级别: L1\n发现: 2026-09-12\n---\n# INCIDENT — inc\n## 时间线\n- 用户确认:草稿过目通过(2026-09-12)\n## 复盘三件套(缺一不可)\n\n1. 结构性修复\n - 修复 commit:abc1234\n - 是否需要新 intent:\n - 否 → 理由:单点修复\n`);
|
|
3218
|
-
w(T, 'workflow/plans/2026-09-12-inc.md', PLAN('inc', '状态: done\n级别: L1'));
|
|
3219
|
-
const r = run(T);
|
|
3220
|
-
check('场景 25j incident 三件套不全 → 只告三件套不全,不出未沉淀',
|
|
3221
|
-
r.status === 0 && outOf(r).includes('三件套不全') && !outOf(r).includes('未沉淀'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
// ---- 场景 25 续:落点真实性(2026-10-11 landing-reality,P1-3,灰度 warning)----
|
|
3225
|
-
// sedimentLandingIsShell 纯函数 + 端到端(落点只有标题/占位 → WARN 落点空壳;带「来源:」frontmatter
|
|
3226
|
-
// 或 ≥3 行非占位正文 → 不出;不存在 → 仍 WARN 沉淀断档(回归);指向目录(读取失败)→ 空壳并注明读取失败)
|
|
3227
|
-
{
|
|
3228
|
-
check('场景 25k sedimentLandingIsShell:只有标题+占位(空壳)→ true',
|
|
3229
|
-
sedimentLandingIsShell('---\n标题: 某主题\n---\n# 某主题\n<状态:待整理>\n') === true);
|
|
3230
|
-
check('场景 25l sedimentLandingIsShell:frontmatter 有「来源:」→ false',
|
|
3231
|
-
sedimentLandingIsShell('---\n来源: workflow/incidents/2026-09-12-inc.md\n---\n# 某主题\n') === false);
|
|
3232
|
-
check('场景 25m sedimentLandingIsShell:非占位正文 ≥3 行 → false',
|
|
3233
|
-
sedimentLandingIsShell('# 某主题\n- 要点一\n- 要点二\n- 要点三\n') === false);
|
|
3234
|
-
check('场景 25n sedimentLandingIsShell:非占位正文仅 2 行 → true',
|
|
3235
|
-
sedimentLandingIsShell('# 某主题\n- 要点一\n- 要点二\n') === true);
|
|
3236
|
-
check('场景 25o sedimentLandingIsShell:读取失败(null)→ true',
|
|
3237
|
-
sedimentLandingIsShell(null) === true);
|
|
3238
|
-
}
|
|
3239
|
-
{
|
|
3240
|
-
// incident 侧:closed + 三件套齐全 + 沉淀指向只有标题/占位的文件 → WARN 落点空壳(不阻断)
|
|
3241
|
-
const T = mkfix();
|
|
3242
|
-
w(T, 'workflow/incidents/2026-09-12-inc.md',
|
|
3243
|
-
`---\n状态: closed\n级别: L1\n发现: 2026-09-12\n沉淀: wiki/项目规范/shell.md\n---\n# INCIDENT — inc\n## 时间线\n- 用户确认:草稿过目通过(2026-09-12)\n## 复盘三件套(缺一不可)\n\n${三件套(false)}`);
|
|
3244
|
-
w(T, 'workflow/plans/2026-09-12-inc.md', PLAN('inc', '状态: done\n级别: L1'));
|
|
3245
|
-
fs.mkdirSync(path.join(T, 'wiki', '项目规范'), { recursive: true });
|
|
3246
|
-
w(T, 'wiki/项目规范/shell.md', '---\n标题: 某主题沉淀\n---\n# 某主题沉淀\n<状态:待整理>\n');
|
|
3247
|
-
const r = run(T);
|
|
3248
|
-
check('场景 25p incident 沉淀落点为标题/占位空壳 → WARN 落点空壳',
|
|
3249
|
-
r.status === 0 && outOf(r).includes('落点空壳') && outOf(r).includes('2026-09-12-inc.md')
|
|
3250
|
-
&& !outOf(r).includes('沉淀断档') && !outOf(r).includes('未沉淀'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3251
|
-
}
|
|
3252
|
-
{
|
|
3253
|
-
// intent 侧对称:done + 影响面有内容 + 沉淀指向空壳文件 → WARN 落点空壳(不阻断)
|
|
3254
|
-
const T = mkfix();
|
|
3255
|
-
w(T, 'workflow/intents/2026-09-12-sed.md', INTENT('sed', '状态: done\n级别: L1\n日期: 2026-09-12\n沉淀: wiki/项目规范/shell.md',
|
|
3256
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n## 影响面\n- 页面 A\n'));
|
|
3257
|
-
w(T, 'workflow/plans/2026-09-12-sed.md', PLAN('sed', '状态: done\n级别: L1'));
|
|
3258
|
-
fs.mkdirSync(path.join(T, 'wiki', '项目规范'), { recursive: true });
|
|
3259
|
-
w(T, 'wiki/项目规范/shell.md', '# 某主题沉淀\n<待整理>\n');
|
|
3260
|
-
const r = run(T);
|
|
3261
|
-
check('场景 25q intent 沉淀落点为标题/占位空壳 → WARN 落点空壳',
|
|
3262
|
-
r.status === 0 && outOf(r).includes('落点空壳') && outOf(r).includes('2026-09-12-sed.md')
|
|
3263
|
-
&& !outOf(r).includes('沉淀断档') && !outOf(r).includes('未沉淀'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3264
|
-
}
|
|
3265
|
-
{
|
|
3266
|
-
// 落点带「来源:」frontmatter → 非空壳(无落点空壳告警)
|
|
3267
|
-
const T = mkfix();
|
|
3268
|
-
w(T, 'workflow/intents/2026-09-12-sed.md', INTENT('sed', '状态: done\n级别: L1\n日期: 2026-09-12\n沉淀: wiki/项目规范/real.md',
|
|
3269
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n## 影响面\n- 页面 A\n'));
|
|
3270
|
-
w(T, 'workflow/plans/2026-09-12-sed.md', PLAN('sed', '状态: done\n级别: L1'));
|
|
3271
|
-
fs.mkdirSync(path.join(T, 'wiki', '项目规范'), { recursive: true });
|
|
3272
|
-
w(T, 'wiki/项目规范/real.md', '---\n来源: workflow/incidents/2026-09-12-inc.md\n---\n# 某主题沉淀\n');
|
|
3273
|
-
const r = run(T);
|
|
3274
|
-
check('场景 25r 沉淀落点带来源 frontmatter → 不出落点空壳',
|
|
3275
|
-
r.status === 0 && !outOf(r).includes('落点空壳') && !outOf(r).includes('沉淀断档'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3276
|
-
}
|
|
3277
|
-
{
|
|
3278
|
-
// 落点有 ≥3 行非占位正文 → 非空壳(incident 侧非空壳路径)
|
|
3279
|
-
const T = mkfix();
|
|
3280
|
-
w(T, 'workflow/incidents/2026-09-12-inc.md',
|
|
3281
|
-
`---\n状态: closed\n级别: L1\n发现: 2026-09-12\n沉淀: wiki/项目规范/real.md\n---\n# INCIDENT — inc\n## 时间线\n- 用户确认:草稿过目通过(2026-09-12)\n## 复盘三件套(缺一不可)\n\n${三件套(false)}`);
|
|
3282
|
-
w(T, 'workflow/plans/2026-09-12-inc.md', PLAN('inc', '状态: done\n级别: L1'));
|
|
3283
|
-
fs.mkdirSync(path.join(T, 'wiki', '项目规范'), { recursive: true });
|
|
3284
|
-
w(T, 'wiki/项目规范/real.md', '---\n标题: 某主题沉淀\n---\n# 某主题沉淀\n- 要点一\n- 要点二\n- 要点三\n');
|
|
3285
|
-
const r = run(T);
|
|
3286
|
-
check('场景 25s 沉淀落点有 ≥3 行非占位正文 → 不出落点空壳',
|
|
3287
|
-
r.status === 0 && !outOf(r).includes('落点空壳') && !outOf(r).includes('沉淀断档'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3288
|
-
}
|
|
3289
|
-
{
|
|
3290
|
-
// 回归:沉淀指向不存在的文件 → 仍是 WARN 沉淀断档(incident 侧;intent 侧由既有 25g 锚定)
|
|
3291
|
-
const T = mkfix();
|
|
3292
|
-
w(T, 'workflow/incidents/2026-09-12-inc.md',
|
|
3293
|
-
`---\n状态: closed\n级别: L1\n发现: 2026-09-12\n沉淀: wiki/项目规范/不存在.md\n---\n# INCIDENT — inc\n## 时间线\n- 用户确认:草稿过目通过(2026-09-12)\n## 复盘三件套(缺一不可)\n\n${三件套(false)}`);
|
|
3294
|
-
w(T, 'workflow/plans/2026-09-12-inc.md', PLAN('inc', '状态: done\n级别: L1'));
|
|
3295
|
-
const r = run(T);
|
|
3296
|
-
check('场景 25t 沉淀指向缺失文件 → 仍 WARN 沉淀断档(不出落点空壳)',
|
|
3297
|
-
r.status === 0 && outOf(r).includes('沉淀断档') && !outOf(r).includes('落点空壳'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3298
|
-
}
|
|
3299
|
-
{
|
|
3300
|
-
// 落点指向目录(readFileSync 抛异常)→ 按空壳出账并在文案注明读取失败
|
|
3301
|
-
const T = mkfix();
|
|
3302
|
-
w(T, 'workflow/incidents/2026-09-12-inc.md',
|
|
3303
|
-
`---\n状态: closed\n级别: L1\n发现: 2026-09-12\n沉淀: wiki/项目规范\n---\n# INCIDENT — inc\n## 时间线\n- 用户确认:草稿过目通过(2026-09-12)\n## 复盘三件套(缺一不可)\n\n${三件套(false)}`);
|
|
3304
|
-
w(T, 'workflow/plans/2026-09-12-inc.md', PLAN('inc', '状态: done\n级别: L1'));
|
|
3305
|
-
fs.mkdirSync(path.join(T, 'wiki', '项目规范'), { recursive: true });
|
|
3306
|
-
const r = run(T);
|
|
3307
|
-
check('场景 25u 沉淀落点读取失败(目录)→ WARN 落点空壳并注明读取失败',
|
|
3308
|
-
r.status === 0 && outOf(r).includes('落点空壳') && outOf(r).includes('读取失败'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3309
|
-
}
|
|
3310
|
-
|
|
3311
|
-
// ---- 场景 92:THEN 断言抽取(2026-10-11 then-assertion,P2-2,灰度 warning)----
|
|
3312
|
-
// extractThenByScenario 纯函数 + 端到端(声明场景缺 THEN 行 = 空壳场景 → WARN 场景无断言,聚合一条;
|
|
3313
|
-
// 验收场景节本身可选——节不存在零变化)
|
|
3314
|
-
{
|
|
3315
|
-
const spec = [
|
|
3316
|
-
'## 验收场景(可选)',
|
|
3317
|
-
'<说明行,不属于任何场景块>',
|
|
3318
|
-
'### S1 折扣',
|
|
3319
|
-
'- GIVEN 购物车有商品 THEN 不误抽(行中段 THEN 字样不算断言)',
|
|
3320
|
-
'- WHEN 结算',
|
|
3321
|
-
'- THEN 折扣金额入账',
|
|
3322
|
-
' - THEN 重复结算幂等(缩进变体也认)',
|
|
3323
|
-
'### S2 退款',
|
|
3324
|
-
'THEN: 退款单生成(无连字符带冒号也认)',
|
|
3325
|
-
'THEN 审计流水落库(无连字符空格也认)',
|
|
3326
|
-
].join('\n');
|
|
3327
|
-
const m = extractThenByScenario(spec);
|
|
3328
|
-
check('场景 92a extractThenByScenario:- THEN / 缩进 / THEN: / THEN 变体均抽取',
|
|
3329
|
-
m.get('S1').length === 2 && m.get('S2').length === 2
|
|
3330
|
-
&& m.get('S1')[0] === '- THEN 折扣金额入账' && m.get('S2')[0] === 'THEN: 退款单生成(无连字符带冒号也认)',
|
|
3331
|
-
JSON.stringify([...m]));
|
|
3332
|
-
check('场景 92b extractThenByScenario:GIVEN 行中段 THEN 字样不误抽',
|
|
3333
|
-
!m.get('S1').some((s) => s.includes('GIVEN')), JSON.stringify(m.get('S1')));
|
|
3334
|
-
check('场景 92c extractThenByScenario:无验收场景节 → 空 Map(节外 THEN 不认)',
|
|
3335
|
-
extractThenByScenario('# SPEC\n## 功能行为\n- THEN 节外不认\n').size === 0);
|
|
3336
|
-
check('场景 92d extractThenByScenario:非字符串(读取失败)→ 空 Map(不可判定不误报)',
|
|
3337
|
-
extractThenByScenario(null).size === 0);
|
|
3338
|
-
}
|
|
3339
|
-
{
|
|
3340
|
-
// 端到端:全场景带 THEN → 无「场景无断言」
|
|
3341
|
-
const T = mkfix();
|
|
3342
|
-
w(T, 'workflow/specs/2026-09-12-thn.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — thn\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n- WHEN y\n- THEN z\n### S2 退款\n- GIVEN a\n- THEN b\n');
|
|
3343
|
-
w(T, 'workflow/intents/2026-09-12-thn.md', INTENT('thn', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3344
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1 折扣生效(证据:checkout.json)\n- [x] 场景:S2 退款到账(证据:refund.test.js)\n'));
|
|
3345
|
-
w(T, 'workflow/plans/2026-09-12-thn.md', PLAN('thn', '状态: done\n级别: L1'));
|
|
3346
|
-
const r = run(T);
|
|
3347
|
-
check('场景 92e 全场景带 THEN → 无场景无断言告警(exit 0)',
|
|
3348
|
-
r.status === 0 && !outOf(r).includes('场景无断言'), `exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3349
|
-
}
|
|
3350
|
-
{
|
|
3351
|
-
// 端到端:S2/S3 缺 THEN → 聚合一条 WARN,S1(有断言)不在列
|
|
3352
|
-
const T = mkfix();
|
|
3353
|
-
w(T, 'workflow/specs/2026-09-12-thn.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — thn\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n- THEN z\n### S2 退款\n- GIVEN a\n### S3 对账\n- WHEN c\n');
|
|
3354
|
-
w(T, 'workflow/intents/2026-09-12-thn.md', INTENT('thn', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3355
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1(证据:a.log)\n- [x] 场景:S2(证据:b.log)\n- [x] 场景:S3(证据:c.log)\n'));
|
|
3356
|
-
w(T, 'workflow/plans/2026-09-12-thn.md', PLAN('thn', '状态: done\n级别: L1'));
|
|
3357
|
-
const r = run(T);
|
|
3358
|
-
check('场景 92f 场景缺 THEN → 聚合 WARN 且只列缺断言的 ID(S2、S3)',
|
|
3359
|
-
r.status === 0 && outOf(r).includes('场景无断言') && outOf(r).includes('缺 THEN 断言:S2、S3'),
|
|
3360
|
-
`exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3361
|
-
}
|
|
3362
|
-
{
|
|
3363
|
-
// 端到端:spec 无验收场景节 → THEN 对账不适用(零变化)
|
|
3364
|
-
const T = mkfix();
|
|
3365
|
-
w(T, 'workflow/specs/2026-09-12-thn.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — thn\n## 功能行为\n普通 spec,无场景节\n');
|
|
3366
|
-
w(T, 'workflow/intents/2026-09-12-thn.md', INTENT('thn', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3367
|
-
'\n## 验收标准(可测试)\n- [x] 用例通过(证据:jest 全绿)\n'));
|
|
3368
|
-
w(T, 'workflow/plans/2026-09-12-thn.md', PLAN('thn', '状态: done\n级别: L1'));
|
|
3369
|
-
const r = run(T);
|
|
3370
|
-
check('场景 92g spec 无验收场景节 → 零场景告警(exit 0)',
|
|
3371
|
-
r.status === 0 && !outOf(r).includes('场景无断言') && !outOf(r).includes('场景引用断链'),
|
|
3372
|
-
`exit=${r.status}\n${outOf(r).slice(0, 400)}`);
|
|
3373
|
-
}
|
|
3374
|
-
|
|
3375
|
-
// ---- 场景 93:引用断链反向对账(2026-10-11 dangling-ref,P2-1,灰度 warning)----
|
|
3376
|
-
// 端到端(与「场景未覆盖」共用同一次解析):验收标准引用 spec 未声明的场景 ID → WARN 场景引用断链
|
|
3377
|
-
{
|
|
3378
|
-
// 引用 S1(已声明且覆盖)+ S9(未声明)→ 只出断链,不出未覆盖
|
|
3379
|
-
const T = mkfix();
|
|
3380
|
-
w(T, 'workflow/specs/2026-09-12-dr.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — dr\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n- THEN z\n');
|
|
3381
|
-
w(T, 'workflow/intents/2026-09-12-dr.md', INTENT('dr', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3382
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1(证据:a.log)\n- [x] 场景:S9 幻影场景(证据:ghost.log)\n'));
|
|
3383
|
-
w(T, 'workflow/plans/2026-09-12-dr.md', PLAN('dr', '状态: done\n级别: L1'));
|
|
3384
|
-
const r = run(T);
|
|
3385
|
-
check('场景 93a 引用 spec 未声明的 S9 → WARN 场景引用断链(不阻断)',
|
|
3386
|
-
r.status === 0 && outOf(r).includes('场景引用断链') && outOf(r).includes('S9'),
|
|
3387
|
-
`exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3388
|
-
check('场景 93b 已覆盖的 S1 不触发未覆盖(两方向互不误伤)',
|
|
3389
|
-
!outOf(r).includes('场景未覆盖'), outOf(r).slice(0, 400));
|
|
3390
|
-
}
|
|
3391
|
-
{
|
|
3392
|
-
// 引用与声明完全一致 → 无断链
|
|
3393
|
-
const T = mkfix();
|
|
3394
|
-
w(T, 'workflow/specs/2026-09-12-dr.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — dr\n## 验收场景(可选)\n### S1 折扣\n- GIVEN x\n- THEN z\n### S2 退款\n- GIVEN a\n- THEN b\n');
|
|
3395
|
-
w(T, 'workflow/intents/2026-09-12-dr.md', INTENT('dr', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3396
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1(证据:a.log)\n- [x] 场景:S2(证据:b.log)\n'));
|
|
3397
|
-
w(T, 'workflow/plans/2026-09-12-dr.md', PLAN('dr', '状态: done\n级别: L1'));
|
|
3398
|
-
const r = run(T);
|
|
3399
|
-
check('场景 93c 引用与声明一致 → 无断链无未覆盖(exit 0)',
|
|
3400
|
-
r.status === 0 && !outOf(r).includes('场景引用断链') && !outOf(r).includes('场景未覆盖'),
|
|
3401
|
-
`exit=${r.status}\n${outOf(r).slice(0, 400)}`);
|
|
3402
|
-
}
|
|
3403
|
-
{
|
|
3404
|
-
// spec 无场景节但 intent 引用 S1 → 同样算断链(引用了不存在的场景)
|
|
3405
|
-
const T = mkfix();
|
|
3406
|
-
w(T, 'workflow/specs/2026-09-12-dr.md', '---\n状态: approved\n级别: L1\n---\n# SPEC — dr\n## 功能行为\n普通 spec,无场景节\n');
|
|
3407
|
-
w(T, 'workflow/intents/2026-09-12-dr.md', INTENT('dr', '状态: done\n级别: L1\n日期: 2026-09-12',
|
|
3408
|
-
'\n## 验收标准(可测试)\n- [x] 场景:S1(证据:a.log)\n'));
|
|
3409
|
-
w(T, 'workflow/plans/2026-09-12-dr.md', PLAN('dr', '状态: done\n级别: L1'));
|
|
3410
|
-
const r = run(T);
|
|
3411
|
-
check('场景 93d spec 无场景节且引用 S1 → 断链 WARN(exit 0)',
|
|
3412
|
-
r.status === 0 && outOf(r).includes('场景引用断链') && outOf(r).includes('S1'),
|
|
3413
|
-
`exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3414
|
-
}
|
|
3415
|
-
|
|
3416
|
-
// ==== B6b-2(2026-10-13 自源仓 check-loop.test.mjs 回灌):§21–§26 六段 + §3x + 检查 1 L1 plan 即入口 + 复核上下文 ====
|
|
3417
|
-
// 场景集与关键词断言沿源仓同段场景移植;锚日期换 kit 口径(v12:specVerifSince/attributionSince/
|
|
3418
|
-
// convergenceSince=2026-10-13,convergenceHardSince/reviewerSince/incidentRegrSince=2026-10-14)。
|
|
3419
|
-
|
|
3420
|
-
// ---- 检查 1:L1 plan 即入口(源仓 s01,2026-10-10 L1 收成一份 plan)----
|
|
3421
|
-
{
|
|
3422
|
-
const T = mkfix();
|
|
3423
|
-
w(T, 'workflow/plans/2026-10-13-solo.md', PLAN('solo', '状态: draft\n级别: L1'));
|
|
3424
|
-
expectOk('检查1 L1 plan 即入口:无同名 intent/incident → exit 0', T);
|
|
3425
|
-
rmfix(T);
|
|
3426
|
-
}
|
|
3427
|
-
{
|
|
3428
|
-
const T = mkfix();
|
|
3429
|
-
w(T, 'workflow/plans/2026-10-13-hi.md', PLAN('hi', '状态: draft\n级别: L2'));
|
|
3430
|
-
expectHard('检查1 L2 plan 落单 → hard 配对断裂', T, '配对断裂');
|
|
3431
|
-
rmfix(T);
|
|
3432
|
-
}
|
|
3433
|
-
{
|
|
3434
|
-
const T = mkfix();
|
|
3435
|
-
w(T, 'workflow/plans/2026-10-13-nolvl.md', PLAN('nolvl', '状态: draft'));
|
|
3436
|
-
expectHard('检查1 级别缺失 plan 落单 → 仍 hard(fail-closed)', T, '配对断裂');
|
|
3437
|
-
rmfix(T);
|
|
3438
|
-
}
|
|
3439
|
-
|
|
3440
|
-
// ---- 检查 1 扩展:独立复核上下文标识(policy v12 reviewerSince,advisory)----
|
|
3441
|
-
{
|
|
3442
|
-
const trio = (name, fm, body, pv = 12) => {
|
|
3443
|
-
const T = mkfix();
|
|
3444
|
-
w(T, '.agents/kit.json', `{"policyVersion":${pv}}\n`);
|
|
3445
|
-
w(T, `workflow/intents/${name}.md`, INTENT(name, '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
3446
|
-
w(T, `workflow/specs/${name}.md`, SPEC(name, fm, body));
|
|
3447
|
-
w(T, `workflow/plans/${name}.md`, PLAN(name, '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3448
|
-
return T;
|
|
3449
|
-
};
|
|
3450
|
-
let T = trio('2026-10-14-rv1', '状态: draft\n级别: L1\n日期: 2026-10-14', '\n- 独立复核:通过\n');
|
|
3451
|
-
let r = run(T);
|
|
3452
|
-
check('检查1 复核上下文:锚后复核行缺标识 → WARN 且 exit 0',
|
|
3453
|
-
r.status === 0 && outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3454
|
-
rmfix(T);
|
|
3455
|
-
T = trio('2026-10-14-rv2', '状态: draft\n级别: L1\n日期: 2026-10-14', '\n- 独立复核:通过(复核上下文:session-abc)\n');
|
|
3456
|
-
r = run(T);
|
|
3457
|
-
check('检查1 复核上下文:复核行带「复核上下文:<id>」→ 无告警',
|
|
3458
|
-
r.status === 0 && !outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3459
|
-
rmfix(T);
|
|
3460
|
-
T = trio('2026-10-14-rv3', '状态: draft\n级别: L1\n日期: 2026-10-13', '\n- 独立复核:通过\n');
|
|
3461
|
-
r = run(T);
|
|
3462
|
-
check('检查1 复核上下文:锚前存量(<reviewerSince)豁免 → 无告警',
|
|
3463
|
-
r.status === 0 && !outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3464
|
-
rmfix(T);
|
|
3465
|
-
T = trio('2026-10-14-rv4', '状态: draft\n级别: L1\n日期: 2026-10-14', '\n- 独立复核:通过\n', 11);
|
|
3466
|
-
r = run(T);
|
|
3467
|
-
check('检查1 复核上下文:v11 缺键 → 子检整体跳过',
|
|
3468
|
-
r.status === 0 && !outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3469
|
-
rmfix(T);
|
|
3470
|
-
}
|
|
3471
|
-
|
|
3472
|
-
// ---- 检查 3x:incident 防复发验证证据门(hard;锚 incidentRegrSince=2026-10-14,git 建档日锚)----
|
|
3473
|
-
{
|
|
3474
|
-
const mkRegr = (verBody, { pv = 12, addIso = '2026-10-14T01:00:00.000Z', name = '2026-10-14-regr' } = {}) => {
|
|
3475
|
-
const T = mkfix();
|
|
3476
|
-
gitInit(T);
|
|
3477
|
-
w(T, '.agents/kit.json', `{"policyVersion":${pv}}\n`);
|
|
3478
|
-
const fm = '状态: fixed\n级别: L1\n发现: 2026-10-14\n流程: standard';
|
|
3479
|
-
const body = `# INCIDENT — ${name}\n\n1. 结构性修复\n - 修复 commit:abc1234\n - 是否需要新 intent:\n - 否 → 理由:单点修复\n\n2. 防复发验证\n${verBody}\n\n3. 规范条目\n - AGENTS.md 某节\n`;
|
|
3480
|
-
const noFp = `---\n状态: open\n级别: L1\n发现: 2026-10-14\n流程: standard\n---\n${body}`;
|
|
3481
|
-
const fp = computeFingerprint(noFp);
|
|
3482
|
-
w(T, `workflow/incidents/${name}.md`, `---\n${fm}\n确认指纹: ${fp.slice(0, 16)}\n---\n${body}`);
|
|
3483
|
-
w(T, `workflow/plans/${name}.md`, PLAN(name, '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3484
|
-
writeLedger(T, [{ ts: addIso, doc: `workflow/incidents/${name}.md`, stage: 'fixed', fingerprint: fp, prev: 'open', source: 'chat-delegated', batch: 't-3x', seq: 1, of: 1 }]);
|
|
3485
|
-
gitCommitAllAt(T, 'incident fixture', addIso);
|
|
3486
|
-
return T;
|
|
3487
|
-
};
|
|
3488
|
-
let T = mkRegr(' - 已人工验证,无自动化\n');
|
|
3489
|
-
let r = run(T, { git: true, args: [] });
|
|
3490
|
-
check('检查3x 锚后 fixed 无回归引用 → hard「回归证据缺失」',
|
|
3491
|
-
r.status === 1 && outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3492
|
-
rmfix(T);
|
|
3493
|
-
T = mkRegr(' - 回归用例 check-scope.test.mjs 已覆盖\n');
|
|
3494
|
-
r = run(T, { git: true });
|
|
3495
|
-
check('检查3x fixed 有 .test.mjs 引用 → 不拦',
|
|
3496
|
-
r.status === 0 && !outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3497
|
-
rmfix(T);
|
|
3498
|
-
T = mkRegr(' - 已人工验证,无自动化\n', { addIso: '2026-10-13T01:00:00.000Z', name: '2026-10-13-regr-pre' });
|
|
3499
|
-
r = run(T, { git: true, args: [] });
|
|
3500
|
-
check('检查3x 锚前存量(建档日 < incidentRegrSince)→ 豁免不拦',
|
|
3501
|
-
r.status === 0 && !outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3502
|
-
rmfix(T);
|
|
3503
|
-
T = mkRegr(' - 已人工验证,无自动化\n', { pv: 8, name: '2026-10-14-regr-v8' });
|
|
3504
|
-
r = run(T, { git: true, args: [] });
|
|
3505
|
-
check('检查3x 缺键(v8)→ 整体跳过不拦',
|
|
3506
|
-
r.status === 0 && !outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3507
|
-
rmfix(T);
|
|
3508
|
-
}
|
|
3509
|
-
|
|
3510
|
-
// ---- 检查 21:spec 验证落地核查(锚 specVerifSince=2026-10-13,git 建档日锚)----
|
|
3511
|
-
{
|
|
3512
|
-
const mkSpec = (lvl, verBody) => {
|
|
3513
|
-
const T = mkfix();
|
|
3514
|
-
gitInit(T);
|
|
3515
|
-
w(T, '.agents/kit.json', '{"policyVersion":12}\n');
|
|
3516
|
-
w(T, 'workflow/intents/2026-10-13-sp.md', INTENT('sp', '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
3517
|
-
w(T, 'workflow/plans/2026-10-13-sp.md', PLAN('sp', '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3518
|
-
const fm = `状态: done\n级别: ${lvl}\n日期: 2026-10-13`;
|
|
3519
|
-
const body = `# SPEC — sp\n\n## 测试用例\n${verBody}\n`;
|
|
3520
|
-
// 指纹按 prev 态(approved)内容计算——检查 15 bindingSha256 以台账 prev 字段复原重算
|
|
3521
|
-
const fp = computeFingerprint(`---\n状态: approved\n级别: ${lvl}\n日期: 2026-10-13\n---\n${body}`);
|
|
3522
|
-
w(T, 'workflow/specs/2026-10-13-sp.md', `---\n${fm}\n确认指纹: ${fp.slice(0, 16)}\n---\n${body}`);
|
|
3523
|
-
writeLedger(T, [{ ts: '2026-10-13T01:00:00.000Z', doc: 'workflow/specs/2026-10-13-sp.md', stage: 'done', fingerprint: fp, prev: 'approved', source: 'chat-delegated', batch: 't-21', seq: 1, of: 1 }]);
|
|
3524
|
-
gitCommitAllAt(T, 'spec fixture', '2026-10-13T01:00:00.000Z');
|
|
3525
|
-
return T;
|
|
3526
|
-
};
|
|
3527
|
-
let T = mkSpec('L2', ' - 人工走查确认\n');
|
|
3528
|
-
let r = run(T, { git: true });
|
|
3529
|
-
check('检查21 锚后 L2 done spec 验证节无引用 → WARN「spec 验证落地」',
|
|
3530
|
-
r.status === 0 && outOf(r).includes('spec 验证落地'), `exit=${r.status}\n${outOf(r)}`);
|
|
3531
|
-
rmfix(T);
|
|
3532
|
-
T = mkSpec('L2', ' - `node .agents/scripts/verify.mjs` 全绿\n');
|
|
3533
|
-
r = run(T, { git: true });
|
|
3534
|
-
check('检查21 验证节有 verify.mjs 引用 → 无 WARN',
|
|
3535
|
-
r.status === 0 && !outOf(r).includes('spec 验证落地'), `exit=${r.status}\n${outOf(r)}`);
|
|
3536
|
-
rmfix(T);
|
|
3537
|
-
T = mkSpec('L1', ' - 人工走查确认\n');
|
|
3538
|
-
r = run(T, { git: true });
|
|
3539
|
-
check('检查21 L1 spec 不核查',
|
|
3540
|
-
r.status === 0 && !outOf(r).includes('spec 验证落地'), `exit=${r.status}\n${outOf(r)}`);
|
|
3541
|
-
rmfix(T);
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
|
-
// ---- 检查 22:机制使用量台账(格式校验 + 待退役强制处置期限;台账文件在场才启用)----
|
|
3545
|
-
{
|
|
3546
|
-
const REG = (rows) => ['# 机制使用量台账', '', '| 机制 | 门禁位置 | 触发统计口径 | 退役阈值 | 最近触发 | 状态 | 处置期限 |', '|---|---|---|---|---|---|---|', ...rows].join('\n');
|
|
3547
|
-
const T0 = mkfix();
|
|
3548
|
-
w(T0, 'workflow/intents/2026-09-12-r0.md', INTENT('r0', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
3549
|
-
w(T0, 'workflow/plans/2026-09-12-r0.md', PLAN('r0', '状态: draft\n级别: L1'));
|
|
3550
|
-
let r = run(T0);
|
|
3551
|
-
check('检查22 台账文件缺失 → 跳过不告警(装户无感)',
|
|
3552
|
-
r.status === 0 && !outOf(r).includes('机制台账'), outOf(r));
|
|
3553
|
-
rmfix(T0);
|
|
3554
|
-
|
|
3555
|
-
const T1 = mkfix();
|
|
3556
|
-
w(T1, '.agents/mechanism-registry.md', REG([
|
|
3557
|
-
'| 三件套配对 | check-loop 检查 3 | WARN 计数 | 6月=0 | 2026-10 | 现役 | — |',
|
|
3558
|
-
'| 某低频机制 | check-loop 检查 9 | WARN 计数 0/219 | =0 | 2026-10-13 | 待退役 | B7 批次 |',
|
|
3559
|
-
]));
|
|
3560
|
-
r = run(T1);
|
|
3561
|
-
check('检查22 合法台账(现役+待退役带期限)→ 不告警',
|
|
3562
|
-
r.status === 0 && !outOf(r).includes('机制台账'), outOf(r));
|
|
3563
|
-
rmfix(T1);
|
|
3564
|
-
|
|
3565
|
-
const T2 = mkfix();
|
|
3566
|
-
w(T2, '.agents/mechanism-registry.md', REG([
|
|
3567
|
-
'| 某机制 | check-loop 检查 9 | WARN 计数 | 6月=0 | 2026-10 | 待退役 | |',
|
|
3568
|
-
]));
|
|
3569
|
-
r = run(T2);
|
|
3570
|
-
check('检查22 待退役缺处置期限 → WARN 强制处置',
|
|
3571
|
-
r.status === 0 && outOf(r).includes('待退役但缺处置期限'), outOf(r));
|
|
3572
|
-
rmfix(T2);
|
|
3573
|
-
|
|
3574
|
-
const T3 = mkfix();
|
|
3575
|
-
w(T3, '.agents/mechanism-registry.md', REG([
|
|
3576
|
-
'| 某机制 | check-loop 检查 9 | WARN 计数 | 6月=0 | 2026-10 | 已删除 | — |',
|
|
3577
|
-
'| 列数不足 | check-loop | — | — | — | 现役 |',
|
|
3578
|
-
]));
|
|
3579
|
-
r = run(T3);
|
|
3580
|
-
check('检查22 状态非法 + 列数≠7 → 双 WARN(格式防线)',
|
|
3581
|
-
r.status === 0 && outOf(r).includes('状态非法') && outOf(r).includes('行列数'), outOf(r));
|
|
3582
|
-
rmfix(T3);
|
|
3583
|
-
}
|
|
3584
|
-
|
|
3585
|
-
// ---- 检查 23:判据级收敛对账(advisory 档 convergenceSince=2026-10-13 / hard 档 convergenceHardSince=2026-10-14)----
|
|
3586
|
-
{
|
|
3587
|
-
const TABLE = (rows) => [
|
|
3588
|
-
'## 验收判据',
|
|
3589
|
-
'',
|
|
3590
|
-
'| 判据(可观察行为) | 证据(测试文件 / 命令 / 证据文件) | 状态 |',
|
|
3591
|
-
'|---|---|---|',
|
|
3592
|
-
...rows,
|
|
3593
|
-
].join('\n');
|
|
3594
|
-
const GREEN_ROW = (iso) => [
|
|
3595
|
-
'# 验证绿凭证摘要',
|
|
3596
|
-
'',
|
|
3597
|
-
'| 提交(短SHA) | 时间(UTC) | 门数 | 套件计数 | 绑定单 | runId |',
|
|
3598
|
-
'|---|---|---|---|---|---|',
|
|
3599
|
-
`| \`abc1234\` | ${iso} | 7 | PASS 3 / FAIL 0 | — | — |`,
|
|
3600
|
-
'',
|
|
3601
|
-
].join('\n');
|
|
3602
|
-
const mkSpec = (body, date = '2026-10-14', lvl = 'L2', pv = 12) => {
|
|
3603
|
-
const T = mkfix();
|
|
3604
|
-
gitInit(T);
|
|
3605
|
-
w(T, '.agents/kit.json', `{"policyVersion":${pv}}\n`);
|
|
3606
|
-
w(T, 'workflow/intents/2026-10-13-sp.md', INTENT('sp', '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
3607
|
-
w(T, 'workflow/plans/2026-10-13-sp.md', PLAN('sp', '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3608
|
-
const full = `# SPEC — sp\n\n${body}\n`;
|
|
3609
|
-
const fp = computeFingerprint(`---\n状态: approved\n级别: ${lvl}\n日期: ${date}\n---\n${full}`);
|
|
3610
|
-
// 两笔提交复刻真实流:approved 留痕(检查 14)→ 置 done + 台账
|
|
3611
|
-
w(T, 'workflow/specs/2026-10-13-sp.md', `---\n状态: approved\n级别: ${lvl}\n日期: ${date}\n确认指纹: ${fp.slice(0, 16)}\n---\n${full}`);
|
|
3612
|
-
gitCommitAll(T, 'spec approved');
|
|
3613
|
-
w(T, 'workflow/specs/2026-10-13-sp.md', `---\n状态: done\n级别: ${lvl}\n日期: ${date}\n确认指纹: ${fp.slice(0, 16)}\n---\n${full}`);
|
|
3614
|
-
writeLedger(T, [{ ts: `${date}T01:00:00.000Z`, doc: 'workflow/specs/2026-10-13-sp.md', stage: 'done', fingerprint: fp, prev: 'approved', source: 'chat-delegated', batch: 't-23', seq: 1, of: 1 }]);
|
|
3615
|
-
gitCommitAll(T, 'spec done');
|
|
3616
|
-
return T;
|
|
3617
|
-
};
|
|
3618
|
-
// ① 判据全覆盖(全量门命令 + 存在文件,hard 档)→ 无收敛告警
|
|
3619
|
-
let T = mkSpec(TABLE([
|
|
3620
|
-
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3621
|
-
'| 守卫拒绝超量 | src/guard.test.mjs | 覆盖 |',
|
|
3622
|
-
]));
|
|
3623
|
-
fs.mkdirSync(path.join(T, 'src'), { recursive: true });
|
|
3624
|
-
w(T, 'src/guard.test.mjs', '// fixture');
|
|
3625
|
-
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-14T08:00:00.000Z'));
|
|
3626
|
-
let r = run(T, { git: true, args: [] });
|
|
3627
|
-
check('检查23 硬档判据全覆盖(命令+存在文件+当日全绿行)→ exit 0 无收敛告警',
|
|
3628
|
-
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3629
|
-
rmfix(T);
|
|
3630
|
-
// ② advisory 档:状态自报「覆盖」但文件不存在 → WARN 收敛未覆盖(完成声明不是证据;exit 0 即未升 hard)
|
|
3631
|
-
T = mkSpec(TABLE([
|
|
3632
|
-
'| 守卫拒绝超量 | src/nope.test.mjs | 覆盖 |',
|
|
3633
|
-
]), '2026-10-13');
|
|
3634
|
-
r = run(T, { git: true });
|
|
3635
|
-
check('检查23 advisory 档自报覆盖但证据不存在 → WARN「收敛未覆盖」',
|
|
3636
|
-
r.status === 0 && outOf(r).includes('[WARN 收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3637
|
-
rmfix(T);
|
|
3638
|
-
// ③ 无验收判据节 → 收敛无判据(WARN)
|
|
3639
|
-
T = mkSpec('## 风险评估\n\n- 暂无\n', '2026-10-13');
|
|
3640
|
-
r = run(T, { git: true });
|
|
3641
|
-
check('检查23 无验收判据节 → WARN「收敛无判据」',
|
|
3642
|
-
r.status === 0 && outOf(r).includes('收敛无判据'), `exit=${r.status}\n${outOf(r)}`);
|
|
3643
|
-
rmfix(T);
|
|
3644
|
-
// ④ 表只有占位行 → 收敛无判据
|
|
3645
|
-
T = mkSpec(TABLE(['| <行为 1> | <路径或命令> | <覆盖 / 未覆盖 / 人工> |']), '2026-10-13');
|
|
3646
|
-
r = run(T, { git: true });
|
|
3647
|
-
check('检查23 表仅占位行 → WARN「收敛无判据」',
|
|
3648
|
-
r.status === 0 && outOf(r).includes('收敛无判据'), `exit=${r.status}\n${outOf(r)}`);
|
|
3649
|
-
rmfix(T);
|
|
3650
|
-
// ⑤ 人工判据空证据合法 + 命令声明(advisory 档认声明)→ 无告警
|
|
3651
|
-
T = mkSpec(TABLE([
|
|
3652
|
-
'| 视觉布局走查 | | 人工 |',
|
|
3653
|
-
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3654
|
-
]), '2026-10-13');
|
|
3655
|
-
r = run(T, { git: true });
|
|
3656
|
-
check('检查23 人工判据(空证据)+ advisory 命令声明 → 无告警',
|
|
3657
|
-
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3658
|
-
rmfix(T);
|
|
3659
|
-
// ⑥ 锚前存量(日期 < convergenceSince)→ 豁免
|
|
3660
|
-
T = mkSpec('## 风险评估\n\n- 暂无\n', '2026-10-12');
|
|
3661
|
-
r = run(T, { git: true });
|
|
3662
|
-
check('检查23 锚前存量件 → 豁免',
|
|
3663
|
-
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3664
|
-
rmfix(T);
|
|
3665
|
-
// ⑦ v9(有 specVerif/attribution、无 convergenceSince)→ 检查整体跳过
|
|
3666
|
-
T = mkSpec('## 风险评估\n\n- 暂无\n', '2026-10-13', 'L2', 9);
|
|
3667
|
-
r = run(T, { git: true });
|
|
3668
|
-
check('检查23 v9 缺键 → 检查整体跳过',
|
|
3669
|
-
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3670
|
-
rmfix(T);
|
|
3671
|
-
// ⑧ v12 硬锚前一日(advisory 面)无效证据 → 仍 WARN 不升 hard
|
|
3672
|
-
T = mkSpec(TABLE([
|
|
3673
|
-
'| 守卫拒绝超量 | src/nope.test.mjs | 覆盖 |',
|
|
3674
|
-
]), '2026-10-13');
|
|
3675
|
-
r = run(T, { git: true, args: [] });
|
|
3676
|
-
check('检查23 硬锚前一日无效证据 → 默认硬门仍 WARN 不阻断',
|
|
3677
|
-
r.status === 0 && outOf(r).includes('[WARN 收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3678
|
-
rmfix(T);
|
|
3679
|
-
// ⑨ 硬锚起:路径不存在 → 默认硬门 HARD
|
|
3680
|
-
T = mkSpec(TABLE([
|
|
3681
|
-
'| 守卫拒绝超量 | src/nope.test.mjs | 覆盖 |',
|
|
3682
|
-
]));
|
|
3683
|
-
r = run(T, { git: true, args: [] });
|
|
3684
|
-
check('检查23 硬锚起路径不存在 → 默认硬门 HARD「收敛未覆盖」',
|
|
3685
|
-
r.status !== 0 && outOf(r).includes('[收敛未覆盖]') && !outOf(r).includes('[WARN 收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3686
|
-
rmfix(T);
|
|
3687
|
-
// ⑩ 硬锚起:全量门命令但无全绿行 → HARD(声明不是凭证)
|
|
3688
|
-
T = mkSpec(TABLE([
|
|
3689
|
-
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3690
|
-
]));
|
|
3691
|
-
r = run(T, { git: true });
|
|
3692
|
-
check('检查23 硬锚起命令无全绿行 → HARD',
|
|
3693
|
-
r.status !== 0 && outOf(r).includes('[收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3694
|
-
rmfix(T);
|
|
3695
|
-
// ⑪ 全绿行早于 spec 日期 → 仍 HARD
|
|
3696
|
-
T = mkSpec(TABLE([
|
|
3697
|
-
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3698
|
-
]));
|
|
3699
|
-
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-13T08:00:00.000Z'));
|
|
3700
|
-
r = run(T, { git: true });
|
|
3701
|
-
check('检查23 全绿行早于 spec 日期 → HARD',
|
|
3702
|
-
r.status !== 0 && outOf(r).includes('[收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3703
|
-
rmfix(T);
|
|
3704
|
-
// ⑫ 硬锚起非全量门命令(无对应绿记录通道)→ HARD
|
|
3705
|
-
T = mkSpec(TABLE([
|
|
3706
|
-
'| 页面走查 | run-tests | 覆盖 |',
|
|
3707
|
-
]));
|
|
3708
|
-
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-14T08:00:00.000Z'));
|
|
3709
|
-
r = run(T, { git: true });
|
|
3710
|
-
check('检查23 硬锚起非全量门命令有全绿行 → HARD',
|
|
3711
|
-
r.status !== 0 && outOf(r).includes('[收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3712
|
-
rmfix(T);
|
|
3713
|
-
// ⑬ 硬锚起路径存在即覆盖(不读正文是否与判据相关)
|
|
3714
|
-
T = mkSpec(TABLE([
|
|
3715
|
-
'| 守卫拒绝超量 | src/guard.test.mjs | 覆盖 |',
|
|
3716
|
-
]));
|
|
3717
|
-
fs.mkdirSync(path.join(T, 'src'), { recursive: true });
|
|
3718
|
-
w(T, 'src/guard.test.mjs', '// fixture');
|
|
3719
|
-
r = run(T, { git: true });
|
|
3720
|
-
check('检查23 硬锚起路径存在 → 无收敛告警',
|
|
3721
|
-
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3722
|
-
rmfix(T);
|
|
3723
|
-
// ⑭ 硬锚起人工空证据仍合法 + 有当日全绿行的全量门命令 → 无告警;无判据表在硬锚日仍 WARN 不阻断
|
|
3724
|
-
T = mkSpec(TABLE([
|
|
3725
|
-
'| 视觉布局走查 | | 人工 |',
|
|
3726
|
-
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3727
|
-
]));
|
|
3728
|
-
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-14T08:00:00.000Z'));
|
|
3729
|
-
r = run(T, { git: true });
|
|
3730
|
-
check('检查23 硬锚起人工空证据 + 有凭证的命令 → 无告警',
|
|
3731
|
-
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3732
|
-
rmfix(T);
|
|
3733
|
-
T = mkSpec('## 风险评估\n\n- 暂无\n');
|
|
3734
|
-
r = run(T, { git: true });
|
|
3735
|
-
check('检查23 硬锚起无验收判据表 → 仍 WARN 不阻断',
|
|
3736
|
-
r.status === 0 && outOf(r).includes('[WARN 收敛无判据]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3737
|
-
rmfix(T);
|
|
3738
|
-
}
|
|
3739
|
-
|
|
3740
|
-
// ---- 检查 24:incident 归因字段核查(锚 attributionSince=2026-10-13;发现日锚)----
|
|
3741
|
-
{
|
|
3742
|
-
const mkInc = (name, fm, withPlanDate = '2026-09-01') => {
|
|
3743
|
-
const T = mkfix();
|
|
3744
|
-
w(T, '.agents/kit.json', '{"policyVersion":12}\n'); // 锚经 kit.json policyVersion 供给(v1 缺 attributionSince → 整段跳过)
|
|
3745
|
-
// L1 配对:检查 3 要求 L1 incident 有同名 plan(否则 HARD 掩盖本段 WARN)
|
|
3746
|
-
w(T, `workflow/plans/${name}`, PLAN(name.replace(/\.md$/, ''), `状态: draft\n级别: L1\n日期: ${withPlanDate}`));
|
|
3747
|
-
w(T, `workflow/incidents/${name}`, `---\n${fm}\n---\n# INCIDENT\n\n## 复盘三件套(缺一不可)\n\n1. 结构性修复\n2. 防复发验证\n3. 规范条目\n- 是否需要新 intent:否\n`);
|
|
3748
|
-
return T;
|
|
3749
|
-
};
|
|
3750
|
-
let T = mkInc('2026-10-13-a.md', '状态: open\n级别: L1\n发现: 2026-10-13\n来源: user\n影响环境: dev');
|
|
3751
|
-
let r = run(T);
|
|
3752
|
-
check('检查24 锚后两键合法 → 无归因告警',
|
|
3753
|
-
r.status === 0 && !outOf(r).includes('归因'), `exit=${r.status}\n${outOf(r)}`);
|
|
3754
|
-
rmfix(T);
|
|
3755
|
-
T = mkInc('2026-10-13-b.md', '状态: open\n级别: L1\n发现: 2026-10-13');
|
|
3756
|
-
r = run(T);
|
|
3757
|
-
check('检查24 锚后缺来源/影响环境 → 双 WARN「归因缺字段」',
|
|
3758
|
-
r.status === 0 && (outOf(r).match(/归因缺字段/g) || []).length >= 2, `exit=${r.status}\n${outOf(r)}`);
|
|
3759
|
-
rmfix(T);
|
|
3760
|
-
T = mkInc('2026-10-13-c.md', '状态: open\n级别: L1\n发现: 2026-10-13\n来源: robot\n影响环境: qa');
|
|
3761
|
-
r = run(T);
|
|
3762
|
-
check('检查24 锚后值不在枚举 → WARN「归因非法值」',
|
|
3763
|
-
r.status === 0 && outOf(r).includes('归因非法值'), `exit=${r.status}\n${outOf(r)}`);
|
|
3764
|
-
rmfix(T);
|
|
3765
|
-
T = mkInc('2026-10-12-d.md', '状态: open\n级别: L1\n发现: 2026-10-12');
|
|
3766
|
-
r = run(T);
|
|
3767
|
-
check('检查24 锚前存量件缺键 → 豁免不回填',
|
|
3768
|
-
r.status === 0 && !outOf(r).includes('归因'), `exit=${r.status}\n${outOf(r)}`);
|
|
3769
|
-
rmfix(T);
|
|
3770
|
-
T = mkInc('2026-10-13-e.md', '状态: open\n级别: L1\n发现: 2026-10-13\n流程: legacy');
|
|
3771
|
-
r = run(T);
|
|
3772
|
-
check('检查24 legacy 回填件 → 豁免',
|
|
3773
|
-
r.status === 0 && !outOf(r).includes('归因'), `exit=${r.status}\n${outOf(r)}`);
|
|
3774
|
-
rmfix(T);
|
|
3775
|
-
}
|
|
3776
|
-
|
|
3777
|
-
// ---- 检查 25:路由预判一致性(advisory 纯 WARN;预判字段在场才检)----
|
|
3778
|
-
{
|
|
3779
|
-
// ① 一致 → 零告警
|
|
3780
|
-
const T = mkfix();
|
|
3781
|
-
w(T, 'workflow/intents/2026-09-12-pj1.md', INTENT('pj1', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=require level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3782
|
-
w(T, 'workflow/plans/2026-09-12-pj1.md', PLAN('pj1', '状态: done\n级别: L1'));
|
|
3783
|
-
let r = run(T);
|
|
3784
|
-
check('检查25 预判与落位一致 → 零告警 exit 0',
|
|
3785
|
-
r.status === 0 && !outOf(r).includes('预判'), `exit=${r.status}\n${outOf(r)}`);
|
|
3786
|
-
rmfix(T);
|
|
3787
|
-
// ② 类型不符:intent(≡require)预判 fix;incident(≡fix)预判 require → 双 WARN 不阻断
|
|
3788
|
-
const T2 = mkfix();
|
|
3789
|
-
w(T2, 'workflow/intents/2026-09-12-pj2.md', INTENT('pj2', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=fix level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3790
|
-
w(T2, 'workflow/plans/2026-09-12-pj2.md', PLAN('pj2', '状态: done\n级别: L1'));
|
|
3791
|
-
w(T2, 'workflow/incidents/2026-09-12-pj3.md', `---\n状态: open\n级别: L1\n发现: 2026-09-12\n预判: kind=require level=L1\n---\n# INCIDENT — pj3\n\n${三件套(false)}`);
|
|
3792
|
-
w(T2, 'workflow/plans/2026-09-12-pj3.md', PLAN('pj3', '状态: draft\n级别: L1'));
|
|
3793
|
-
r = run(T2);
|
|
3794
|
-
check('检查25 类型不符(intent 预判 fix / incident 预判 require)→ 双 WARN「预判类型不符」且 exit 0',
|
|
3795
|
-
r.status === 0 && (outOf(r).match(/预判类型不符/g) || []).length === 2, `exit=${r.status}\n${outOf(r)}`);
|
|
3796
|
-
rmfix(T2);
|
|
3797
|
-
// ③ 级别不符:预判 L2 落位 L1 → WARN「预判级别不符」含判低信号
|
|
3798
|
-
const T3 = mkfix();
|
|
3799
|
-
w(T3, 'workflow/intents/2026-09-12-pj4.md', INTENT('pj4', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=require level=L2', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3800
|
-
w(T3, 'workflow/plans/2026-09-12-pj4.md', PLAN('pj4', '状态: done\n级别: L1'));
|
|
3801
|
-
r = run(T3);
|
|
3802
|
-
check('检查25 预判 L2 落位 L1 → WARN「预判级别不符」含判低信号',
|
|
3803
|
-
r.status === 0 && outOf(r).includes('预判级别不符') && outOf(r).includes('判低信号'), `exit=${r.status}\n${outOf(r)}`);
|
|
3804
|
-
rmfix(T3);
|
|
3805
|
-
// ④ 词表非法 kind / 整字段不可解析 → WARN「预判字段不可解析」(夹具取 approved 活文档——终态 done
|
|
3806
|
-
// 自 2026-10-15 起形态类告警跳过,见 ⑥)
|
|
3807
|
-
const T4 = mkfix();
|
|
3808
|
-
w(T4, 'workflow/intents/2026-09-12-pj5.md', INTENT('pj5', '状态: approved\n级别: L1\n日期: 2026-09-12\n预判: kind=bug level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3809
|
-
w(T4, 'workflow/plans/2026-09-12-pj5.md', PLAN('pj5', '状态: draft\n级别: L1'));
|
|
3810
|
-
r = run(T4);
|
|
3811
|
-
check('检查25 kind=bug 不在词表 → WARN「预判字段不可解析」',
|
|
3812
|
-
r.status === 0 && outOf(r).includes('预判字段不可解析'), `exit=${r.status}\n${outOf(r)}`);
|
|
3813
|
-
rmfix(T4);
|
|
3814
|
-
const T5 = mkfix();
|
|
3815
|
-
w(T5, 'workflow/intents/2026-09-12-pj6.md', INTENT('pj6', '状态: approved\n级别: L1\n日期: 2026-09-12\n预判: 这是个修复', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3816
|
-
w(T5, 'workflow/plans/2026-09-12-pj6.md', PLAN('pj6', '状态: draft\n级别: L1'));
|
|
3817
|
-
r = run(T5);
|
|
3818
|
-
check('检查25 预判字段无 kv 形态 → WARN「预判字段不可解析」',
|
|
3819
|
-
r.status === 0 && outOf(r).includes('预判字段不可解析'), `exit=${r.status}\n${outOf(r)}`);
|
|
3820
|
-
rmfix(T5);
|
|
3821
|
-
// ⑥ 终态跳过(2026-10-15):done 文档词表/形态类告警不产——终态受检查 15 内容绑定不可修订,
|
|
3822
|
-
// WARN 永不可消(「不可消除噪声」红线);类型/级别比对照常(level 一致;类型告警不在本断言面)
|
|
3823
|
-
{
|
|
3824
|
-
const T6 = mkfix();
|
|
3825
|
-
w(T6, 'workflow/intents/2026-09-12-pj7.md', INTENT('pj7', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=bug level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3826
|
-
w(T6, 'workflow/plans/2026-09-12-pj7.md', PLAN('pj7', '状态: done\n级别: L1'));
|
|
3827
|
-
r = run(T6);
|
|
3828
|
-
check('检查25 终态 done 词表外 kind → 形态类告警不产(2026-10-15 终态跳过)',
|
|
3829
|
-
r.status === 0 && !outOf(r).includes('预判字段不可解析'), `exit=${r.status}\n${outOf(r)}`);
|
|
3830
|
-
rmfix(T6);
|
|
3831
|
-
}
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
|
-
// ---- 检查 26:闭环漏斗「协议前」占比动作阈值(相邻两月均 >30% → WARN;消费 gen-workflow-metrics 产物)----
|
|
3835
|
-
{
|
|
3836
|
-
// 漏斗表构造器(kit gen-workflow-metrics.mjs 产出的表头/节名;单元格带占比与裸数字两种形态都测)
|
|
3837
|
-
const mkMetrics = (rows) => `<!-- GENERATED:BEGIN -->
|
|
3838
|
-
|
|
3839
|
-
## 闭环漏斗(机器口径,源:.agents/confirmations.jsonl)
|
|
3840
|
-
|
|
3841
|
-
| 月份 | 收口 | 完整链 | 协议前 | 一次通过 | 返工件 | 中位周期(天) | 台账行数 |
|
|
3842
|
-
|---|---|---|---|---|---|---|---|
|
|
3843
|
-
${rows.map((r) => `| ${r} |`).join('\n')}
|
|
3844
|
-
|
|
3845
|
-
## 来源与影响环境维度(源:incident frontmatter 枚举)
|
|
3846
|
-
|
|
3847
|
-
| 月份 | incident | 来源(谁发现) | 影响环境 |
|
|
3848
|
-
|---|---|---|---|
|
|
3849
|
-
| 2026-10 | 0 | self 0 / user 0 / test 0 / monitor 0 | dev 0 / staging 0 / prod 0 / none 0 |
|
|
3850
|
-
|
|
3851
|
-
<!-- GENERATED:END -->
|
|
3852
|
-
`;
|
|
3853
|
-
// ① 无 metrics.md(装户/新仓未启用观测)→ 静默跳过零告警
|
|
3854
|
-
const T = mkfix();
|
|
3855
|
-
let r = run(T);
|
|
3856
|
-
check('检查26 无 metrics.md → 跳过零告警 exit 0',
|
|
3857
|
-
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3858
|
-
rmfix(T);
|
|
3859
|
-
// ② 相邻两月均 >30% → WARN「漏斗协议前超标」且不阻断(exit 0)
|
|
3860
|
-
const T2 = mkfix();
|
|
3861
|
-
w(T2, 'workflow/metrics.md', mkMetrics([
|
|
3862
|
-
'2026-09 | 10 | 4 | 6(60%) ⚠️ | 3 | 1 | 1.0 | 20',
|
|
3863
|
-
'2026-10 | 10 | 4 | 5(50%) ⚠️ | 4 | 1 | 1.0 | 20',
|
|
3864
|
-
]));
|
|
3865
|
-
r = run(T2);
|
|
3866
|
-
check('检查26 相邻两月协议前占比均 >30% → WARN 且 exit 0',
|
|
3867
|
-
r.status === 0 && outOf(r).includes('漏斗协议前超标') && outOf(r).includes('2026-09 60% → 2026-10 50%'), `exit=${r.status}\n${outOf(r)}`);
|
|
3868
|
-
rmfix(T2);
|
|
3869
|
-
// ③ 仅单月 >30%(相邻月正常)→ 零告警
|
|
3870
|
-
const T3 = mkfix();
|
|
3871
|
-
w(T3, 'workflow/metrics.md', mkMetrics([
|
|
3872
|
-
'2026-09 | 10 | 9 | 1(10%) | 8 | 1 | 1.0 | 20',
|
|
3873
|
-
'2026-10 | 10 | 4 | 5(50%) ⚠️ | 4 | 1 | 1.0 | 20',
|
|
3874
|
-
]));
|
|
3875
|
-
r = run(T3);
|
|
3876
|
-
check('检查26 仅单月超标(相邻月正常)→ 零告警 exit 0',
|
|
3877
|
-
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3878
|
-
rmfix(T3);
|
|
3879
|
-
// ④ 两月都超标但不相邻(跳月 09→11)→ 零告警(判据是日历相邻)
|
|
3880
|
-
const T4 = mkfix();
|
|
3881
|
-
w(T4, 'workflow/metrics.md', mkMetrics([
|
|
3882
|
-
'2026-09 | 10 | 4 | 6(60%) ⚠️ | 3 | 1 | 1.0 | 20',
|
|
3883
|
-
'2026-11 | 10 | 4 | 5(50%) ⚠️ | 4 | 1 | 1.0 | 20',
|
|
3884
|
-
]));
|
|
3885
|
-
r = run(T4);
|
|
3886
|
-
check('检查26 超标两月不相邻(跳月)→ 零告警 exit 0',
|
|
3887
|
-
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3888
|
-
rmfix(T4);
|
|
3889
|
-
// ⑤ 收口=0 的月无占比(0/0 不判),且存量裸数字单元格同样可解析
|
|
3890
|
-
const T5 = mkfix();
|
|
3891
|
-
w(T5, 'workflow/metrics.md', mkMetrics([
|
|
3892
|
-
'2026-09 | 0 | 0 | 0 | 0 | 0 | — | 1',
|
|
3893
|
-
'2026-10 | 10 | 4 | 5 | 4 | 1 | 1.0 | 20',
|
|
3894
|
-
]));
|
|
3895
|
-
r = run(T5);
|
|
3896
|
-
check('检查26 收口=0 月不判 0/0;裸数字单元格可解析(10 月 50% 单月不触发)',
|
|
3897
|
-
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3898
|
-
rmfix(T5);
|
|
3899
|
-
// ⑥ 存量裸数字 + 相邻两月超标 → 照判(解析只取首个整数,混合形态可读)
|
|
3900
|
-
const T6 = mkfix();
|
|
3901
|
-
w(T6, 'workflow/metrics.md', mkMetrics([
|
|
3902
|
-
'2026-09 | 10 | 4 | 6 | 3 | 1 | 1.0 | 20',
|
|
3903
|
-
'2026-10 | 10 | 4 | 5 | 4 | 1 | 1.0 | 20',
|
|
3904
|
-
]));
|
|
3905
|
-
r = run(T6);
|
|
3906
|
-
check('检查26 存量裸数字单元格相邻两月超标 → 照判 WARN',
|
|
3907
|
-
r.status === 0 && outOf(r).includes('漏斗协议前超标') && outOf(r).includes('2026-09 60% → 2026-10 50%'), `exit=${r.status}\n${outOf(r)}`);
|
|
3908
|
-
rmfix(T6);
|
|
3909
|
-
}
|
|
3910
|
-
|
|
3911
|
-
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
3912
|
-
process.exit(fail ? 1 : 0);
|