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,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docs-debt — 模块文档欠账事实计算(change: 2026-08-15-docs-debt-inject,债单第六节)
|
|
3
|
+
*
|
|
4
|
+
* 原则:"CLI 算事实注入"——git 事实算出本变更触及模块的文档欠账,注入 Wave prompt 的是
|
|
5
|
+
* 结论不是要求(advisory 不阻断,无债零输出,D-005)。
|
|
6
|
+
*
|
|
7
|
+
* 归属三级(D-003):module.paths || module.core_files(读端口径同 prompt.js loadModuleContextIndex)
|
|
8
|
+
* → 模块卡 doc 内容中的文件路径字面量(v1 map 兼容)→ 未匹配归 unmapped。
|
|
9
|
+
* behind 口径(D-004):每模块两次 git log -1 --format="%h %ct" 取时间戳判方向,
|
|
10
|
+
* behind = rev-list --count docCommit..srcCommit;untracked 卡 behind=null 显式"卡片从未提交";
|
|
11
|
+
* git 失败/超时 5s → 该模块 behind=null + 降级注记,不抛。
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs'
|
|
14
|
+
import { join, relative } from 'node:path'
|
|
15
|
+
import { safeGit } from './git-helper.js'
|
|
16
|
+
import { parseModuleMapSimple } from './modules.js'
|
|
17
|
+
import { runDocsCheck, readDocsCheckConfig, parseDecisionEntries, anchorFilePath } from './docs-check.js'
|
|
18
|
+
|
|
19
|
+
/** safeGit 超时(大仓 git log 慢时降级该模块,不挂起 Wave 渲染) */
|
|
20
|
+
const GIT_TIMEOUT_MS = 5000
|
|
21
|
+
|
|
22
|
+
/** realpath 归一(失败回退原值——路径不可及时保持调用方原有行为) */
|
|
23
|
+
function realpathSafe(p) {
|
|
24
|
+
try { return realpathSync(p) } catch { return p }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 纯函数:把 changedFiles 归属到模块。
|
|
29
|
+
* @param {string[]} changedFiles 变更文件(POSIX 相对路径)
|
|
30
|
+
* @param {object} moduleIndex parseModuleMapSimple 返回值({ modules: { id: { paths?, core_files?, doc? } } })
|
|
31
|
+
* @param {{ cardsDir: string }} opts cardsDir = <specBase>/docs/<project>/modules
|
|
32
|
+
* @returns {{ byModule: Map<string, { doc: string, files: string[] }>, unmapped: string[] }}
|
|
33
|
+
*/
|
|
34
|
+
export function matchFilesToModules(changedFiles, moduleIndex, opts = {}) {
|
|
35
|
+
const byModule = new Map()
|
|
36
|
+
const unmapped = []
|
|
37
|
+
// parseModuleMapSimple 返回扁平模块对象({ id: {paths?, core_files?, doc?} },无 .modules 包层)
|
|
38
|
+
const modules = moduleIndex && !moduleIndex.modules ? moduleIndex : (moduleIndex?.modules || {})
|
|
39
|
+
const cardsDir = opts.cardsDir
|
|
40
|
+
|
|
41
|
+
// 模块 → 候选路径集合(一级:paths || core_files;同时预载卡片内容供二级)
|
|
42
|
+
const modulePaths = new Map() // moduleId → string[]
|
|
43
|
+
const cardContents = new Map() // moduleId → string
|
|
44
|
+
for (const [id, m] of Object.entries(modules)) {
|
|
45
|
+
const ps = [...(Array.isArray(m.paths) ? m.paths : []), ...(Array.isArray(m.core_files) ? m.core_files : [])]
|
|
46
|
+
modulePaths.set(id, ps)
|
|
47
|
+
if (cardsDir && m.doc) {
|
|
48
|
+
const cardPath = join(cardsDir, m.doc.replace(/^modules\//, ''))
|
|
49
|
+
if (existsSync(cardPath)) {
|
|
50
|
+
try { cardContents.set(id, readFileSync(cardPath, 'utf8')) } catch { /* 降级二级 */ }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const f of changedFiles) {
|
|
56
|
+
let matched = null
|
|
57
|
+
// 一级:paths/core_files 前缀或字面量命中
|
|
58
|
+
for (const [id, ps] of modulePaths) {
|
|
59
|
+
if (ps.some(p => p === f || f.startsWith(p.replace(/\/$/, '') + '/') || p.startsWith(f.split('/')[0] + '/') && p === f)) {
|
|
60
|
+
matched = id; break
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 二级:卡片 doc 内容中的文件路径字面量(精确子串)
|
|
64
|
+
if (!matched) {
|
|
65
|
+
for (const [id, content] of cardContents) {
|
|
66
|
+
if (content && content.includes(f)) { matched = id; break }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 二级b:裸文件名匹配——卡片引用习惯是裸名(`execute.js` 而非 `src/stages/execute.js`,
|
|
70
|
+
// 本仓 stages.md 实证),全路径 includes 对这类卡永远落空 → 归属 unmapped(假"无归属")。
|
|
71
|
+
// 规则:基名在卡内存在"独立出现"(两侧均非路径/标识符字符),防 a.js 误配 xa.js.txt 等。
|
|
72
|
+
// 全路径命中(上面循环)优先,裸名仅在未命中时兜底。
|
|
73
|
+
if (!matched) {
|
|
74
|
+
const base = f.slice(f.lastIndexOf('/') + 1)
|
|
75
|
+
if (base && !base.startsWith('.')) {
|
|
76
|
+
outer: for (const [id, content] of cardContents) {
|
|
77
|
+
if (!content) continue
|
|
78
|
+
let pos = content.indexOf(base)
|
|
79
|
+
while (pos !== -1) {
|
|
80
|
+
const before = pos > 0 ? content[pos - 1] : ''
|
|
81
|
+
const after = content[pos + base.length] || ''
|
|
82
|
+
const beforeOk = before === '' || !/[A-Za-z0-9_\/.\-]/.test(before)
|
|
83
|
+
const afterOk = after === '' || !/[A-Za-z0-9_\-./]/.test(after)
|
|
84
|
+
if (beforeOk && afterOk) { matched = id; break outer }
|
|
85
|
+
pos = content.indexOf(base, pos + 1)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!matched) { unmapped.push(f); continue }
|
|
91
|
+
const entry = byModule.get(matched) || { doc: modules[matched].doc || '', files: [] }
|
|
92
|
+
entry.files.push(f)
|
|
93
|
+
byModule.set(matched, entry)
|
|
94
|
+
}
|
|
95
|
+
return { byModule, unmapped }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** "h ct" 输出解析(git log -1 --format=%h %ct 的单行值)→ { h, ct };空输出 → null。
|
|
99
|
+
* 刻意不额外校验 ct 可析性——与 moduleDebt 原内联解析逐字等价(含畸形输出 NaN ct 形态),
|
|
100
|
+
* 保证公共抽取零行为漂移。 */
|
|
101
|
+
function parseHct(out) {
|
|
102
|
+
if (!out || !out.trim()) return null
|
|
103
|
+
const [h, ct] = out.trim().split(' ')
|
|
104
|
+
return { h, ct: parseInt(ct, 10) }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** behind 公共口径(D-004;2026-08-23-adopt-harness-practices task-05 抽公共实现):双 commit
|
|
108
|
+
* 时间戳判方向 + rev-list 计数。src.ct >= ref.ct → rev-list --count ref..src(git 失败 →
|
|
109
|
+
* behind=null + degraded=true,不抛);src 时间戳落后 ref → behind=0。ct 相等也进对账
|
|
110
|
+
* (同一秒内的提交——测试/快速序列场景),由 rev-list count 分晓。
|
|
111
|
+
* moduleDebt 与 computeModuleBehind 共用——behind 口径单一真相源。 */
|
|
112
|
+
function behindWithDirection(gitRoot, srcCommit, refCommit) {
|
|
113
|
+
if (srcCommit.ct >= refCommit.ct) {
|
|
114
|
+
const cnt = safeGit(gitRoot, ['rev-list', '--count', `${refCommit.h}..${srcCommit.h}`], { timeout: GIT_TIMEOUT_MS })
|
|
115
|
+
const behind = cnt.error ? null : parseInt(cnt.value, 10)
|
|
116
|
+
return { behind, degraded: behind === null }
|
|
117
|
+
}
|
|
118
|
+
return { behind: 0, degraded: false }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** 单模块欠账:双 commit 时间戳判方向 + rev-list 计数(复用 parseHct/behindWithDirection 公共
|
|
122
|
+
* 实现,抽取前后逐字等价,C-10)。全失败 → behind null + 注记。
|
|
123
|
+
* docGitPath:模块卡的 git 相对路径(specBase 相对 projectRoot 换算——specBase 在项目内时
|
|
124
|
+
* 是 .sillyspec/docs/<p>/modules/<id>.md;平台模式 specBase 在仓外时卡片不在 git 内 →
|
|
125
|
+
* docLog 恒失败 → 走"从未提交"形态,如实呈现)。 */
|
|
126
|
+
function moduleDebt(projectRoot, moduleId, entry, docGitPath, docGitRoot) {
|
|
127
|
+
const notes = []
|
|
128
|
+
const srcPaths = entry.files
|
|
129
|
+
const srcLog = safeGit(projectRoot, ['log', '-1', '--format=%h %ct', '--', ...srcPaths], { timeout: GIT_TIMEOUT_MS })
|
|
130
|
+
let behind = null
|
|
131
|
+
let srcCommit = null
|
|
132
|
+
let docCommit = null
|
|
133
|
+
let neverCommitted = false
|
|
134
|
+
srcCommit = (!srcLog.error && srcLog.value) ? parseHct(srcLog.value) : null
|
|
135
|
+
if (!srcCommit) notes.push('源码 commit 历史不可得(新文件或 git 失败)')
|
|
136
|
+
if (docGitPath) {
|
|
137
|
+
// 卡片历史在卡片所在 git 仓查(docGitRoot;linked worktree 场景 = 主仓根),
|
|
138
|
+
// 源码历史在 projectRoot 查——两侧 commit 同属一个仓库历史可 rev-list 对账
|
|
139
|
+
const docGitCwd = docGitRoot || projectRoot
|
|
140
|
+
const docLog = safeGit(docGitCwd, ['log', '-1', '--format=%h %ct', '--', docGitPath], { timeout: GIT_TIMEOUT_MS })
|
|
141
|
+
docCommit = (!docLog.error && docLog.value) ? parseHct(docLog.value) : null
|
|
142
|
+
if (!docCommit) neverCommitted = true
|
|
143
|
+
}
|
|
144
|
+
if (srcCommit && !docCommit) {
|
|
145
|
+
// 有源码历史但卡片从未提交 → 欠账最重形态
|
|
146
|
+
return { module: moduleId, ...entry, srcCommit: srcCommit.h, docCommit: null, behind: null, neverCommitted: true, notes }
|
|
147
|
+
}
|
|
148
|
+
if (srcCommit && docCommit) {
|
|
149
|
+
behind = behindWithDirection(docGitRoot || projectRoot, srcCommit, docCommit).behind
|
|
150
|
+
if (behind === null) notes.push('rev-list 失败降级')
|
|
151
|
+
}
|
|
152
|
+
return { module: moduleId, ...entry, srcCommit: srcCommit?.h || null, docCommit: docCommit?.h || null, behind, neverCommitted, notes }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 公共导出(C-10,2026-08-23-adopt-harness-practices task-05):模块源码在参考 commit 后的
|
|
157
|
+
* 前进数(behind)。口径同 moduleDebt(D-004:双 commit 时间戳判方向 + rev-list 计数,复用
|
|
158
|
+
* behindWithDirection;git 失败/输入缺失 → behind=null + degraded=true,不抛)。
|
|
159
|
+
* 消费方:docs-check 决策规则 behind 复核(决策锚定模块源码在「最近确认」commit 后的前进数)。
|
|
160
|
+
* 不改 moduleDebt 对外行为与既有返回结构(computeDocsDebt / matchFilesToModules 输出不变)。
|
|
161
|
+
* @param {string} moduleId 模块 id(_module-map.yaml 键;决策规则里 = 条目所在域文件名)
|
|
162
|
+
* @param {string} lastConfirmedCommit 参考 commit(决策「最近确认」hash;空/「未记录」/非 hash 串 → degraded)
|
|
163
|
+
* @param {{ projectRoot?: string, srcPaths?: string[], moduleIndex?: object }} [opts]
|
|
164
|
+
* projectRoot git 锚(缺省 process.cwd());srcPaths 显式源码路径集(优先);
|
|
165
|
+
* moduleIndex _module-map 解析结果(平铺或 {modules} 包裹均可,取该模块 paths∪core_files 兜底)
|
|
166
|
+
* @returns {{ behind: number|null, degraded: boolean }} degraded=true = behind 不可得
|
|
167
|
+
* (路径集为空 / 参考 commit 不可解析 / git 失败),调用方降级呈现不阻断
|
|
168
|
+
*/
|
|
169
|
+
export function computeModuleBehind(moduleId, lastConfirmedCommit, opts = {}) {
|
|
170
|
+
const projectRoot = opts.projectRoot || process.cwd()
|
|
171
|
+
let srcPaths = Array.isArray(opts.srcPaths) ? opts.srcPaths.filter(Boolean).map(p => String(p)) : null
|
|
172
|
+
if ((!srcPaths || srcPaths.length === 0) && opts.moduleIndex) {
|
|
173
|
+
const flat = opts.moduleIndex.modules ? opts.moduleIndex.modules : opts.moduleIndex
|
|
174
|
+
const m = flat && typeof flat === 'object' ? flat[moduleId] : null
|
|
175
|
+
if (m) srcPaths = [...(Array.isArray(m.paths) ? m.paths : []), ...(Array.isArray(m.core_files) ? m.core_files : [])]
|
|
176
|
+
}
|
|
177
|
+
if (!srcPaths || srcPaths.length === 0) return { behind: null, degraded: true }
|
|
178
|
+
const ref = String(lastConfirmedCommit || '').trim()
|
|
179
|
+
if (!/^[0-9a-f]{4,40}$/i.test(ref)) return { behind: null, degraded: true }
|
|
180
|
+
const srcCommit = parseHct(safeGit(projectRoot, ['log', '-1', '--format=%h %ct', '--', ...srcPaths], { timeout: GIT_TIMEOUT_MS }).value)
|
|
181
|
+
const refCommit = parseHct(safeGit(projectRoot, ['log', '-1', '--format=%h %ct', ref], { timeout: GIT_TIMEOUT_MS }).value)
|
|
182
|
+
if (!srcCommit || !refCommit) return { behind: null, degraded: true }
|
|
183
|
+
return behindWithDirection(projectRoot, srcCommit, refCommit)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* IO 入口:算模块文档欠账事实。
|
|
188
|
+
* @param {{ projectRoot: string, specBase: string, projectName: string, changedFiles: string[] }} opts
|
|
189
|
+
* projectRoot:git 锚(worktree 根或 cwd);specBase/projectName:module-map 与卡片定位
|
|
190
|
+
* @returns {{ ok: boolean, facts: string, entries: object[], unmapped: string[] }}
|
|
191
|
+
* ok=false = 归属数据缺失(map 不存在/解析空)→ facts 为单行说明
|
|
192
|
+
* facts 空 = 无债(调用方零输出)
|
|
193
|
+
*/
|
|
194
|
+
export function computeDocsDebt(opts) {
|
|
195
|
+
const { projectRoot, specBase, projectName, changedFiles = [] } = opts || {}
|
|
196
|
+
const cardsDir = specBase && projectName ? join(specBase, 'docs', projectName, 'modules') : null
|
|
197
|
+
const mapPath = cardsDir ? join(cardsDir, '_module-map.yaml') : null
|
|
198
|
+
if (!mapPath || !existsSync(mapPath)) {
|
|
199
|
+
return { ok: false, facts: '[docs-debt] 无 _module-map.yaml,模块归属数据缺失(跑 sillyspec modules rebuild 生成)', entries: [], unmapped: changedFiles }
|
|
200
|
+
}
|
|
201
|
+
let moduleIndex
|
|
202
|
+
try {
|
|
203
|
+
moduleIndex = parseModuleMapSimple(readFileSync(mapPath, 'utf8'))
|
|
204
|
+
} catch (e) {
|
|
205
|
+
return { ok: false, facts: `[docs-debt] module-map 解析失败(${e.message}),跳过欠账计算`, entries: [], unmapped: changedFiles }
|
|
206
|
+
}
|
|
207
|
+
if (!moduleIndex || Object.keys(moduleIndex.modules ? moduleIndex.modules : moduleIndex).length === 0) {
|
|
208
|
+
return { ok: false, facts: '[docs-debt] module-map 解析为空(0 模块),归属数据缺失', entries: [], unmapped: changedFiles }
|
|
209
|
+
}
|
|
210
|
+
const { byModule, unmapped } = matchFilesToModules(changedFiles, moduleIndex, { cardsDir })
|
|
211
|
+
if (byModule.size === 0) {
|
|
212
|
+
return { ok: true, facts: '', entries: [], unmapped }
|
|
213
|
+
}
|
|
214
|
+
const entries = []
|
|
215
|
+
// doc 的 git 锚定(execute 审查 FAIL-1 修复):卡片查询按 specBase 所在 git 仓根算相对路径,
|
|
216
|
+
// 不按 projectRoot——dogfood execute 场景 projectRoot=worktree 而 specBase=主仓/.sillyspec,
|
|
217
|
+
// linked worktree 共享 git 历史,git -C <worktree> log -- <主仓相对路径> 查得到卡片真实历史;
|
|
218
|
+
// 若按 projectRoot relative 判 null 会把"卡片在主仓 git 内"误报为"从未提交"(假事实)。
|
|
219
|
+
// 平台模式 specRoot 在仓外(git -C specBase 失败/无 git)→ docGitPath=null 走"从未提交"如实呈现。
|
|
220
|
+
let docGitRoot = null
|
|
221
|
+
let docPathPrefix = null
|
|
222
|
+
try {
|
|
223
|
+
const specTop = safeGit(specBase, ['rev-parse', '--show-toplevel'], { timeout: GIT_TIMEOUT_MS })
|
|
224
|
+
if (!specTop.error && specTop.value) {
|
|
225
|
+
docGitRoot = specTop.value.trim()
|
|
226
|
+
// macOS 坑:git 返回 realpath(/var → /private/var 的 symlink 解析),cardsDir 可能是
|
|
227
|
+
// symlink 形态(tmpdir/用户项目路径经符号链接)——两侧不归一时 relative() 产出 ../..
|
|
228
|
+
// 前缀 → 卡片被误判"从未提交"(假账)。两侧 realpathSync 归一后再算(失败回退原值)。
|
|
229
|
+
const rel = relative(realpathSafe(docGitRoot), realpathSafe(cardsDir))
|
|
230
|
+
if (rel && !rel.startsWith('..')) docPathPrefix = rel.replace(/\\/g, '/')
|
|
231
|
+
}
|
|
232
|
+
} catch { /* 保持 null */ }
|
|
233
|
+
for (const [id, entry] of byModule) {
|
|
234
|
+
const docGitPath = docPathPrefix && entry.doc ? `${docPathPrefix}/${entry.doc.replace(/^modules\//, '')}` : null
|
|
235
|
+
entries.push(moduleDebt(projectRoot, id, entry, docGitPath, docGitPath ? docGitRoot : null))
|
|
236
|
+
}
|
|
237
|
+
// 只报欠账(behind>0 / neverCommitted / behind=null 降级注记);behind=0 不出(无债零输出)
|
|
238
|
+
const debtEntries = entries.filter(e => e.neverCommitted || (e.behind !== null && e.behind > 0) || (e.behind === null && !e.neverCommitted))
|
|
239
|
+
if (debtEntries.length === 0) return { ok: true, facts: '', entries, unmapped }
|
|
240
|
+
const lines = ['[docs-debt] 本变更已触及以下模块(CLI 算,diff × _module-map.yaml,累计):']
|
|
241
|
+
for (const e of debtEntries) {
|
|
242
|
+
if (e.neverCommitted) {
|
|
243
|
+
lines.push(` - ${e.module}:模块卡 ${e.doc} 从未提交(欠账最重)`)
|
|
244
|
+
} else if (e.behind === null) {
|
|
245
|
+
lines.push(` - ${e.module}:欠账数不可得(${(e.notes || []).join(';') || 'git 降级'})`)
|
|
246
|
+
} else {
|
|
247
|
+
lines.push(` - ${e.module}:源码 ${e.behind} commit 未同步卡(卡停 ${e.docCommit},源码到 ${e.srcCommit})`)
|
|
248
|
+
}
|
|
249
|
+
// O-2(docs-signals-o12):内联卡片失效引用 + 建议行号——agent 从"知道欠账"到"知道改哪行"。
|
|
250
|
+
// 守卫 docGitPath && docGitRoot(D-004 同锚;平台模式仓外跳过);每模块上限 3 条(D-002);
|
|
251
|
+
// 异常整块降级跳过(advisory 链不因增强而脆)。
|
|
252
|
+
const inline = inlineCardInvalidRefs(e, docGitRoot, docPathPrefix)
|
|
253
|
+
if (inline) lines.push(inline)
|
|
254
|
+
lines.push(` 涉及文件:${e.files.join(', ')}`)
|
|
255
|
+
}
|
|
256
|
+
return { ok: true, facts: lines.join('\n'), entries, unmapped }
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** O-2:欠账模块卡片的失效引用内联行(docs check 层1 单文档)。无失效/不可跑 → null。 */
|
|
260
|
+
function inlineCardInvalidRefs(entry, docGitRoot, docPathPrefix) {
|
|
261
|
+
if (!docGitRoot || !docPathPrefix || !entry.doc) return null
|
|
262
|
+
try {
|
|
263
|
+
const docGitPath = `${docPathPrefix}/${entry.doc.replace(/^modules\//, '')}`
|
|
264
|
+
// HUB-06:补传 crossRepoRoots(与 docs gate 同口径),repo:// 跨仓引用不再恒跳过;
|
|
265
|
+
// 配置读取失败(无 local.yaml 段等)→ 不启用跨仓,与原行为一致
|
|
266
|
+
let crossRepoRoots
|
|
267
|
+
try { crossRepoRoots = readDocsCheckConfig(docGitRoot)?.crossRepoRoots } catch { crossRepoRoots = undefined }
|
|
268
|
+
const check = runDocsCheck({ projectRoot: docGitRoot, docs: [docGitPath], crossRepoRoots })
|
|
269
|
+
if (check.ok || !Array.isArray(check.invalid) || check.invalid.length === 0) return null
|
|
270
|
+
const parts = check.invalid.slice(0, 3).map((inv) => {
|
|
271
|
+
const sug = Array.isArray(inv.suggest) && inv.suggest.length > 0 ? `→建议 L${inv.suggest.join('/L')}` : ''
|
|
272
|
+
return `${inv.ref || inv.doc}${sug}`
|
|
273
|
+
})
|
|
274
|
+
const more = check.invalid.length > 3 ? ` · 等 ${check.invalid.length} 处` : ''
|
|
275
|
+
return ` 卡内失效引用 ${check.invalid.length} 处:${parts.join(' · ')}${more}`
|
|
276
|
+
} catch {
|
|
277
|
+
return null
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// 决策锚点触碰事实(change: 2026-08-24-decision-touch-cli-drift task-01,W-A)
|
|
283
|
+
// 同「CLI 算事实注入」原则:execute 期改到决策锚定文件时立即呈现「触碰 N 条决策锚点」
|
|
284
|
+
// 事实(advisory 不阻断,无触碰零输出)。条目解析/锚点剥离复用 docs-check 口径
|
|
285
|
+
// (parseDecisionEntries + anchorFilePath 静态 import,本文件已 import docs-check 无环),
|
|
286
|
+
// 勿复刻正则防两处口径漂移。
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
/** 触碰事实行条数上限(R-05 prompt 体积):超出截断 +「…另有 N 条」 */
|
|
290
|
+
export const DECISION_TOUCH_RENDER_LIMIT = 5
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 纯事实计算:变更文件 × 决策锚点前缀匹配 → 触碰事实(唯一真相形态)。
|
|
294
|
+
* @param {string[]} changedFiles 变更文件(入口统一 POSIX 化——调用方不重复处理)
|
|
295
|
+
* @param {string} knowledgeRoot 知识库根(<specBase>/knowledge;decisions 库在其 decisions/ 子目录,
|
|
296
|
+
* 与 docs-check runDecisionRules 的 <specBase>/knowledge/decisions 同布局)
|
|
297
|
+
* @returns {{ touches: Array<{ id: string, title: string, anchorFile: string, touchedFile: string, file: string }>, empty: boolean }}
|
|
298
|
+
* touches 每项 = 一条 implemented 决策 × 一个命中变更文件(file = 条目来源域文件
|
|
299
|
+
* knowledge/decisions/<域>.md);empty=true = decisions 库不存在(冷启动);
|
|
300
|
+
* 无触碰 → touches=[] 且 empty=false(零输出由渲染层保证)
|
|
301
|
+
*/
|
|
302
|
+
export function computeDecisionTouches(changedFiles, knowledgeRoot) {
|
|
303
|
+
const emptyResult = { touches: [], empty: true }
|
|
304
|
+
if (!knowledgeRoot) return emptyResult
|
|
305
|
+
const decisionsDir = join(knowledgeRoot, 'decisions')
|
|
306
|
+
let domainFiles = []
|
|
307
|
+
try {
|
|
308
|
+
domainFiles = readdirSync(decisionsDir).filter(f => f.endsWith('.md')).sort()
|
|
309
|
+
} catch {
|
|
310
|
+
return emptyResult // decisions 库不存在 → 零信号(与 runDecisionRules 冷启动同语义)
|
|
311
|
+
}
|
|
312
|
+
const changed = (Array.isArray(changedFiles) ? changedFiles : [])
|
|
313
|
+
.filter(Boolean).map(f => String(f).replace(/\\/g, '/'))
|
|
314
|
+
const touches = []
|
|
315
|
+
for (const f of domainFiles) {
|
|
316
|
+
const file = `knowledge/decisions/${f}`
|
|
317
|
+
let entries
|
|
318
|
+
try { entries = parseDecisionEntries(readFileSync(join(decisionsDir, f), 'utf8')) } catch { continue }
|
|
319
|
+
for (const e of entries) {
|
|
320
|
+
if ((e.status || '').toLowerCase() !== 'implemented') continue // 仅 implemented(对齐 docs-check.js:793 先例,rejected 不提示)
|
|
321
|
+
const anchorPath = anchorFilePath(e.anchor) // 剥 :行号/:行号-行号/:符号 后缀 + POSIX 化;「未记录」/空 → null 跳过
|
|
322
|
+
if (!anchorPath) continue
|
|
323
|
+
for (const c of changed) {
|
|
324
|
+
// 前缀匹配粒度 = 文件或其子路径(R-03:锚点是文件级非目录级,防同前缀不同文件误报)
|
|
325
|
+
if (c === anchorPath || c.startsWith(anchorPath + '/')) {
|
|
326
|
+
touches.push({ id: e.id, title: e.title, anchorFile: anchorPath, touchedFile: c, file })
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
touches.sort((a, b) => a.file.localeCompare(b.file) || a.id.localeCompare(b.id) || a.touchedFile.localeCompare(b.touchedFile))
|
|
332
|
+
return { touches, empty: false }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 触碰事实 → 事实行文本(advisory):无触碰返回 ''(零输出);≤DECISION_TOUCH_RENDER_LIMIT 条
|
|
337
|
+
* 逐条呈现,超出截断加「…另有 N 条」(R-05)。双渲染点(run/prompt.js {DOCS_DEBT} 注入处 +
|
|
338
|
+
* stages/execute.js buildWavePrompt)共用同一渲染实现,防两处文案漂移。
|
|
339
|
+
* @param {Array<{ id: string, title: string, anchorFile: string, touchedFile: string, file: string }>} touches
|
|
340
|
+
* @returns {string}
|
|
341
|
+
*/
|
|
342
|
+
export function renderDecisionTouchFacts(touches) {
|
|
343
|
+
if (!Array.isArray(touches) || touches.length === 0) return ''
|
|
344
|
+
const shown = touches.slice(0, DECISION_TOUCH_RENDER_LIMIT)
|
|
345
|
+
const lines = [`[decision-touch] 本次变更触碰 ${touches.length} 条决策锚点(改动可能使该决策需复核):`]
|
|
346
|
+
for (const t of shown) {
|
|
347
|
+
lines.push(` - ${t.id}(${t.title || '无标题'},锚点 ${t.anchorFile})← 触碰文件 ${t.touchedFile}(${t.file})`)
|
|
348
|
+
}
|
|
349
|
+
if (touches.length > DECISION_TOUCH_RENDER_LIMIT) {
|
|
350
|
+
lines.push(` …另有 ${touches.length - DECISION_TOUCH_RENDER_LIMIT} 条`)
|
|
351
|
+
}
|
|
352
|
+
return lines.join('\n')
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* execute 期事实注入的 changedFiles 公共口径(D-003 主/次渲染点同源,勿双写):
|
|
357
|
+
* worktree 根解析(changeName → <specBase>/.runtime/worktrees/<changeName>/ 存在即锚,
|
|
358
|
+
* 否则 cwd/显式 projectRoot)+ porcelain 未提交(剥 .sillyspec/ 前缀、rename 取新路径)∪
|
|
359
|
+
* worktree meta.json baselineCommit..HEAD 已提交并集,POSIX 化。从 run/prompt.js {DOCS_DEBT}
|
|
360
|
+
* 内联实现抽取(行为逐字等价);execute.js buildWavePrompt(主渲染点)同源复用。
|
|
361
|
+
* git 失败/meta 读失败 → 只用可用集合,不抛。
|
|
362
|
+
* @param {{ specBase?: string, changeName?: string|null, cwd?: string|null, projectRoot?: string|null }} opts
|
|
363
|
+
* @returns {{ root: string, changedFiles: string[] }} root = 实际 git 锚(computeDocsDebt 的 projectRoot 同值)
|
|
364
|
+
*/
|
|
365
|
+
export function collectExecuteChangedFiles({ specBase, changeName, cwd, projectRoot } = {}) {
|
|
366
|
+
const wtRoot = specBase ? join(specBase, '.runtime', 'worktrees', changeName || '') : null
|
|
367
|
+
const root = projectRoot || (wtRoot && changeName && existsSync(wtRoot) ? wtRoot : cwd) || process.cwd()
|
|
368
|
+
const changed = new Set()
|
|
369
|
+
const statusRes = safeGit(root, ['status', '--porcelain'], { trim: false })
|
|
370
|
+
if (!statusRes.error && statusRes.value) {
|
|
371
|
+
for (const line of statusRes.value.split('\n')) {
|
|
372
|
+
if (!line.trim()) continue
|
|
373
|
+
// "XY path" / rename "R old -> new" 取 new
|
|
374
|
+
const pathPart = line.slice(3).split('->').pop().trim().replace(/"/g, '')
|
|
375
|
+
if (pathPart && !pathPart.startsWith('.sillyspec/')) changed.add(pathPart.replace(/\\/g, '/'))
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const metaPath = wtRoot ? join(wtRoot, 'meta.json') : null
|
|
379
|
+
if (metaPath && changeName && existsSync(metaPath)) {
|
|
380
|
+
try {
|
|
381
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf8'))
|
|
382
|
+
if (meta.baselineCommit) {
|
|
383
|
+
const diffRes = safeGit(root, ['diff', '--name-only', `${meta.baselineCommit}..HEAD`], { trim: false })
|
|
384
|
+
if (!diffRes.error && diffRes.value) {
|
|
385
|
+
for (const f of diffRes.value.split('\n')) {
|
|
386
|
+
if (f.trim() && !f.trim().startsWith('.sillyspec/')) changed.add(f.trim().replace(/\\/g, '/'))
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
} catch { /* meta 读失败只用未提交集 */ }
|
|
391
|
+
}
|
|
392
|
+
return { root, changedFiles: [...changed] }
|
|
393
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docs gate:docs check 的 ratchet 门(欠账只许减少不许增加)。
|
|
3
|
+
*
|
|
4
|
+
* 动机(doc-consistency-debt 第七节裁决):behind 计数是代理信号不能当阈值(源码活跃
|
|
5
|
+
* 不代表卡错,误报会让所有人学会忽略报警);docs check 失效数是直接信号(每条都是
|
|
6
|
+
* 具体的错),ratchet 语义 = 失效数 ≤ 基线即过、超基线拦——不管历史存量,只拦增量。
|
|
7
|
+
*
|
|
8
|
+
* 基线文件:.sillyspec/docs-check-baseline(纯数字一行,可手工改)。首次无基线时
|
|
9
|
+
* 必须显式 --init-baseline 生成(不悄悄合法化存量欠账——与 quick fail-closed 调性
|
|
10
|
+
* 一致);--init-baseline 幂等,重跑以当前实测数覆盖。
|
|
11
|
+
*
|
|
12
|
+
* exit code:0 过(≤基线)/ 1 拦(>基线)/ 2 配置或 IO 错误(含无基线)。
|
|
13
|
+
* 纯判定逻辑(evaluateRatchet)与 IO 面(runDocsGate)分离,前者可单测。
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { runDocsCheck, readDocsCheckConfig, DocsCheckConfigError } from './docs-check.js';
|
|
18
|
+
|
|
19
|
+
/** 基线文件名(相对 specBase/.sillyspec 根;specBase 由调用方传入完整路径锚) */
|
|
20
|
+
export const BASELINE_FILENAME = 'docs-check-baseline';
|
|
21
|
+
|
|
22
|
+
/** 展示用基线路径(文案用,POSIX 风格;join 防 specBase 带不带尾分隔符两种形态) */
|
|
23
|
+
function baselineDisplay(specBase) {
|
|
24
|
+
return [specBase.replace(/[\\/]+$/, ''), BASELINE_FILENAME].join('/').replace(/\\/g, '/');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 纯判定:ratchet 语义。
|
|
29
|
+
* @param {{ current: number, baseline: number }} r
|
|
30
|
+
* @returns {{ ok: boolean, delta: number, message: string }}
|
|
31
|
+
* delta = current - baseline;负值(清偿了)提示可下调基线但不强制。
|
|
32
|
+
*/
|
|
33
|
+
export function evaluateRatchet({ current, baseline }) {
|
|
34
|
+
const delta = current - baseline;
|
|
35
|
+
if (delta <= 0) {
|
|
36
|
+
const hint = delta < 0
|
|
37
|
+
? `✅ docs gate: ${current} 处失效 ≤ 基线 ${baseline}(清偿了 ${-delta} 处,可跑 --init-baseline 下调基线锁住成果)`
|
|
38
|
+
: `✅ docs gate: ${current} 处失效 = 基线 ${baseline},放行`;
|
|
39
|
+
return { ok: true, delta, message: hint };
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
ok: false, delta,
|
|
43
|
+
message: `❌ docs gate: ${current} 处失效 > 基线 ${baseline}(新增 ${delta} 处),拦截。修掉新增引用或显式 --init-baseline 重置基线(需你确认存量合法)`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 读基线:不存在返回 null(调用方决定 init 或报错);内容非数字行返回 NaN(按损坏报错)。 */
|
|
48
|
+
export function readBaseline(specBase) {
|
|
49
|
+
const p = join(specBase, BASELINE_FILENAME);
|
|
50
|
+
if (!existsSync(p)) return null;
|
|
51
|
+
const raw = readFileSync(p, 'utf8').trim();
|
|
52
|
+
const n = parseInt(raw, 10);
|
|
53
|
+
return Number.isInteger(n) && n >= 0 ? n : NaN;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 写基线(--init-baseline):以实测当前失效数覆盖。 */
|
|
57
|
+
export function writeBaseline(specBase, value) {
|
|
58
|
+
writeFileSync(join(specBase, BASELINE_FILENAME), `${value}\n`, 'utf8');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* IO 入口:跑一次 gate。
|
|
63
|
+
* @param {{ projectRoot: string, specBase: string, initBaseline?: boolean }} opts
|
|
64
|
+
* projectRoot 源码仓根(docs check 锚);specBase .sillyspec 根(基线文件所在)
|
|
65
|
+
* @param {object} checkOpts 透传 runDocsCheck(paths/skip/keywordAssert 覆盖;缺省读 local.yaml)
|
|
66
|
+
* @returns {Promise<{ exitCode: 0|1|2, ok: boolean, current: number, baseline: number|null,
|
|
67
|
+
* delta: number|null, message: string, inited: boolean }>}
|
|
68
|
+
*/
|
|
69
|
+
export async function runDocsGate(opts = {}, checkOpts = {}) {
|
|
70
|
+
const { projectRoot, specBase, initBaseline = false } = opts;
|
|
71
|
+
let result;
|
|
72
|
+
try {
|
|
73
|
+
const cfg = readDocsCheckConfig(projectRoot);
|
|
74
|
+
result = runDocsCheck({
|
|
75
|
+
projectRoot,
|
|
76
|
+
paths: checkOpts.paths || cfg.paths,
|
|
77
|
+
skip: checkOpts.skip || cfg.skip,
|
|
78
|
+
keywordAssert: checkOpts.keywordAssert ?? cfg.keywordAssert,
|
|
79
|
+
crossRepoRoots: checkOpts.crossRepoRoots || cfg.crossRepoRoots,
|
|
80
|
+
});
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (e instanceof DocsCheckConfigError) {
|
|
83
|
+
return { exitCode: 2, ok: false, current: null, baseline: null, delta: null, message: `docs gate 配置错误:${e.message}`, inited: false };
|
|
84
|
+
}
|
|
85
|
+
throw e;
|
|
86
|
+
}
|
|
87
|
+
const current = result.invalid.length;
|
|
88
|
+
|
|
89
|
+
if (initBaseline) {
|
|
90
|
+
writeBaseline(specBase, current);
|
|
91
|
+
return { exitCode: 0, ok: true, current, baseline: current, delta: 0, message: `📌 docs gate: 基线已初始化为当前实测 ${current} 处失效(${baselineDisplay(specBase)})`, inited: true };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const baseline = readBaseline(specBase);
|
|
95
|
+
if (baseline === null) {
|
|
96
|
+
return {
|
|
97
|
+
exitCode: 2, ok: false, current, baseline: null, delta: null,
|
|
98
|
+
message: `❌ docs gate: 无基线文件(${baselineDisplay(specBase)})。首次使用先跑 sillyspec docs gate --init-baseline(以当前实测数立基线,存量既往不咎只拦增量)`,
|
|
99
|
+
inited: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (Number.isNaN(baseline)) {
|
|
103
|
+
return { exitCode: 2, ok: false, current, baseline: null, delta: null, message: `❌ docs gate: 基线文件损坏(非非负整数),手工修正或 --init-baseline 重置`, inited: false };
|
|
104
|
+
}
|
|
105
|
+
const v = evaluateRatchet({ current, baseline });
|
|
106
|
+
return { exitCode: v.ok ? 0 : 1, ok: v.ok, current, baseline, delta: v.delta, message: v.message, inited: false };
|
|
107
|
+
}
|