sillyspec 3.27.2 → 3.27.6
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/.claude/skills/sillyspec-execute/SKILL.md +8 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +2 -1
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +93 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +47 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +214 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +102 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +70 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +64 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
- package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +2 -0
- package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +20 -0
- package/.claude/worktrees/sillyspec-data-model-research/README.md +203 -0
- package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +91 -0
- package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +2 -0
- package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +977 -0
- package/.claude/worktrees/sillyspec-data-model-research/package.json +39 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +16 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +256 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +25 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +95 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +679 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +526 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +357 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +803 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +290 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +270 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +65 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +109 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +202 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +88 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +328 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +97 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +480 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +20 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +55 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/db.js +381 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +517 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +155 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +245 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +246 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +121 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +825 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +393 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +107 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +1099 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +420 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +78 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +73 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +150 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +130 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +892 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/index.js +2798 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/init.js +626 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +231 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +128 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +104 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +543 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +127 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +166 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +222 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +580 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +336 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +355 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +36 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +594 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +265 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +1304 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +115 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +852 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +77 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +1595 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +1552 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +1259 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +156 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +1084 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +208 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +128 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +843 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +186 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +269 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +1562 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +584 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run.js +23 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +388 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +512 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +95 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +414 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +597 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +62 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +50 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +358 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +171 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +550 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +889 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +597 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +199 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +236 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +617 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +145 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +540 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +1275 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +43 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +29 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +514 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +1344 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +650 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +167 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +608 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +64 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +246 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +1470 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +1498 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +210 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +1718 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +277 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/version.js +19 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +600 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +125 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +1446 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +576 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +1715 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +23 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +28 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +12 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +32 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +79 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +132 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +93 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +47 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +214 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +102 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +70 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +64 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
- package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +6 -0
- package/.worktrees/8d2d0392/.gitattributes +2 -0
- package/.worktrees/8d2d0392/.husky/pre-push +20 -0
- package/.worktrees/8d2d0392/README.md +203 -0
- package/.worktrees/8d2d0392/SKILL.md +91 -0
- package/.worktrees/8d2d0392/architecture-analysis.md +448 -0
- package/.worktrees/8d2d0392/bin/sillyspec.js +2 -0
- package/.worktrees/8d2d0392/eval/README.md +86 -0
- package/.worktrees/8d2d0392/eval/eval.config.json +28 -0
- package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +63 -0
- package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +27 -0
- package/.worktrees/8d2d0392/eval/lib/config.mjs +82 -0
- package/.worktrees/8d2d0392/eval/lib/db.mjs +76 -0
- package/.worktrees/8d2d0392/eval/lib/report.mjs +120 -0
- package/.worktrees/8d2d0392/eval/lib/runner.mjs +157 -0
- package/.worktrees/8d2d0392/eval/lib/tasks.mjs +73 -0
- package/.worktrees/8d2d0392/eval/lib/util.mjs +107 -0
- package/.worktrees/8d2d0392/eval/lib/workdir.mjs +30 -0
- package/.worktrees/8d2d0392/eval/run.mjs +83 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +28 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +31 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +5 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +38 -0
- package/.worktrees/8d2d0392/logo.jpg +0 -0
- package/.worktrees/8d2d0392/package-lock.json +977 -0
- package/.worktrees/8d2d0392/package.json +41 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +16 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +256 -0
- package/.worktrees/8d2d0392/packages/dashboard/package.json +25 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +95 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/index.js +679 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +526 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +357 -0
- package/.worktrees/8d2d0392/src/agent-session-log.js +803 -0
- package/.worktrees/8d2d0392/src/change-list.js +290 -0
- package/.worktrees/8d2d0392/src/change-risk-profile.js +339 -0
- package/.worktrees/8d2d0392/src/check-primitives.js +65 -0
- package/.worktrees/8d2d0392/src/classify-change.js +109 -0
- package/.worktrees/8d2d0392/src/commit-suggest.js +202 -0
- package/.worktrees/8d2d0392/src/config-cat.js +88 -0
- package/.worktrees/8d2d0392/src/config-schema.js +328 -0
- package/.worktrees/8d2d0392/src/constants.js +97 -0
- package/.worktrees/8d2d0392/src/contract-matrix.js +533 -0
- package/.worktrees/8d2d0392/src/datetime.js +20 -0
- package/.worktrees/8d2d0392/src/db-engine.js +55 -0
- package/.worktrees/8d2d0392/src/db.js +381 -0
- package/.worktrees/8d2d0392/src/decision-distill.js +517 -0
- package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +155 -0
- package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +245 -0
- package/.worktrees/8d2d0392/src/dispatch/probe.js +246 -0
- package/.worktrees/8d2d0392/src/dispatch/strategy.js +121 -0
- package/.worktrees/8d2d0392/src/docs-check.js +825 -0
- package/.worktrees/8d2d0392/src/docs-debt.js +393 -0
- package/.worktrees/8d2d0392/src/docs-gate.js +107 -0
- package/.worktrees/8d2d0392/src/doctor-diagnostics.js +1099 -0
- package/.worktrees/8d2d0392/src/endpoint-extractor.js +409 -0
- package/.worktrees/8d2d0392/src/foreign-declared.js +78 -0
- package/.worktrees/8d2d0392/src/fs-atomic.js +73 -0
- package/.worktrees/8d2d0392/src/git-helper.js +150 -0
- package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +130 -0
- package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +892 -0
- package/.worktrees/8d2d0392/src/index.js +2854 -0
- package/.worktrees/8d2d0392/src/init.js +626 -0
- package/.worktrees/8d2d0392/src/knowledge-match.js +231 -0
- package/.worktrees/8d2d0392/src/local-detect.js +128 -0
- package/.worktrees/8d2d0392/src/local-register.js +104 -0
- package/.worktrees/8d2d0392/src/machine-interface.js +543 -0
- package/.worktrees/8d2d0392/src/migrate.js +127 -0
- package/.worktrees/8d2d0392/src/module-impact.js +166 -0
- package/.worktrees/8d2d0392/src/module-resolve.js +222 -0
- package/.worktrees/8d2d0392/src/modules.js +580 -0
- package/.worktrees/8d2d0392/src/plan-adopt-waves.js +126 -0
- package/.worktrees/8d2d0392/src/progress/change-registry.js +336 -0
- package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +355 -0
- package/.worktrees/8d2d0392/src/progress/shared.js +36 -0
- package/.worktrees/8d2d0392/src/progress/stage-machine.js +594 -0
- package/.worktrees/8d2d0392/src/progress/step-store.js +265 -0
- package/.worktrees/8d2d0392/src/progress.js +1304 -0
- package/.worktrees/8d2d0392/src/quick-recommend.js +115 -0
- package/.worktrees/8d2d0392/src/quicklog.js +883 -0
- package/.worktrees/8d2d0392/src/review-tier.js +77 -0
- package/.worktrees/8d2d0392/src/run/command.js +1641 -0
- package/.worktrees/8d2d0392/src/run/complete-handlers.js +1552 -0
- package/.worktrees/8d2d0392/src/run/complete.js +1259 -0
- package/.worktrees/8d2d0392/src/run/concurrent-detect.js +156 -0
- package/.worktrees/8d2d0392/src/run/gates.js +1094 -0
- package/.worktrees/8d2d0392/src/run/multi-repo-context.js +208 -0
- package/.worktrees/8d2d0392/src/run/next.js +128 -0
- package/.worktrees/8d2d0392/src/run/prompt.js +843 -0
- package/.worktrees/8d2d0392/src/run/quick-audit.js +186 -0
- package/.worktrees/8d2d0392/src/run/scan-profile.js +269 -0
- package/.worktrees/8d2d0392/src/run/shared.js +1562 -0
- package/.worktrees/8d2d0392/src/run/stage.js +587 -0
- package/.worktrees/8d2d0392/src/run.js +23 -0
- package/.worktrees/8d2d0392/src/scan-diff.js +388 -0
- package/.worktrees/8d2d0392/src/scan-postcheck.js +524 -0
- package/.worktrees/8d2d0392/src/scan-staleness.js +95 -0
- package/.worktrees/8d2d0392/src/setup.js +414 -0
- package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +597 -0
- package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +62 -0
- package/.worktrees/8d2d0392/src/spec-dir-typo.js +50 -0
- package/.worktrees/8d2d0392/src/spec-sync.js +358 -0
- package/.worktrees/8d2d0392/src/stage-contract-engine.js +171 -0
- package/.worktrees/8d2d0392/src/stage-contract-spec.js +551 -0
- package/.worktrees/8d2d0392/src/stage-contract.js +948 -0
- package/.worktrees/8d2d0392/src/stage-review.js +597 -0
- package/.worktrees/8d2d0392/src/stages/archive.js +199 -0
- package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +285 -0
- package/.worktrees/8d2d0392/src/stages/brainstorm.js +624 -0
- package/.worktrees/8d2d0392/src/stages/cmd-existence.js +145 -0
- package/.worktrees/8d2d0392/src/stages/doctor.js +540 -0
- package/.worktrees/8d2d0392/src/stages/execute.js +1277 -0
- package/.worktrees/8d2d0392/src/stages/explore.js +43 -0
- package/.worktrees/8d2d0392/src/stages/index.js +29 -0
- package/.worktrees/8d2d0392/src/stages/knowledge.js +514 -0
- package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +1398 -0
- package/.worktrees/8d2d0392/src/stages/plan.js +651 -0
- package/.worktrees/8d2d0392/src/stages/quick.js +167 -0
- package/.worktrees/8d2d0392/src/stages/scan.js +608 -0
- package/.worktrees/8d2d0392/src/stages/status.js +64 -0
- package/.worktrees/8d2d0392/src/stages/verify.js +246 -0
- package/.worktrees/8d2d0392/src/sync.js +1470 -0
- package/.worktrees/8d2d0392/src/task-review.js +1498 -0
- package/.worktrees/8d2d0392/src/taskcard-placeholders.js +21 -0
- package/.worktrees/8d2d0392/src/taskcard.js +217 -0
- package/.worktrees/8d2d0392/src/verify-postcheck.js +1718 -0
- package/.worktrees/8d2d0392/src/verify-probes.js +327 -0
- package/.worktrees/8d2d0392/src/version.js +19 -0
- package/.worktrees/8d2d0392/src/workflow.js +600 -0
- package/.worktrees/8d2d0392/src/workspace.js +125 -0
- package/.worktrees/8d2d0392/src/worktree-apply.js +1611 -0
- package/.worktrees/8d2d0392/src/worktree-deps.js +576 -0
- package/.worktrees/8d2d0392/src/worktree.js +1838 -0
- package/.worktrees/8d2d0392/templates/claude-instruction.md +23 -0
- package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +29 -0
- package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +12 -0
- package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +32 -0
- package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +79 -0
- package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +132 -0
- package/architecture-analysis.md +448 -0
- package/eval/README.md +86 -0
- package/eval/eval.config.json +28 -0
- package/eval/lib/adapters/cli.mjs +63 -0
- package/eval/lib/adapters/dryrun.mjs +27 -0
- package/eval/lib/config.mjs +82 -0
- package/eval/lib/db.mjs +76 -0
- package/eval/lib/report.mjs +120 -0
- package/eval/lib/runner.mjs +157 -0
- package/eval/lib/tasks.mjs +73 -0
- package/eval/lib/util.mjs +107 -0
- package/eval/lib/workdir.mjs +30 -0
- package/eval/run.mjs +83 -0
- package/eval/tasks/001-slugify/files/package.json +6 -0
- package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
- package/eval/tasks/001-slugify/task.json +6 -0
- package/eval/tasks/001-slugify/verify.mjs +28 -0
- package/eval/tasks/002-each-day/files/package.json +6 -0
- package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
- package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
- package/eval/tasks/002-each-day/task.json +6 -0
- package/eval/tasks/002-each-day/verify.mjs +31 -0
- package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
- package/eval/tasks/003-word-report/files/package.json +6 -0
- package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
- package/eval/tasks/003-word-report/task.json +6 -0
- package/eval/tasks/003-word-report/verify.mjs +38 -0
- package/package.json +4 -2
- package/src/agent-session-log.js +45 -5
- package/src/change-risk-profile.js +83 -14
- package/src/config-schema.js +18 -3
- package/src/contract-matrix.js +90 -29
- package/src/datetime.js +20 -0
- package/src/decision-distill.js +517 -0
- package/src/dispatch/backends/local-agent.js +4 -4
- package/src/docs-check.js +227 -0
- package/src/docs-debt.js +187 -24
- package/src/doctor-diagnostics.js +102 -2
- package/src/endpoint-extractor.js +41 -52
- package/src/foreign-declared.js +155 -0
- package/src/index.js +95 -13
- package/src/init.js +9 -7
- package/src/knowledge-match.js +107 -6
- package/src/local-register.js +7 -1
- package/src/module-resolve.js +12 -1
- package/src/modules.js +2 -1
- package/src/plan-adopt-waves.js +126 -0
- package/src/quicklog.js +50 -4
- package/src/run/command.js +124 -44
- package/src/run/complete-handlers.js +48 -19
- package/src/run/complete.js +5 -2
- package/src/run/gates.js +104 -22
- package/src/run/prompt.js +84 -42
- package/src/run/quick-audit.js +11 -0
- package/src/run/shared.js +152 -10
- package/src/run/stage.js +8 -5
- package/src/run.js +2 -2
- package/src/scan-postcheck.js +30 -17
- package/src/stage-contract-spec.js +3 -2
- package/src/stage-contract.js +68 -4
- package/src/stages/archive.js +53 -12
- package/src/stages/brainstorm-auto.js +50 -1
- package/src/stages/brainstorm.js +13 -0
- package/src/stages/doctor.js +157 -1
- package/src/stages/execute.js +32 -6
- package/src/stages/plan-postcheck.js +76 -17
- package/src/stages/plan.js +9 -4
- package/src/stages/quick.js +6 -1
- package/src/stages/verify.js +32 -15
- package/src/sync.js +41 -3
- package/src/task-review.js +2 -2
- package/src/taskcard-placeholders.js +21 -0
- package/src/taskcard.js +43 -1
- package/src/verify-postcheck.js +315 -13
- package/src/verify-probes.js +73 -10
- package/src/worktree-apply.js +569 -227
- package/src/worktree-deps.js +150 -1
- package/src/worktree.js +285 -77
- package/templates/prompts/taskcard-rules.md +2 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 路径项归一化:去反引号、去行内括号注释(`src/foo.js (新增)` / `src/foo.js(说明)`)、
|
|
5
|
+
* 统一为正斜杠、去首尾空白与尾部斜杠。
|
|
6
|
+
* design 清单 cell 与 task allowed_paths 共用此归一化(两处写法都可能带注释/反引号)。
|
|
7
|
+
* @param {string} raw
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
export function normalizePath(raw) {
|
|
11
|
+
if (!raw) return ''
|
|
12
|
+
return raw
|
|
13
|
+
.replace(/`/g, '')
|
|
14
|
+
.replace(/\s*([^)]*)\s*$/, '')
|
|
15
|
+
.replace(/\s*\([^)]*\)\s*$/, '')
|
|
16
|
+
.replace(/\\/g, '/')
|
|
17
|
+
.replace(/\/+$/, '')
|
|
18
|
+
.trim()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 组合路径单元格拆分(坑 design-combined-cell-mismatch,2026-08-22 实证:"router.py + service.py"
|
|
23
|
+
* 一行两文件整体当单路径,字面匹配对账不过)。拆分符:+ 、 / | ; ;——每 token 独立校验
|
|
24
|
+
* (looksLikePath 不过的丢弃,防自由文本混入);无分隔符返回单元素数组(零变化)。
|
|
25
|
+
* 不含空白要求("a.py+b.py" 与 "a.py + b.py" 都拆)。
|
|
26
|
+
* @param {string} cell 路径单元格(normalizePath 后)
|
|
27
|
+
* @returns {string[]}
|
|
28
|
+
*/
|
|
29
|
+
function splitCombinedPaths(cell) {
|
|
30
|
+
const raw = String(cell || '').trim()
|
|
31
|
+
if (!raw) return []
|
|
32
|
+
const tokens = raw.split(/\s*[+|;;、]\s*|//).map(t => t.trim()).filter(Boolean)
|
|
33
|
+
const valid = tokens.filter(t => !isPlaceholder(t) && looksLikePath(t) && !t.startsWith('.sillyspec/'))
|
|
34
|
+
return valid.length > 0 ? valid : (looksLikePath(raw) ? [raw] : [])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* glob → 正则:双星(globstar)跨零或多段目录(含零段,bash 语义)、
|
|
39
|
+
* 单星 → 单段([^/]*)、问号 → 单字符([^/]),其余转义。
|
|
40
|
+
* pattern 不含通配符时返回 false(调用方先用完全相等判断)。
|
|
41
|
+
* 不支持字符类(design/allowed_paths 实际不写,遇方括号按字面转义)。
|
|
42
|
+
* @param {string} str
|
|
43
|
+
* @param {string} pattern
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
export function globMatch(str, pattern) {
|
|
47
|
+
if (!/[*?]/.test(pattern)) return false
|
|
48
|
+
let re = '^'
|
|
49
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
50
|
+
const ch = pattern[i]
|
|
51
|
+
if (ch === '*') {
|
|
52
|
+
if (pattern[i + 1] === '*') {
|
|
53
|
+
// ** 跨零或多段:吞掉紧随的 /,输出 (?:.*/)? 让 src/**/a.js 也能匹配 src/a.js
|
|
54
|
+
if (pattern[i + 2] === '/') { re += '(?:.*/)?'; i += 2 }
|
|
55
|
+
else { re += '.*'; i++ }
|
|
56
|
+
} else {
|
|
57
|
+
re += '[^/]*'
|
|
58
|
+
}
|
|
59
|
+
} else if (ch === '?') {
|
|
60
|
+
re += '[^/]'
|
|
61
|
+
} else {
|
|
62
|
+
re += ch.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
re += '$'
|
|
66
|
+
try { return new RegExp(re).test(str) } catch { return false }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 双向容差匹配:design 清单文件 vs task allowed_paths(或脏文件 vs 清单)。
|
|
71
|
+
* 命中条件(任一):完全相等 / 目录前缀包含(双向)/ glob 通配(双向)。
|
|
72
|
+
* @param {string} a
|
|
73
|
+
* @param {string} b
|
|
74
|
+
* @returns {boolean}
|
|
75
|
+
*/
|
|
76
|
+
export function pathMatches(a, b) {
|
|
77
|
+
a = normalizePath(a)
|
|
78
|
+
b = normalizePath(b)
|
|
79
|
+
if (!a || !b) return false
|
|
80
|
+
if (a === b) return true
|
|
81
|
+
if (a.startsWith(b + '/') || b.startsWith(a + '/')) return true
|
|
82
|
+
if (globMatch(a, b) || globMatch(b, a)) return true
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 「文件变更清单」章节标题同义词(与 src/stage-contract.js 的识别集对齐,
|
|
88
|
+
* 避免两个校验器对「有没有清单」给出矛盾结论)。
|
|
89
|
+
* 容忍可选编号前缀(`## 6. 文件变更清单` / `## 6) 文件变更清单`)——
|
|
90
|
+
* brainstorm Step11 模板鼓励 design 章节带编号,编号前缀不应让 plan-postcheck 解析失败。
|
|
91
|
+
*/
|
|
92
|
+
const FILE_LIST_SECTION_RE = /^#{2,3}\s*(?:\d+[.)]\s*)?(文件变更清单|变更文件清单|文件清单|File Changes|Files to Change)/im
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* exclude 子标题词集:「不修改/暂缓/保留」类 —— 其下路径从清单移除,不强制 task 覆盖。
|
|
96
|
+
* 用排除集而非白名单:容忍「改动/变更/涉及」等近义 include 写法。
|
|
97
|
+
*/
|
|
98
|
+
const EXCLUDE_SUBSECTION_RE = /不修改|不变|保留|无变更|未变更|不改动|暂不|暂缓|暂定|待定|后续|未改|无需|不涉及/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* operation 词→归一化标签。design 清单「操作」列 cell 与分类列表子标题共用。
|
|
102
|
+
* 用途:verify 删除探针对账——声明「新增/修改」却整文件删除 = 高风险信号。
|
|
103
|
+
*/
|
|
104
|
+
const OP_KEYWORDS = [
|
|
105
|
+
{ re: /^(新增|新建|创建|添加|add|new|create)$/i, op: '新增' },
|
|
106
|
+
{ re: /^(修改|更新|调整|改动|变更|重写|modify|update|change)$/i, op: '修改' },
|
|
107
|
+
{ re: /^(删除|移除|去掉|remove|delete|drop)$/i, op: '删除' },
|
|
108
|
+
{ re: /^(重命名|改名|rename|move)$/i, op: '重命名' },
|
|
109
|
+
]
|
|
110
|
+
function classifyOperation(raw) {
|
|
111
|
+
if (!raw) return null
|
|
112
|
+
const hit = OP_KEYWORDS.find(k => k.re.test(raw.trim()))
|
|
113
|
+
return hit ? hit.op : null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 分类列表子标题 → operation(`### 删除文件` → '删除')。不命中返回 null。
|
|
118
|
+
* ⚠️ 调用方必须先判 EXCLUDE_SUBSECTION_RE:「### 不修改文件」含「修改」二字,
|
|
119
|
+
* 会被此处误匹配为「修改」——exclude 永远优先,命中 exclude 不跑 OP 映射。
|
|
120
|
+
*/
|
|
121
|
+
const OP_SUBSECTION_RE = [
|
|
122
|
+
{ re: /新增|新建|创建/, op: '新增' },
|
|
123
|
+
{ re: /修改|更新|改动|重写/, op: '修改' },
|
|
124
|
+
{ re: /删除|移除|去掉/, op: '删除' },
|
|
125
|
+
{ re: /重命名|改名/, op: '重命名' },
|
|
126
|
+
]
|
|
127
|
+
function classifySubsectionOp(title) {
|
|
128
|
+
const hit = OP_SUBSECTION_RE.find(k => k.re.test(title))
|
|
129
|
+
return hit ? hit.op : null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** 表头列定位:cell 整体匹配操作类列名(与 isPathHeaderCell 对偶,用于定位 operation 列) */
|
|
133
|
+
function isOperationHeaderCell(c) {
|
|
134
|
+
return /^(操作|类型|变更类型|改动类型|变更|operation|type|action|op)$/i.test(c)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isPlaceholder(p) {
|
|
138
|
+
return !p || p === '—' || p === '-' || /^(n\/?a|无|none|-+)$/i.test(p)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** 表头列定位:cell 整体匹配路径类词 */
|
|
142
|
+
function isPathHeaderCell(c) {
|
|
143
|
+
return /^(文件路径|文件名|文件|路径|filepath|filename|file\s*path|path)$/i.test(c)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 路径合理性兜底:合法路径必含目录分隔 `/` 或扩展名点;
|
|
148
|
+
* 两者皆无时仅保留纯 ASCII 单词 token(Dockerfile / Makefile / LICENSE 这类无扩展名文件名),
|
|
149
|
+
* 丢弃含空格/中文的脏描述(如 design 表格第二列误写的「frontend 组件测试」「后端逻辑」)。
|
|
150
|
+
* 防止把自由文本当路径计入 fileCount,虚高 review-tier 分级(误推 independent)。
|
|
151
|
+
*/
|
|
152
|
+
function looksLikePath(p) {
|
|
153
|
+
if (!p) return false
|
|
154
|
+
if (p.includes('/') || p.includes('.')) return true
|
|
155
|
+
return /^[\w@-]+$/.test(p)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 「顺带修复」标记:design §6 清单里标注的、不属本次变更 task 边界但合规修的预存债文件
|
|
160
|
+
* (CLAUDE.md 规则20 鼓励)。assess 的 allowed_paths 校验对 incidental 文件豁免(坑
|
|
161
|
+
* worktree-execute-apply-friction 坑1/4)。标记载体:表格「说明」列写「顺带修复:xxx」,
|
|
162
|
+
* 或路径 cell / 列表项的括号注释「(顺带修复)」。
|
|
163
|
+
*/
|
|
164
|
+
const INCIDENTAL_RE = /顺带修复|附带修复|顺带|drive-?by|incidental/i
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 从 design.md 解析文件变更清单(含 incidental 标记)。兼容两种真实写法:
|
|
168
|
+
* ① 表格:`| 操作 | 文件路径 | 说明 |`(brainstorm 模板默认)
|
|
169
|
+
* ② 分类列表:`### 新增文件` / `### 修改文件` / `### 不修改文件` 下的 `- path`
|
|
170
|
+
* 表头列顺序自适应(定位「文件/路径/file/path」列,列顺序写反时不会把操作名当路径);
|
|
171
|
+
* 忽略 `.sillyspec/`(keepSillyspecDocs=true 时保留 `.sillyspec/docs/`)与占位符(`—`/`-`/`N/A`/`无`);「不修改/暂缓」子段下的路径会被排除;
|
|
172
|
+
* CRLF 容错。incidental 嗅探:表格非路径列(说明列)+ 路径 cell 原始值(剥注释前的括号内容)+ 列表项原文。
|
|
173
|
+
*
|
|
174
|
+
* 内核函数:parseFileChangeList(Set 包装,向后兼容)与 parseFileChangeListDetailed 共用,
|
|
175
|
+
* 单一真相源,避免两处各自重写清单解析漂移。
|
|
176
|
+
* @param {string} designMdPath - design.md 文件路径
|
|
177
|
+
* @returns {Array<{ path: string, operation: string|null, incidental: boolean }>}(顺序按首次出现,exclude 子段移除)
|
|
178
|
+
*/
|
|
179
|
+
function _parseFileListDetailed(designMdPath, { keepSillyspecDocs = false } = {}) {
|
|
180
|
+
if (!designMdPath || !existsSync(designMdPath)) return []
|
|
181
|
+
|
|
182
|
+
const content = readFileSync(designMdPath, 'utf8').replace(/\r\n/g, '\n')
|
|
183
|
+
|
|
184
|
+
const sectionMatch = content.match(FILE_LIST_SECTION_RE)
|
|
185
|
+
if (!sectionMatch) return []
|
|
186
|
+
|
|
187
|
+
// 从标题后开始,截取到下一个 ## 标题或文件末尾
|
|
188
|
+
const afterSection = content.slice(sectionMatch.index + sectionMatch[0].length)
|
|
189
|
+
const nextSectionMatch = afterSection.match(/^##\s/m)
|
|
190
|
+
const relevantContent = nextSectionMatch
|
|
191
|
+
? afterSection.slice(0, nextSectionMatch.index)
|
|
192
|
+
: afterSection
|
|
193
|
+
|
|
194
|
+
const lines = relevantContent.split('\n')
|
|
195
|
+
let headerSkipped = false
|
|
196
|
+
let pathColIdx = 1 // 默认第 2 列;解析表头后可重定位
|
|
197
|
+
let opColIdx = -1 // 操作列下标(表头扫描后定位;-1 = 无操作列 → operation=null)
|
|
198
|
+
let listMode = 'include' // include | exclude(分类列表子段)
|
|
199
|
+
let currentOp = null // 分类列表子标题推导的 operation(表格模式不用)
|
|
200
|
+
const entries = new Map() // path -> { path, operation, incidental }(exclude 子段 delete)
|
|
201
|
+
|
|
202
|
+
for (const line of lines) {
|
|
203
|
+
// 分类列表子标题:### 新增文件 / ### 修改文件 / ### 不修改文件
|
|
204
|
+
const subHeader = line.match(/^###\s+(.+?)\s*$/)
|
|
205
|
+
if (subHeader) {
|
|
206
|
+
// ⚠️ EXCLUDE 优先:「### 不修改文件」含「修改」二字,会被 classifySubsectionOp 误匹配。
|
|
207
|
+
// 先判 exclude 词集,命中则 exclude 且 currentOp=null,绝不跑 OP 映射。
|
|
208
|
+
if (EXCLUDE_SUBSECTION_RE.test(subHeader[1])) {
|
|
209
|
+
listMode = 'exclude'
|
|
210
|
+
currentOp = null
|
|
211
|
+
} else {
|
|
212
|
+
listMode = 'include'
|
|
213
|
+
currentOp = classifySubsectionOp(subHeader[1])
|
|
214
|
+
}
|
|
215
|
+
continue
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 表格行
|
|
219
|
+
if (line.startsWith('|')) {
|
|
220
|
+
if (/^\|[-:\s|]+\|$/.test(line)) continue // 分隔行
|
|
221
|
+
const cells = line.split('|').slice(1, -1).map(c => c.trim())
|
|
222
|
+
if (cells.length < 2) continue
|
|
223
|
+
|
|
224
|
+
if (!headerSkipped) {
|
|
225
|
+
headerSkipped = true
|
|
226
|
+
// 表头列定位:找「文件/路径/file/path」列,找不到保持默认第 2 列
|
|
227
|
+
const idx = cells.findIndex(isPathHeaderCell)
|
|
228
|
+
if (idx >= 0) pathColIdx = idx
|
|
229
|
+
// 操作列定位:非路径列里找操作类列名(「操作/类型/operation」),找不到 opColIdx 保持 -1
|
|
230
|
+
const opIdx = cells.findIndex((c, i) => i !== pathColIdx && isOperationHeaderCell(c))
|
|
231
|
+
if (opIdx >= 0) opColIdx = opIdx
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const filePath = normalizePath(cells[pathColIdx] || '')
|
|
236
|
+
// 列定位兜底:取到纯操作词(表头未命中且列顺序异常)→ 跳过,避免把「修改」当路径
|
|
237
|
+
if (/^(新增|修改|删除|重命名|new|modify|update|delete|create|rename)$/i.test(filePath)) continue
|
|
238
|
+
if (isPlaceholder(filePath) || (filePath.startsWith('.sillyspec/') && !(keepSillyspecDocs && filePath.startsWith('.sillyspec/docs/')))) continue
|
|
239
|
+
if (!looksLikePath(filePath)) continue // 脏描述兜底:丢弃非路径自由文本(防虚高 fileCount)
|
|
240
|
+
// operation:操作列 cell 分类(无操作列 → null)
|
|
241
|
+
const operation = opColIdx >= 0 ? classifyOperation(cells[opColIdx] || '') : null
|
|
242
|
+
// incidental:说明列(非路径列的所有 cell)+ 路径 cell 原始值(剥注释前的括号内容)
|
|
243
|
+
const incidental = cells.some((c, i) => i !== pathColIdx && INCIDENTAL_RE.test(c))
|
|
244
|
+
|| INCIDENTAL_RE.test(cells[pathColIdx] || '')
|
|
245
|
+
// 组合路径单元格拆分(坑 design-combined-cell-mismatch,2026-08-22 实证:单元格写
|
|
246
|
+
// "router.py + service.py" 一行两文件——整体当单路径字面匹配对账不过,逼文档写法服务
|
|
247
|
+
// 校验器)。拆分符:+ / / / | / 、 / ; / ;——每个 token 独立过校验入表;单路径零变化。
|
|
248
|
+
const pathTokens = splitCombinedPaths(filePath)
|
|
249
|
+
if (listMode === 'exclude') { for (const t of pathTokens) entries.delete(t); continue }
|
|
250
|
+
for (const t of pathTokens) entries.set(t, { path: t, operation, incidental })
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 分类列表项:`- path` / `- \`path\``
|
|
255
|
+
const listItem = line.match(/^\s*-\s+(.+)/)
|
|
256
|
+
if (listItem) {
|
|
257
|
+
const filePath = normalizePath(listItem[1])
|
|
258
|
+
if (isPlaceholder(filePath) || (filePath.startsWith('.sillyspec/') && !(keepSillyspecDocs && filePath.startsWith('.sillyspec/docs/')))) continue
|
|
259
|
+
if (!looksLikePath(filePath)) continue // 脏描述兜底
|
|
260
|
+
const incidental = INCIDENTAL_RE.test(listItem[1])
|
|
261
|
+
if (listMode === 'exclude') { entries.delete(filePath); continue }
|
|
262
|
+
entries.set(filePath, { path: filePath, operation: currentOp, incidental })
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return [...entries.values()]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 从 design.md 解析文件变更清单(路径集合,向后兼容)。
|
|
271
|
+
* @param {string} designMdPath - design.md 文件路径
|
|
272
|
+
* @param {string} designMdPath - design.md 文件路径
|
|
273
|
+
* @param {{ keepSillyspecDocs?: boolean }} [opts] - keepSillyspecDocs=true 时保留 `.sillyspec/docs/`(dogfood 模块文档=交付物,apply gate 需识别;默认 false 跳过全部 .sillyspec/ 保持 fileCount 判档不变)
|
|
274
|
+
* @returns {Set<string>} 文件路径集合(相对路径,如 "src/worktree.js")
|
|
275
|
+
*/
|
|
276
|
+
export function parseFileChangeList(designMdPath, opts) {
|
|
277
|
+
return new Set(_parseFileListDetailed(designMdPath, opts).map(e => e.path))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 从 design.md 解析文件变更清单(含 operation + incidental 标记)。
|
|
282
|
+
* operation 供 verify 删除探针对账(声明「新增/修改」却整文件删除 = 高风险);
|
|
283
|
+
* incidental 供 assess allowed_paths 豁免。
|
|
284
|
+
* @param {string} designMdPath - design.md 文件路径
|
|
285
|
+
* @param {{ keepSillyspecDocs?: boolean }} [opts] - 同 parseFileChangeList
|
|
286
|
+
* @returns {Array<{ path: string, operation: string|null, incidental: boolean }>}
|
|
287
|
+
*/
|
|
288
|
+
export function parseFileChangeListDetailed(designMdPath, opts) {
|
|
289
|
+
return _parseFileListDetailed(designMdPath, opts)
|
|
290
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* change-risk-profile.js — 变更风险分级检测
|
|
3
|
+
*
|
|
4
|
+
* 根据变更涉及的文件类型、关键词、git diff、brainstorm 产物,
|
|
5
|
+
* 判定 5 级风险(doc-only / unit-sufficient / contract-required /
|
|
6
|
+
* integration-critical / deployment-critical),产出门控验证需求。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============ 向后兼容:旧的 INTEGRATION_CRITICAL_PATTERNS ============
|
|
10
|
+
|
|
11
|
+
const INTEGRATION_CRITICAL_PATTERNS = [
|
|
12
|
+
/\bdaemon\b/i,
|
|
13
|
+
/\bbackend\b/i,
|
|
14
|
+
/\bclient.*api\b/i,
|
|
15
|
+
/\bgrpc\b/i,
|
|
16
|
+
/\bwebsocket\b/i,
|
|
17
|
+
/\bhttp.*client\b/i,
|
|
18
|
+
/\bsession\b/i,
|
|
19
|
+
/\blease\b/i,
|
|
20
|
+
/\bagent.?run\b/i,
|
|
21
|
+
/\blifecycle\b/i,
|
|
22
|
+
/\bstate.?transition\b/i,
|
|
23
|
+
/\bclaim\b/i,
|
|
24
|
+
/\bheartbeat\b/i,
|
|
25
|
+
/\bcross.?process\b/i,
|
|
26
|
+
/\bipc\b/i,
|
|
27
|
+
/\bmessage.?queue\b/i,
|
|
28
|
+
/\bpub.?sub\b/i,
|
|
29
|
+
/\bcli\.ts\b/i,
|
|
30
|
+
/\bmain\.ts\b/i,
|
|
31
|
+
/\bentrypoint\b/i,
|
|
32
|
+
/\bserver\.(js|ts)\b/i,
|
|
33
|
+
/\bbootstrap\b/i,
|
|
34
|
+
/\bdockerfile\b/i,
|
|
35
|
+
/\bdocker.?compose\b/i,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const INTEGRATION_FILE_PATTERNS = [
|
|
39
|
+
/daemon/i,
|
|
40
|
+
/session.?manager/i,
|
|
41
|
+
/agent.?run/i,
|
|
42
|
+
/lifecycle/i,
|
|
43
|
+
/state.?machine/i,
|
|
44
|
+
/lease/i,
|
|
45
|
+
/cli\.(js|ts)$/,
|
|
46
|
+
/main\.(js|ts)$/,
|
|
47
|
+
/server\.(js|ts)$/,
|
|
48
|
+
/bootstrap/i,
|
|
49
|
+
/startup/i,
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* task 级「端到端/deployment-critical」文本判定(execute 批量完成 autoCheckPlanFromReviews 用)。
|
|
54
|
+
*
|
|
55
|
+
* 聚焦需要真实集成/启动验证的 task,区别于 change 级 INTEGRATION_CRITICAL_PATTERNS(含 backend/session
|
|
56
|
+
* 等泛词——task 级用会误伤普通后端 task)。命中端到端集成语义(端到端/真实集成/daemon↔backend/
|
|
57
|
+
* integration test/e2e)或部署启动入口(cli.ts/main.ts/entrypoint/server/bootstrap/startup/docker)。
|
|
58
|
+
* 坑 execute-batch-complete-endtoend-checkbox:这类 task 的 review cannot_verify 不算 checked,
|
|
59
|
+
* 阻断 execute 批量完成(必须 pass),防端到端未真验就被批量放行(verify integration-evidence 兜底外的
|
|
60
|
+
* execute 侧前置门)。
|
|
61
|
+
* @param {string} text task 描述文本(plan.md task 行 + tasks/task-XX.md 内容)
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
const END_TO_END_TASK_KEYWORDS = [
|
|
65
|
+
/端到端/, /真实集成/, /daemon.*backend/i, /integration\s*test/i, /e2e\s*test/i,
|
|
66
|
+
/runtime\s*evidence/i, /运行时证据/,
|
|
67
|
+
/\bcli\.ts\b/i, /\bmain\.ts\b/i, /\bentrypoint\b/i, /\bserver\.(js|ts)\b/i,
|
|
68
|
+
/\bbootstrap\b/i, /\bstartup\b/i, /启动/, /\bdockerfile\b/i, /\bdocker.?compose\b/i,
|
|
69
|
+
]
|
|
70
|
+
export function isEndToEndTaskText(text = '') {
|
|
71
|
+
return END_TO_END_TASK_KEYWORDS.some(re => re.test(text))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============ 门控可执行化(A:报错说人话) ============
|
|
75
|
+
//
|
|
76
|
+
// 历史教训:integration-critical / deployment-critical 门控只报「缺少真实集成证据 / 需要真实
|
|
77
|
+
// 启动验证证据」,agent 看不出具体缺哪一项、要写/做什么才算过,只能靠改结论文案撞墙。
|
|
78
|
+
// 此处把每一项 requiredVerification 的需求(写什么 / 做什么)和判级原因显式导出,
|
|
79
|
+
// 供 stage-contract 拼成可执行的报错信息。
|
|
80
|
+
|
|
81
|
+
/** 每项 requiredVerification 的需求描述 + 字面期望 */
|
|
82
|
+
export const VERIFICATION_NEEDS = {
|
|
83
|
+
unit_tests: {
|
|
84
|
+
desc: '单元测试(verify 必做;在 verify-result.md 写明测试套件与结果)',
|
|
85
|
+
literals: [],
|
|
86
|
+
},
|
|
87
|
+
contract_tests: {
|
|
88
|
+
desc: '契约测试(前后端/跨进程 API parity 对账,写明结论)',
|
|
89
|
+
literals: [],
|
|
90
|
+
},
|
|
91
|
+
real_daemon_backend_integration: {
|
|
92
|
+
desc: '真实 daemon↔backend 集成验证(非仅 mock 单测)。写「真实集成/端到端」的证据;',
|
|
93
|
+
// 同义扩充(坑 verify-literal-evidence-mismatch,2026-08-22 实证:证据第一轮就齐但表述
|
|
94
|
+
// 不含字面词被误拦三轮)——覆盖常见自然表述 + PID 登记句式(verify prompt 要求照写)
|
|
95
|
+
literals: ['端到端', 'integration test', 'e2e test', 'daemon.*backend', '真实集成',
|
|
96
|
+
'runtime evidence', '运行时证据', '联调', '打通', '实际请求', '真实请求', '跨进程',
|
|
97
|
+
'PID 已登记', 'verify-services'],
|
|
98
|
+
},
|
|
99
|
+
runtime_log_evidence: {
|
|
100
|
+
desc: 'Runtime Evidence section + 指向真实日志/证据片段。',
|
|
101
|
+
literals: ['Runtime Evidence', '运行时证据', 'daemon log', '日志片段', '日志关键',
|
|
102
|
+
'日志:', '日志摘录', '日志', 'log 摘录', 'log 片段', '进程日志', 'PID 已登记', 'verify-services'],
|
|
103
|
+
},
|
|
104
|
+
real_startup_once: {
|
|
105
|
+
desc: '真实启动一次本变更触及的部署/启动入口(服务入口、CLI 主入口、守护进程等——须是本变更实际改动的那一类入口,不能拿无关进程的启动来凑数)。',
|
|
106
|
+
// 同义扩充(同坑):自然表述 + CLI 回执句式;checkIntegrationEvidence 另拼 CLI 回执文本
|
|
107
|
+
//(verify-services.receipt.json,服务回收器落盘)——结构化信号不依赖 agent 措辞
|
|
108
|
+
literals: ['启动.*一次', '实际.*启动', 'real startup', 'docker up', 'npm start', 'node server',
|
|
109
|
+
'真实启动', '拉起', '已启动', '进程启动', '服务启动', '启动验证',
|
|
110
|
+
'PID 已登记', 'verify-services'],
|
|
111
|
+
},
|
|
112
|
+
terminal_state_assertion: {
|
|
113
|
+
desc: '终态断言(建议项,不阻断):AgentRun running→completed/failed、session/lease end 状态同步。',
|
|
114
|
+
literals: ['terminal state', '终态', 'completed failed', 'session end', 'lease end',
|
|
115
|
+
'生命周期终态', '状态同步'],
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** 风险分级的判定来源(命中关键词来源 = design.md / plan.md 内容,非改动文件本身) */
|
|
120
|
+
export const RISK_LEVEL_CAUSES = {
|
|
121
|
+
'deployment-critical':
|
|
122
|
+
'design.md / plan.md 命中启动入口关键词(cli.ts / main.ts / server.(js|ts) / bootstrap / entrypoint)。' +
|
|
123
|
+
'注意:这是按 design/plan 里的措辞判定的,不一定代表你真改了启动入口;若属误判可在 design.md frontmatter 用 risk_level 显式声明真实等级(如 unit-sufficient)覆盖——不要靠删措辞绕门控,危险链路该有真实启动证据。',
|
|
124
|
+
'integration-critical':
|
|
125
|
+
'design.md / plan.md 命中跨进程/状态机关键词(daemon / backend / session / lease / lifecycle / heartbeat 等)。',
|
|
126
|
+
'contract-required': 'design.md / plan.md 命中 API contract 关键词(api / client / contract / dto)。',
|
|
127
|
+
'explicit': 'design.md frontmatter 的 risk_level 显式声明(覆盖关键词判级)。',
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** design.md frontmatter 可显式声明的合法 risk_level 值(与 detectChangeRisk 判级结果同集合) */
|
|
131
|
+
const RISK_LEVELS = ['doc-only', 'unit-sufficient', 'contract-required', 'integration-critical', 'deployment-critical']
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 从 design.md 顶部 frontmatter 提取显式 risk_level 声明。
|
|
135
|
+
* 只认文档最开头 `---\n...\n---` 块内的 `risk_level: <level>` 单行(与 plan.md 的 plan_level 同款解析),
|
|
136
|
+
* 不扫正文——避免正文里讨论 risk_level 措辞时被误当声明。返回合法 level 或 null。
|
|
137
|
+
*/
|
|
138
|
+
export function extractExplicitRiskLevel(designContent = '') {
|
|
139
|
+
const fm = designContent.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
|
140
|
+
if (!fm) return null
|
|
141
|
+
const line = fm[1].split('\n').map(l => l.trim()).find(l => l.startsWith('risk_level:'))
|
|
142
|
+
if (!line) return null
|
|
143
|
+
const value = line.slice('risk_level:'.length).trim().replace(/^["']|["']$/g, '').toLowerCase()
|
|
144
|
+
return RISK_LEVELS.includes(value) ? value : null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function detectChangeRisk({ designContent = '', planContent = '', changedFiles = [] } = {}) {
|
|
148
|
+
const triggers = []
|
|
149
|
+
|
|
150
|
+
// ── 显式豁免优先:design.md frontmatter 声明 risk_level → 以声明为准,跳过关键词判级 ──
|
|
151
|
+
// 历史教训:detectChangeRisk 是机械字面匹配,不认否定语境——design 写「本次不改动 daemon/session」
|
|
152
|
+
// 仍命中关键词被误判 integration/deployment-critical,强制要全套集成证据,对无状态后端变更误伤。
|
|
153
|
+
// 与其在正则层做脆弱的否定识别,不如给一条显式、诚实、可审计(落在 design frontmatter + verify-result)
|
|
154
|
+
// 的覆盖通道。声明仍走门控(结论 FAIL 仍拦),但豁免级(unit-sufficient 等)不再强制集成证据。
|
|
155
|
+
const explicit = extractExplicitRiskLevel(designContent)
|
|
156
|
+
if (explicit) {
|
|
157
|
+
const requiredVerification =
|
|
158
|
+
explicit === 'deployment-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'real_startup_once'] :
|
|
159
|
+
explicit === 'integration-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion'] :
|
|
160
|
+
explicit === 'contract-required' ? ['unit_tests', 'contract_tests'] :
|
|
161
|
+
explicit === 'doc-only' ? ['static_check'] :
|
|
162
|
+
['unit_tests']
|
|
163
|
+
return { level: explicit, triggers: ['risk_level (explicit)'], requiredVerification, explicit: true }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const combined = [designContent, planContent].join('\n')
|
|
167
|
+
|
|
168
|
+
for (const pattern of INTEGRATION_CRITICAL_PATTERNS) {
|
|
169
|
+
if (pattern.test(combined)) {
|
|
170
|
+
pattern.lastIndex = 0
|
|
171
|
+
const match = combined.match(pattern)
|
|
172
|
+
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const file of changedFiles) {
|
|
177
|
+
for (const pattern of INTEGRATION_FILE_PATTERNS) {
|
|
178
|
+
if (pattern.test(file)) {
|
|
179
|
+
pattern.lastIndex = 0
|
|
180
|
+
const match = file.match(pattern)
|
|
181
|
+
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (triggers.length === 0) {
|
|
187
|
+
return { level: 'doc-only', triggers: [], requiredVerification: ['static_check'] }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const deploymentTrigger = triggers.some(t => /cli\.ts|main\.ts|server\.(js|ts)|bootstrap|entrypoint/i.test(t))
|
|
191
|
+
const lifecycleTrigger = triggers.some(t => /session|lease|agent.?run|lifecycle|state.?transition|claim|heartbeat/i.test(t))
|
|
192
|
+
const crossProcessTrigger = triggers.some(t => /daemon|backend|client.*api|grpc|websocket|cross.?process|ipc|message.?queue/i.test(t))
|
|
193
|
+
|
|
194
|
+
let level
|
|
195
|
+
const requiredVerification = ['unit_tests']
|
|
196
|
+
|
|
197
|
+
if (deploymentTrigger) {
|
|
198
|
+
level = 'deployment-critical'
|
|
199
|
+
requiredVerification.push('contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'real_startup_once')
|
|
200
|
+
} else if (lifecycleTrigger || crossProcessTrigger) {
|
|
201
|
+
level = 'integration-critical'
|
|
202
|
+
requiredVerification.push('contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion')
|
|
203
|
+
} else if (triggers.some(t => /api|client|contract|dto/i.test(t))) {
|
|
204
|
+
level = 'contract-required'
|
|
205
|
+
requiredVerification.push('contract_tests')
|
|
206
|
+
} else {
|
|
207
|
+
level = 'unit-sufficient'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { level, triggers, requiredVerification }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 检查 verify-result.md 是否包含集成验证证据
|
|
215
|
+
*
|
|
216
|
+
* opts.extraEvidenceText(坑 verify-literal-evidence-mismatch,2026-08-22):调用方可注入
|
|
217
|
+
* CLI 结构化回执文本(如 verify-services.receipt.json 的服务回收回执)——它随 verifyContent
|
|
218
|
+
* 一起参与 literals 匹配,agent 真实起过服务且 CLI 回收过(有回执)时不再依赖其自然语言
|
|
219
|
+
* 措辞恰好含字面词,表述差异不再误拦。
|
|
220
|
+
*/
|
|
221
|
+
export function checkIntegrationEvidence(verifyContent, requiredVerification, opts = {}) {
|
|
222
|
+
const errors = []
|
|
223
|
+
const warnings = []
|
|
224
|
+
const lower = (String(verifyContent || '') + '\n' + String(opts.extraEvidenceText || '')).toLowerCase()
|
|
225
|
+
|
|
226
|
+
// 字面证据正则从 VERIFICATION_NEEDS[k].literals 派生——与报错描述、prompt 事前契约严格同源,
|
|
227
|
+
// 杜绝历史上"描述说 A、正则查 B"的分叉(checkIntegrationEvidence 正则曾比 VERIFICATION_NEEDS
|
|
228
|
+
// 描述多匹配 集成测试/backend.*daemon/real.*integration 等)。literals 为空的 need
|
|
229
|
+
// (unit_tests/contract_tests)无字面校验,视为满足。
|
|
230
|
+
const hasEvidence = (k) => {
|
|
231
|
+
const n = VERIFICATION_NEEDS[k]
|
|
232
|
+
if (!n || !n.literals || n.literals.length === 0) return true
|
|
233
|
+
return new RegExp(n.literals.join('|'), 'i').test(lower)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const needsIntegration = requiredVerification.includes('real_daemon_backend_integration')
|
|
237
|
+
const needsLogEvidence = requiredVerification.includes('runtime_log_evidence')
|
|
238
|
+
const needsTerminalState = requiredVerification.includes('terminal_state_assertion')
|
|
239
|
+
const needsRealStartup = requiredVerification.includes('real_startup_once')
|
|
240
|
+
|
|
241
|
+
if (needsIntegration) {
|
|
242
|
+
const hasMockOnly = /mock.*test.*passed|unit.*test.*passed/i.test(lower)
|
|
243
|
+
const hasIntegrationEvidence = hasEvidence('real_daemon_backend_integration')
|
|
244
|
+
if (!hasIntegrationEvidence && hasMockOnly) {
|
|
245
|
+
errors.push('integration-critical 变更只提供了 mock 单测证据,缺少真实 daemon↔backend 集成验证')
|
|
246
|
+
} else if (!hasIntegrationEvidence) {
|
|
247
|
+
errors.push('integration-critical 变更缺少集成验证证据 — 需要真实 daemon↔backend 测试结果或运行时日志')
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (needsLogEvidence) {
|
|
252
|
+
if (!hasEvidence('runtime_log_evidence')) {
|
|
253
|
+
errors.push('integration-critical 变更的 verify-result.md 缺少 Runtime Evidence section')
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (needsTerminalState) {
|
|
258
|
+
if (!hasEvidence('terminal_state_assertion')) {
|
|
259
|
+
warnings.push('建议检查终态断言:AgentRun running→completed/failed、session end 状态同步')
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (needsRealStartup) {
|
|
264
|
+
if (!hasEvidence('real_startup_once')) {
|
|
265
|
+
errors.push('deployment-critical 变更需要真实启动验证证据')
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return { ok: errors.length === 0, errors, warnings }
|
|
270
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-primitives.js — 共享的产物字面校验原语(纯函数)
|
|
3
|
+
*
|
|
4
|
+
* 从 workflow.js 的 checkOutput / _checkWorkflow 抽出,作为"何谓 contains_sections / min_lines /
|
|
5
|
+
* no_placeholder / no_empty_files"的**全仓单一语义源**。workflow 引擎与 stage-contract 引擎
|
|
6
|
+
* (stage-contract-engine.js)共用同一组谓词,杜绝"workflow 认 `## X` 子串、stage-contract 认别的"
|
|
7
|
+
* 这类判定分叉(本任务的"事前==事后"同源在原语层就成立)。
|
|
8
|
+
*
|
|
9
|
+
* 设计原则:纯文本判定,不碰 fs。调用方负责读文件 + CRLF 归一化后传入 content——
|
|
10
|
+
* 这样 Windows CRLF 差异在调用方收敛(plan-postcheck 已用 LF-normalizing reader),引擎拿到
|
|
11
|
+
* 什么判什么,不在原语层二次引入 readFileSync 差异(缺陷 windows-python-crlf-taskcard 的根因)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// no_placeholder 的默认占位词。与原 workflow.js checkOutput/_checkWorkflow 逐字一致——
|
|
15
|
+
// 改动会破坏存量扫描文档的占位判定,属契约级常量。
|
|
16
|
+
const DEFAULT_PLACEHOLDER_PATTERNS = [
|
|
17
|
+
'待补充', 'TODO', 'TBD', '未分析', '根据项目情况', '根据实际情况', '按需填写',
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
/** 内容非空(trim 后有字符)。用于 no_empty_files。 */
|
|
21
|
+
export function contentNonEmpty(content) {
|
|
22
|
+
return typeof content === 'string' && content.trim().length > 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 行数(split('\n'))。用于 min_lines 的 detail 文案与判定。 */
|
|
26
|
+
export function lineCount(content) {
|
|
27
|
+
return typeof content === 'string' ? content.split('\n').length : 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 行数 ≥ min。用于 min_lines 判定。 */
|
|
31
|
+
export function meetsMinLines(content, min) {
|
|
32
|
+
return lineCount(content) >= min
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 返回缺失的章节(sections 中未以 `## <section>` 子串出现的)。
|
|
37
|
+
* 用于 contains_sections——判定与原 workflow.js 逐字一致(`## ${s}` 前缀子串匹配)。
|
|
38
|
+
*/
|
|
39
|
+
export function missingSections(content, sections) {
|
|
40
|
+
if (typeof content !== 'string') return sections.slice()
|
|
41
|
+
return sections.filter(s => !content.includes(`## ${s}`))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 是否含全部章节(missingSections 为空的语法糖)。 */
|
|
45
|
+
export function hasAllSections(content, sections) {
|
|
46
|
+
return missingSections(content, sections).length === 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 返回命中的占位词(patterns 中匹配"独立成行"的)。
|
|
51
|
+
* 用于 no_placeholder——正则 `^\s*[-*]?\s*<p>\s*$`(m flag)与原 workflow.js 逐字一致。
|
|
52
|
+
* patterns 为空/未传时用 DEFAULT_PLACEHOLDER_PATTERNS(等价原 `check.patterns || [defaults]`)。
|
|
53
|
+
*/
|
|
54
|
+
export function placeholderLineMatches(content, patterns) {
|
|
55
|
+
if (typeof content !== 'string') return []
|
|
56
|
+
const pats = (patterns && patterns.length > 0) ? patterns : DEFAULT_PLACEHOLDER_PATTERNS
|
|
57
|
+
return pats.filter(p => new RegExp(`^\\s*[-*]?\\s*${p}\\s*$`, 'm').test(content))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 无占位词命中(placeholderLineMatches 为空的语法糖)。 */
|
|
61
|
+
export function hasNoPlaceholderLine(content, patterns) {
|
|
62
|
+
return placeholderLineMatches(content, patterns).length === 0
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { DEFAULT_PLACEHOLDER_PATTERNS }
|