spexcode 0.6.1 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -149
- package/bin/spex.mjs +4 -0
- package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
- package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
- package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
- package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
- package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
- package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +115 -22
- package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
- package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
- package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
- package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
- package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
- package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
- package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
- package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
- package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
- package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
- package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
- package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
- package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
- package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
- package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
- package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
- package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
- package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
- package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
- package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
- package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
- package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
- package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
- package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +27 -7
- package/node_modules/@spexcode/spec-cli/package.json +47 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +9 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +22 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +15 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +10 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +13 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +12 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
- package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
- package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
- package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
- package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
- package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
- package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
- package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
- package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
- package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
- package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
- package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
- package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
- package/node_modules/@spexcode/spec-core/package.json +28 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
- package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
- package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
- package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
- package/node_modules/@spexcode/spec-eval/package.json +36 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
- package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
- package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
- package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
- package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
- package/node_modules/@spexcode/spec-forge/package.json +32 -0
- package/package.json +33 -19
- package/spec-cli/bin/spex.mjs +0 -69
- package/spec-cli/src/anchors.ts +0 -706
- package/spec-cli/src/attach.ts +0 -43
- package/spec-cli/src/claude-headless.ts +0 -321
- package/spec-cli/src/cli.ts +0 -1115
- package/spec-cli/src/client.ts +0 -315
- package/spec-cli/src/cockpit.ts +0 -43
- package/spec-cli/src/codex-runtime-generations.ts +0 -646
- package/spec-cli/src/commit-surgery.ts +0 -68
- package/spec-cli/src/contract-filter.ts +0 -202
- package/spec-cli/src/delivery-queue.ts +0 -107
- package/spec-cli/src/doctor.ts +0 -516
- package/spec-cli/src/file-write.ts +0 -22
- package/spec-cli/src/gateway-auth.ts +0 -181
- package/spec-cli/src/gateway-hub.ts +0 -356
- package/spec-cli/src/gateway.ts +0 -368
- package/spec-cli/src/git.ts +0 -2180
- package/spec-cli/src/graph.ts +0 -221
- package/spec-cli/src/graphCache.ts +0 -641
- package/spec-cli/src/graphDelta.ts +0 -77
- package/spec-cli/src/graphStream.ts +0 -1019
- package/spec-cli/src/harness-select.ts +0 -77
- package/spec-cli/src/harness.ts +0 -2747
- package/spec-cli/src/headless-controller.ts +0 -63
- package/spec-cli/src/help.ts +0 -480
- package/spec-cli/src/host-resources.ts +0 -707
- package/spec-cli/src/host.ts +0 -611
- package/spec-cli/src/identity-presets.d.ts +0 -13
- package/spec-cli/src/identity-presets.js +0 -128
- package/spec-cli/src/index.ts +0 -730
- package/spec-cli/src/init.ts +0 -258
- package/spec-cli/src/issues-cli.ts +0 -276
- package/spec-cli/src/issues.ts +0 -237
- package/spec-cli/src/layout.ts +0 -557
- package/spec-cli/src/lint.ts +0 -458
- package/spec-cli/src/localIssues.ts +0 -538
- package/spec-cli/src/materialize.ts +0 -412
- package/spec-cli/src/mentions.ts +0 -81
- package/spec-cli/src/opencode-headless.ts +0 -205
- package/spec-cli/src/pi-headless.ts +0 -153
- package/spec-cli/src/plugin-harness.ts +0 -157
- package/spec-cli/src/process-identity.ts +0 -207
- package/spec-cli/src/project-identity.ts +0 -73
- package/spec-cli/src/project-store.ts +0 -17
- package/spec-cli/src/pty-bridge.ts +0 -396
- package/spec-cli/src/pty-helper.mjs +0 -96
- package/spec-cli/src/pty-native-helper.mjs +0 -22
- package/spec-cli/src/ranker.ts +0 -191
- package/spec-cli/src/reaper.ts +0 -125
- package/spec-cli/src/resilience.ts +0 -41
- package/spec-cli/src/reviewSnapshot.ts +0 -28
- package/spec-cli/src/reviews.ts +0 -405
- package/spec-cli/src/runtime-guard.ts +0 -35
- package/spec-cli/src/runtime-ownership.ts +0 -88
- package/spec-cli/src/search.bench.mjs +0 -80
- package/spec-cli/src/search.ts +0 -63
- package/spec-cli/src/session-cursors.ts +0 -91
- package/spec-cli/src/session-follow.ts +0 -182
- package/spec-cli/src/session-timeline.ts +0 -98
- package/spec-cli/src/sessions.ts +0 -3403
- package/spec-cli/src/sh.ts +0 -3
- package/spec-cli/src/slash-commands.ts +0 -318
- package/spec-cli/src/source-files.ts +0 -104
- package/spec-cli/src/specs.ts +0 -498
- package/spec-cli/src/supervise.ts +0 -245
- package/spec-cli/src/tree.ts +0 -118
- package/spec-cli/src/tsx-bin.ts +0 -18
- package/spec-cli/src/uninstall.ts +0 -171
- package/spec-cli/src/uploads.ts +0 -253
- package/spec-cli/src/worktree-sources.ts +0 -42
- package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
- package/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -28
- package/spec-cli/templates/spec/project/.plugins/core/spec.md +0 -23
- package/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -32
- package/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -9
- package/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -15
- package/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -18
- package/spec-dashboard/dist/assets/App-F9uaAVcH.js +0 -2
- package/spec-dashboard/dist/assets/Dashboard-Ba_jhxp1.js +0 -27
- package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
- package/spec-dashboard/dist/assets/EvalsPage-FixoOg_n.js +0 -2
- package/spec-dashboard/dist/assets/IssuesPage-CuKLFhH3.js +0 -1
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/spec-dashboard/dist/assets/MobileApp-CHgEHORJ.js +0 -2
- package/spec-dashboard/dist/assets/Modal-CQgYymmr.js +0 -1
- package/spec-dashboard/dist/assets/PageScroll-hT7UTLvD.js +0 -1
- package/spec-dashboard/dist/assets/ProjectsPage-CtXxakF9.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
- package/spec-dashboard/dist/assets/SessionInterface-Bpie-9fs.js +0 -39
- package/spec-dashboard/dist/assets/SessionWindow-CixDi4PI.js +0 -1
- package/spec-dashboard/dist/assets/Settings-C2MsucfE.js +0 -1
- package/spec-dashboard/dist/assets/Thread-C6Go8HRh.js +0 -13
- package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
- package/spec-dashboard/dist/assets/data-B-RQmit6.js +0 -1
- package/spec-dashboard/dist/assets/index-CsI8DElI.css +0 -1
- package/spec-dashboard/dist/assets/index-DrVao0Ep.js +0 -41
- package/spec-dashboard/dist/assets/launch-BBH02b1v.js +0 -276
- package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
- package/spec-dashboard/dist/assets/terminalFont-C6oGNKrf.js +0 -1
- package/spec-dashboard/dist/index.html +0 -29
- package/spec-dashboard/src/reviewFilters.js +0 -322
- package/spec-dashboard/src/reviewQuery.js +0 -174
- package/spec-dashboard/src/session.js +0 -199
- package/spec-eval/src/cache.ts +0 -57
- package/spec-eval/src/cli.ts +0 -889
- package/spec-eval/src/evaltab.ts +0 -262
- package/spec-eval/src/filing.ts +0 -39
- package/spec-eval/src/freshness.ts +0 -557
- package/spec-eval/src/humanok.ts +0 -28
- package/spec-eval/src/scenariofresh.ts +0 -244
- package/spec-eval/src/scenarios.ts +0 -672
- package/spec-eval/src/sessioneval.ts +0 -2226
- package/spec-eval/src/sidecar.ts +0 -93
- package/spec-eval/src/timeline.ts +0 -54
- package/spec-forge/src/__fixtures__/github-forge.json +0 -63
- package/spec-forge/src/cache.ts +0 -66
- package/spec-forge/src/cli.ts +0 -99
- package/spec-forge/src/drivers/github.ts +0 -132
- package/spec-forge/src/drivers/gitlab.ts +0 -153
- package/spec-forge/src/drivers.ts +0 -73
- package/spec-forge/src/links.ts +0 -72
- package/spec-forge/src/needs-eval.ts +0 -29
- package/spec-forge/src/port.ts +0 -36
- package/spec-forge/src/resident.ts +0 -46
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
package/spec-cli/src/lint.ts
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync, statSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { repoRoot, git, sourceIndexes, rowsFor, treeFilePaths, treeFileText, type DriftPathEvent } from './git.js'
|
|
4
|
-
import { loadSpecs, parseFrontmatter } from './specs.js'
|
|
5
|
-
import { readJsonConfig } from './layout.js'
|
|
6
|
-
import { extractors, extractorFor, extOf, parseCodeEntry, relationClaimsPath, resolveAnchor, resolveSelectors, windowEvents, anchorHitQueries } from './anchors.js'
|
|
7
|
-
import { DEFAULT_TEST_GLOBS, sourcePolicyDescription, trackedSourceFiles } from './source-files.js'
|
|
8
|
-
|
|
9
|
-
export type Finding = { level: 'error' | 'warn'; rule: string; spec?: string; file?: string; msg: string }
|
|
10
|
-
|
|
11
|
-
export type LintConfig = {
|
|
12
|
-
governedRoots: string[] // dirs whose tracked source files must each be governed by a spec. '.' = whole project.
|
|
13
|
-
sourceIncludeGlobs: string[] | null // null includes every tracked regular text file; [] intentionally includes none
|
|
14
|
-
sourceExcludeGlobs: string[] // explicit source-policy subtraction
|
|
15
|
-
sourceExtensions: string[] | null // compatibility shorthand compiled into sourceIncludeGlobs
|
|
16
|
-
testGlobs: string[] // globs EXCLUDED from coverage; set [] to govern tests too
|
|
17
|
-
maxOwners: number // warn when a file is governed (code:) by > this many nodes
|
|
18
|
-
scenarioTags: string[] // the closed vocabulary an eval scenario's `tags:` must draw from; extend it to mint a new tag
|
|
19
|
-
scopedCodeMiss: 'warn' | 'ignore' // the file-level drift ADVISORY on a selector-scoped code: file whose window has no
|
|
20
|
-
// selector hit ([[code-anchor]]). 'warn' (default) keeps today's drift warning; 'ignore'
|
|
21
|
-
// silences ONLY that advisory — hit blocks, bare code drift, integrity, acks, related
|
|
22
|
-
// semantics, and eval freshness are all untouched by this knob.
|
|
23
|
-
}
|
|
24
|
-
const DEFAULT_CONFIG: LintConfig = {
|
|
25
|
-
governedRoots: ['spec-dashboard/src', 'spec-cli/src'],
|
|
26
|
-
sourceIncludeGlobs: null,
|
|
27
|
-
sourceExcludeGlobs: [],
|
|
28
|
-
sourceExtensions: null,
|
|
29
|
-
testGlobs: DEFAULT_TEST_GLOBS,
|
|
30
|
-
maxOwners: 3,
|
|
31
|
-
scenarioTags: ['frontend-e2e', 'backend-api', 'cli', 'desktop', 'mobile'],
|
|
32
|
-
scopedCodeMiss: 'warn',
|
|
33
|
-
}
|
|
34
|
-
export function loadConfig(root: string, pendingSource?: string | null): LintConfig {
|
|
35
|
-
// Absent spexcode.json → tuned defaults; a MALFORMED one throws LOUD (readJsonConfig) rather than
|
|
36
|
-
// silently reverting the author's budgets to defaults and green-washing the very warnings they tuned.
|
|
37
|
-
let parsed: any
|
|
38
|
-
if (pendingSource === undefined) parsed = readJsonConfig(join(root, 'spexcode.json'))
|
|
39
|
-
else if (pendingSource === null) parsed = {}
|
|
40
|
-
else {
|
|
41
|
-
try { parsed = JSON.parse(pendingSource) }
|
|
42
|
-
catch (e: any) { throw new Error(`invalid JSON in candidate spexcode.json: ${e?.message ?? e}`) }
|
|
43
|
-
}
|
|
44
|
-
const c = parsed?.lint ?? {}
|
|
45
|
-
const merged = { ...DEFAULT_CONFIG, ...c }
|
|
46
|
-
return normalizeConfig(merged)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Compile every author-facing source selector into one include-minus-exclude/test policy. In particular,
|
|
50
|
-
// sourceExtensions is compatibility syntax only: it contributes include globs and never reaches discovery.
|
|
51
|
-
export function normalizeConfig(cfg: LintConfig): LintConfig {
|
|
52
|
-
// a mistyped enum silently reverting to the default would green-wash (or over-warn) exactly the
|
|
53
|
-
// advisory the author meant to tune — same fail-loud rule as a malformed spexcode.json.
|
|
54
|
-
if (cfg.scopedCodeMiss !== 'warn' && cfg.scopedCodeMiss !== 'ignore')
|
|
55
|
-
throw new Error(`spexcode.json lint.scopedCodeMiss must be "warn" or "ignore", got ${JSON.stringify(cfg.scopedCodeMiss)}`)
|
|
56
|
-
const dedot = (xs: string[]) => xs.map((x) => x.replace(/^\.+/, ''))
|
|
57
|
-
const anyDepth = (xs: string[]) => xs.map((g) => (g.includes('/') ? g : `**/${g}`))
|
|
58
|
-
const extensions = cfg.sourceExtensions === null ? null : dedot(cfg.sourceExtensions)
|
|
59
|
-
const includes = cfg.sourceIncludeGlobs === null && extensions === null
|
|
60
|
-
? null
|
|
61
|
-
: [...new Set([...anyDepth(cfg.sourceIncludeGlobs ?? []), ...(extensions ?? []).map((ext) => `**/*.${ext}`)])]
|
|
62
|
-
return {
|
|
63
|
-
...cfg,
|
|
64
|
-
sourceIncludeGlobs: includes,
|
|
65
|
-
sourceExcludeGlobs: anyDepth(cfg.sourceExcludeGlobs),
|
|
66
|
-
sourceExtensions: extensions,
|
|
67
|
-
testGlobs: anyDepth(cfg.testGlobs),
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export type SpecLintOptions = { tip?: string }
|
|
72
|
-
|
|
73
|
-
function untrackedAdoptionFiles(root: string): string[] {
|
|
74
|
-
if (git(['-C', root, 'rev-parse', '--is-inside-work-tree']).trim() !== 'true') return []
|
|
75
|
-
const status = git([
|
|
76
|
-
'-C', root,
|
|
77
|
-
'-c', 'core.quotePath=false',
|
|
78
|
-
'status', '--porcelain=v1', '-z', '--untracked-files=all',
|
|
79
|
-
'--', '.spec', 'spexcode.json',
|
|
80
|
-
])
|
|
81
|
-
return status.split('\0')
|
|
82
|
-
.filter((entry) => entry.startsWith('?? '))
|
|
83
|
-
.map((entry) => entry.slice(3))
|
|
84
|
-
.filter(Boolean)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function pendingChangedPaths(root: string, tip: string): string[] {
|
|
88
|
-
try {
|
|
89
|
-
git(['-C', root, 'rev-parse', `${tip}^{commit}`])
|
|
90
|
-
// `-m` compares a merge with every parent. An `ours` merge can leave the result tree
|
|
91
|
-
// identical to its first parent while still making an unacknowledged side-branch commit
|
|
92
|
-
// reachable; first-parent-only paths would filter that debt out of the pending anchor window.
|
|
93
|
-
const fields = git(['-C', root, '-c', 'core.quotePath=false', 'diff-tree', '--no-commit-id', '--name-status', '-z', '-r', '-m', '-M', tip])
|
|
94
|
-
.split('\0').filter(Boolean)
|
|
95
|
-
const changed: string[] = []
|
|
96
|
-
for (let i = 0; i < fields.length;) {
|
|
97
|
-
const status = fields[i++]
|
|
98
|
-
if (/^[RC]/.test(status)) {
|
|
99
|
-
changed.push(fields[i++], fields[i++])
|
|
100
|
-
} else {
|
|
101
|
-
changed.push(fields[i++])
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return [...new Set(changed.filter(Boolean))]
|
|
105
|
-
} catch { return [] }
|
|
106
|
-
}
|
|
107
|
-
// Cheap pending classification for the reference hook. It reads only the candidate tree and claims; it
|
|
108
|
-
// never constructs either history index. A normal lint call deliberately does not use this
|
|
109
|
-
// shortcut so its full findings/oracle contract remains unchanged.
|
|
110
|
-
export async function pendingTouchesGoverned(root: string, tip: string): Promise<boolean> {
|
|
111
|
-
// A merge can introduce reachable side-branch debt without changing the result tree. The first-parent
|
|
112
|
-
// diff is insufficient for a scope proof, so all multi-parent candidates stay on the full lint path.
|
|
113
|
-
const parentCount = git(['-C', root, 'rev-list', '--parents', '-n1', tip]).trim().split(/\s+/).length - 1
|
|
114
|
-
if (parentCount > 1) return true
|
|
115
|
-
const changed = pendingChangedPaths(root, tip)
|
|
116
|
-
if (!changed.length) return true
|
|
117
|
-
// `governedRoots` is source discovery policy, not the set of actual code claims: a spec may deliberately
|
|
118
|
-
// govern a path outside those roots. Read only the candidate spec tree (no history/drift indexes) so the
|
|
119
|
-
// scope proof follows the same code:/related: declarations that lint later enforces.
|
|
120
|
-
const specs = await loadSpecs(root, { tip, history: null, drift: null })
|
|
121
|
-
const claims = specs.flatMap((spec) => [...spec.code, ...spec.related])
|
|
122
|
-
return changed.some((path) => claims.some((claim) => relationClaimsPath(claim, path))
|
|
123
|
-
|| path === 'spexcode.json' || path === 'spexcode.local.json'
|
|
124
|
-
|| (path.startsWith('.spec/') && !path.startsWith('.spec/.issues/'))
|
|
125
|
-
|| path === '.spec')
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function specLint(root = repoRoot(), regs = extractors(root), options: SpecLintOptions = {}): Promise<Finding[]> {
|
|
129
|
-
const tip = options.tip ?? 'HEAD'
|
|
130
|
-
const pending = tip !== 'HEAD'
|
|
131
|
-
const changed = pending ? pendingChangedPaths(root, tip) : []
|
|
132
|
-
const files = pending ? treeFilePaths(root, tip) : null
|
|
133
|
-
const directories = new Set<string>()
|
|
134
|
-
for (const file of files ?? []) {
|
|
135
|
-
const parts = file.split('/')
|
|
136
|
-
for (let i = 1; i < parts.length; i++) directories.add(parts.slice(0, i).join('/'))
|
|
137
|
-
}
|
|
138
|
-
const existsAtTip = (path: string) => files ? files.has(path) || directories.has(path.replace(/\/+$/, '')) : existsSync(join(root, path))
|
|
139
|
-
const isDirectoryAtTip = (path: string) => files
|
|
140
|
-
? !files.has(path) && directories.has(path.replace(/\/+$/, ''))
|
|
141
|
-
: statSync(join(root, path)).isDirectory()
|
|
142
|
-
const textAtTip = (path: string) => pending ? treeFileText(root, tip, path) : readFileSync(join(root, path), 'utf8')
|
|
143
|
-
const cfg = loadConfig(root, pending ? treeFileText(root, tip, 'spexcode.json') : undefined)
|
|
144
|
-
const untracked = untrackedAdoptionFiles(root)
|
|
145
|
-
if (untracked.length) {
|
|
146
|
-
const shown = untracked.slice(0, 6)
|
|
147
|
-
const suffix = untracked.length > shown.length ? ` (+${untracked.length - shown.length} more)` : ''
|
|
148
|
-
return [{
|
|
149
|
-
level: 'error',
|
|
150
|
-
rule: 'integrity',
|
|
151
|
-
msg: `project source of truth is untracked: ${shown.join(', ')}${suffix} — add it with \`git add .spec spexcode.json\` and commit it; generated harness files such as .codex/, .claude/, and AGENTS.md are machine-local`,
|
|
152
|
-
}]
|
|
153
|
-
}
|
|
154
|
-
const governed = trackedSourceFiles(root, cfg.governedRoots, cfg, tip)
|
|
155
|
-
const [hidx, didx] = await sourceIndexes(root, tip)
|
|
156
|
-
const specs = await loadSpecs(root, { tip, history: hidx, drift: didx })
|
|
157
|
-
const out: Finding[] = []
|
|
158
|
-
|
|
159
|
-
// integrity + build the file -> owners map. A relation's STRUCTURAL problems (a duplicate entry,
|
|
160
|
-
// bare/scoped mixing on one base path, a selector on a glob — all from parseRelation,
|
|
161
|
-
// [[code-anchor]]) are integrity errors: malformed edges block like broken ones.
|
|
162
|
-
const owners = new Map<string, string[]>()
|
|
163
|
-
const claimed = new Set<string>()
|
|
164
|
-
const governedClaims = new Set<string>()
|
|
165
|
-
for (const s of specs) {
|
|
166
|
-
for (const p of s.relationProblems)
|
|
167
|
-
out.push({ level: 'error', rule: 'integrity', spec: s.id, msg: `'${s.id}' ${p}` })
|
|
168
|
-
const scopedPaths = new Set(s.codeScoped.map((e) => e.path))
|
|
169
|
-
for (const f of s.code) {
|
|
170
|
-
if (!existsAtTip(f))
|
|
171
|
-
out.push({ level: 'error', rule: 'integrity', spec: s.id, file: f, msg: `spec '${s.id}' lists a missing file: ${f}` })
|
|
172
|
-
claimed.add(f)
|
|
173
|
-
governedClaims.add(f)
|
|
174
|
-
// a selector-SCOPED entry claims named units, not the whole file, so it stays out of the owners
|
|
175
|
-
// bound below ([[code-anchor]]) — `spex spec owner` still displays it as a (scoped) governor.
|
|
176
|
-
if (!scopedPaths.has(f)) owners.set(f, [...(owners.get(f) ?? []), s.id])
|
|
177
|
-
}
|
|
178
|
-
// one-govern: a node is source of truth for at most ONE file — DISTINCT base paths; several
|
|
179
|
-
// `path#symbol` selectors on the same file are one subject — so drift/eval/ack have a single
|
|
180
|
-
// unambiguous subject (see [[governed-related]]). >1 is a defect — pick the true subject, demote the
|
|
181
|
-
// rest to related. ERROR (the node-side twin of too-many-owners' file-side bound). 0 is fine.
|
|
182
|
-
if (s.code.length > 1)
|
|
183
|
-
out.push({ level: 'error', rule: 'one-govern', spec: s.id, msg: `'${s.id}' governs ${s.code.length} files [${s.code.join(', ')}] — a node is source of truth for at most ONE. Keep the true subject in code:, move the rest to related:` })
|
|
184
|
-
}
|
|
185
|
-
// a file is COVERED if any node GOVERNS (code:) or merely REFERENCES (related:) it; integrity covers both.
|
|
186
|
-
// `related:` is the coverage net: govern is a sharp ideally-one-file pointer, so most files are reached by
|
|
187
|
-
// related, not govern (see [[governed-related]]). It carries coverage but never drift, never eval freshness.
|
|
188
|
-
for (const s of specs) for (const f of s.related) {
|
|
189
|
-
if (!existsAtTip(f))
|
|
190
|
-
out.push({ level: 'error', rule: 'integrity', spec: s.id, file: f, msg: `spec '${s.id}' lists a missing related file: ${f}` })
|
|
191
|
-
claimed.add(f)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// A candidate may retire a node together with the implementation it owned, or transfer that path to a
|
|
195
|
-
// new node. It may not delete the governor while leaving the governed subject behind: ordinary coverage
|
|
196
|
-
// is advisory, so without this transition check a candidate could erase the very contract that would
|
|
197
|
-
// reject its code change. Read only deleted spec blobs from HEAD; no second history index is needed.
|
|
198
|
-
if (pending) {
|
|
199
|
-
let deleted: string[] = []
|
|
200
|
-
try {
|
|
201
|
-
deleted = git(['-C', root, '-c', 'core.quotePath=false', 'diff', '--diff-filter=D', '--name-only', 'HEAD', tip, '--', '.spec'])
|
|
202
|
-
.split('\n').map((path) => path.trim()).filter((path) => path.endsWith('/spec.md'))
|
|
203
|
-
} catch { /* an unreadable comparison is already surfaced by the candidate history reads */ }
|
|
204
|
-
for (const specPath of deleted) {
|
|
205
|
-
const oldSource = treeFileText(root, 'HEAD', specPath)
|
|
206
|
-
if (oldSource === null) continue
|
|
207
|
-
const value = parseFrontmatter(oldSource).fm.code
|
|
208
|
-
const rows = Array.isArray(value) ? value : value ? [value] : []
|
|
209
|
-
for (const row of rows) {
|
|
210
|
-
const path = parseCodeEntry(row).path
|
|
211
|
-
if (existsAtTip(path) && !governedClaims.has(path))
|
|
212
|
-
out.push({ level: 'error', rule: 'integrity', file: path, msg: `candidate deletes governor '${specPath}' but leaves its governed subject '${path}' without a code: owner — delete the retired implementation too, or transfer it to a real node in this commit` })
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// id-format: a node id (its leaf dir basename) passes an EXACT per-character whitelist — an ascii char
|
|
218
|
-
// must be [a-z0-9-]; a non-ascii char must be a unicode letter/number (judged on NFC, the mint's
|
|
219
|
-
// canonical form) — and is UNIQUE tree-wide (ERROR). This is THE id vocabulary, defined once (the
|
|
220
|
-
// spec-lint node's rule table) and referenced by [[mentions]] / [[id-url-safe]]: CJK and every other
|
|
221
|
-
// letter script is first-class, exactly what the script-agnostic resolve machinery already accepts.
|
|
222
|
-
// Everything else is forbidden by construction, no heuristics — space, '/', uppercase Latin (lowercase
|
|
223
|
-
// is the Latin norm), control chars, and '_' (reserved as the mint's parent-qualification join; a '_'
|
|
224
|
-
// inside a basename would make that join ambiguous). One optional leading dot is allowed — the
|
|
225
|
-
// reflexive plugin root `.plugins` is dot-prefixed by design. Uniqueness is what keeps the leaf THE id:
|
|
226
|
-
// on a collision the mint must parent-qualify with `_`, so every surface suddenly speaks a longer id
|
|
227
|
-
// than the dir name — legal to the machinery, illegible to people.
|
|
228
|
-
const ID_RE = /^\.?(?:[a-z0-9-]|(?![\x00-\x7F])[\p{L}\p{N}])+$/u
|
|
229
|
-
const leafOf = (p: string) => { const segs = p.split('/'); return segs[segs.length - 2] }
|
|
230
|
-
const byLeaf = new Map<string, string[]>()
|
|
231
|
-
for (const s of specs) {
|
|
232
|
-
const leaf = leafOf(s.path)
|
|
233
|
-
byLeaf.set(leaf, [...(byLeaf.get(leaf) ?? []), s.path])
|
|
234
|
-
if (!ID_RE.test(leaf.normalize('NFC')))
|
|
235
|
-
out.push({ level: 'error', rule: 'id-format', spec: s.id, msg: `node dir '${leaf}' is not a valid id — each char is ascii [a-z0-9-] or a non-ascii unicode letter/number (one optional leading dot); space, '/', '_', uppercase Latin and control chars are forbidden; rename the directory` })
|
|
236
|
-
}
|
|
237
|
-
for (const [leaf, paths] of byLeaf) {
|
|
238
|
-
if (paths.length > 1)
|
|
239
|
-
out.push({ level: 'error', rule: 'id-format', msg: `leaf id '${leaf}' names ${paths.length} nodes [${paths.map((p) => p.replace(/\/spec\.md$/, '')).join(', ')}] — a leaf id is unique tree-wide; rename all but one` })
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// confusable-id: two leaf ids one edit apart read as the same word (WARN — a typo in either reaches a
|
|
243
|
-
// real, wrong node). Deliberately conservative: distance exactly 1, so hierarchy naming like
|
|
244
|
-
// graph/graph-delivery (a whole suffix apart) and verb pairs like evidence-put/evidence-get (distance 2)
|
|
245
|
-
// never warn — better to miss a borderline pair than to nag legitimate siblings. Distance is measured
|
|
246
|
-
// in CODE POINTS, script-agnostic: a CJK pair one character apart (节点/结点 — a classic homophone
|
|
247
|
-
// IME slip) warns like an ascii pair, an astral char counts as one edit (not two surrogate units), and
|
|
248
|
-
// a pure-CJK id can never sit one edit from a pure-ascii one.
|
|
249
|
-
const lev1 = (a: string, b: string): boolean => {
|
|
250
|
-
const A = [...a], B = [...b]
|
|
251
|
-
if (Math.abs(A.length - B.length) > 1 || a === b) return false
|
|
252
|
-
let i = 0
|
|
253
|
-
while (i < A.length && i < B.length && A[i] === B[i]) i++
|
|
254
|
-
const rest = (x: string[], k: number) => x.slice(k).join('')
|
|
255
|
-
return rest(A, i + 1) === rest(B, i + 1) || rest(A, i + 1) === rest(B, i) || rest(A, i) === rest(B, i + 1)
|
|
256
|
-
}
|
|
257
|
-
const leaves = [...byLeaf.keys()]
|
|
258
|
-
for (let i = 0; i < leaves.length; i++) for (let j = i + 1; j < leaves.length; j++) {
|
|
259
|
-
if (lev1(leaves[i], leaves[j]))
|
|
260
|
-
out.push({ level: 'warn', rule: 'confusable-id', msg: `leaf ids '${leaves[i]}' and '${leaves[j]}' are one edit apart — easily confused; if they are distinct concepts, rename one to read apart` })
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// mention: a `[[id]]` in a body must name a real node (ERROR — a dangling mention is a broken edge in
|
|
264
|
-
// the very graph the tree exists to keep honest). Checked against the SAME minted ids every other
|
|
265
|
-
// surface resolves ([[id-url-safe]]). Prose only: a fenced block or inline `code span` is sample text
|
|
266
|
-
// (`[[node]]`, `[[<id>]]` placeholders live there), not a reference.
|
|
267
|
-
const idSet = new Set(specs.map((s) => s.id))
|
|
268
|
-
const MENTION_RE = /\[\[(\.?[\p{L}\p{N}_-]+)\]\]/gu
|
|
269
|
-
for (const s of specs) {
|
|
270
|
-
let inFence = false
|
|
271
|
-
for (const rawLine of s.body.split('\n')) {
|
|
272
|
-
if (/^\s*```/.test(rawLine)) { inFence = !inFence; continue }
|
|
273
|
-
if (inFence) continue
|
|
274
|
-
const line = rawLine.replace(/`[^`]*`/g, '')
|
|
275
|
-
for (const m of line.matchAll(MENTION_RE)) {
|
|
276
|
-
if (!idSet.has(m[1]))
|
|
277
|
-
out.push({ level: 'error', rule: 'mention', spec: s.id, msg: `'${s.id}' mentions [[${m[1]}]] — no such node; retarget or drop it (backtick it if it is sample text)` })
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// living: a spec body describes the node's CURRENT intent — it is not a changelog. Version history
|
|
283
|
-
// (every content commit, its reason/session/line-diff) is read from git and shown in the dashboard's
|
|
284
|
-
// recent/history tabs, so a `## vN`-style heading in the body is duplicated, drift-prone state.
|
|
285
|
-
// Reject it. Fence-aware — a `## v2` inside a ``` block is sample text, not a heading.
|
|
286
|
-
const VER_HEADING = /^#{1,6}\s+v\d+\b/
|
|
287
|
-
for (const s of specs) {
|
|
288
|
-
let inFence = false
|
|
289
|
-
for (const line of s.body.split('\n')) {
|
|
290
|
-
if (/^\s*```/.test(line)) { inFence = !inFence; continue }
|
|
291
|
-
if (!inFence && VER_HEADING.test(line))
|
|
292
|
-
out.push({ level: 'error', rule: 'living', spec: s.id, msg: `'${s.id}' has a changelog heading "${line.trim()}" — keep the body current-state; version history lives in git (recent/history tabs)` })
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// coverage: every governed source file must be claimed by at least one spec.
|
|
297
|
-
if (governed.length === 0)
|
|
298
|
-
out.push({ level: 'warn', rule: 'coverage', msg: `governing NOTHING — 0 source candidates under governedRoots [${cfg.governedRoots.join(', ')}]; ${sourcePolicyDescription(cfg)}. Repair these knobs under the "lint" key in spexcode.json (top-level keys are ignored): governedRoots, sourceIncludeGlobs, sourceExcludeGlobs, testGlobs; sourceExtensions remains compatibility shorthand for include globs.` })
|
|
299
|
-
for (const f of governed)
|
|
300
|
-
if (!claimed.has(f)) out.push({ level: 'warn', rule: 'coverage', file: f, msg: `no spec governs: ${f}` })
|
|
301
|
-
|
|
302
|
-
// too-many-owners: many nodes governing one file is ordinary composition (drift fans to each — correct,
|
|
303
|
-
// every owner has a stake), so this is NOT flagged. Only an OVER-owned file is a smell — governed by more
|
|
304
|
-
// than maxOwners nodes, it has accreted more independently-specified functionality than one file should
|
|
305
|
-
// hold (see [[governed-related]]). ONE summary line (not one per file — its own wall of noise): the count,
|
|
306
|
-
// the worst offenders, and the remedy, which blames the FILE not the ownership — SPLIT it so each governor
|
|
307
|
-
// reclaims its own module; or merge the nodes if they're one concern; or give it a single foundation owner.
|
|
308
|
-
const over = [...owners].filter(([, ids]) => ids.length > cfg.maxOwners).sort((a, b) => b[1].length - a[1].length)
|
|
309
|
-
if (over.length) {
|
|
310
|
-
const top = over.slice(0, 5).map(([f, ids]) => `${f.split('/').pop()}(${ids.length})`).join(', ')
|
|
311
|
-
out.push({ level: 'warn', rule: 'owners', msg: `${over.length} file(s) are governed by > ${cfg.maxOwners} nodes — each holds more separately-specified functionality than one file should. Worst: ${top}. SPLIT the file so each governor owns its own module (or merge the nodes, or give it a single foundation owner + related:).` })
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// code anchors ([[code-anchor]]): a code:/related: entry may pin named units (`path#symbol` — any
|
|
315
|
-
// number per base file, OR'd). On code:, the anchor is the BLOCKING tier of drift: a window
|
|
316
|
-
// commit (spec's last version..tip, ordinary diff or merge-authored combined diff) whose zero-context hunks
|
|
317
|
-
// intersect any pinned unit's line range — extracted from the file AS OF that commit, by the
|
|
318
|
-
// extension's ONE designated extractor — is ONE anchor-drift ERROR naming the hit selectors, unless a
|
|
319
|
-
// Spec-OK ack covers it. On related:, the SAME engine yields only a soft warn on a hit — a scoped
|
|
320
|
-
// related miss is silent (never blocks, no ack, no eval freshness). Resolution failures are never
|
|
321
|
-
// silent for either relation: a dead or ambiguous selector, a selector on a directory, and an
|
|
322
|
-
// unparseable working-tree file ERROR. An extension with no designated extractor, or a designated
|
|
323
|
-
// extractor that cannot run here, also ERRORS but skips those anchors so the remaining checks continue.
|
|
324
|
-
type AnchorLintQuery = { id: string; version: number; relation: 'code' | 'related'; path: string; symbols: string[]; win: DriftPathEvent[] }
|
|
325
|
-
type AnchorLintStep = { finding: Finding } | { query: AnchorLintQuery }
|
|
326
|
-
const readyWarned = new Set<string>()
|
|
327
|
-
const anchorSteps: AnchorLintStep[] = []
|
|
328
|
-
for (const s of specs) {
|
|
329
|
-
for (const { relation, entries } of [{ relation: 'code' as const, entries: s.codeScoped }, { relation: 'related' as const, entries: s.relatedScoped }]) {
|
|
330
|
-
for (const { path, selectors } of entries) {
|
|
331
|
-
if (pending && !changed.some((file) => relationClaimsPath(path, file))) continue
|
|
332
|
-
const x = extractorFor(regs, extOf(path))
|
|
333
|
-
if (!x) {
|
|
334
|
-
anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `'${s.id}' anchors ${path}#${selectors.join(', #')} (${relation}:), but no extractor is designated for '.${extOf(path)}' files — anchor validation was skipped and remains unverified; add a LangSpec row (anchors.ts) or drop the selector(s)` } })
|
|
335
|
-
continue
|
|
336
|
-
}
|
|
337
|
-
const ready = x.ready()
|
|
338
|
-
if (ready !== true) {
|
|
339
|
-
// once per (extractor, reason), even across several anchored nodes — one repair, one message.
|
|
340
|
-
if (!readyWarned.has(x.id + ready)) { readyWarned.add(x.id + ready); anchorSteps.push({ finding: { level: 'error', rule: 'integrity', msg: `anchor extractor '${x.id}' cannot run: ${ready}` } }) }
|
|
341
|
-
continue
|
|
342
|
-
}
|
|
343
|
-
if (!existsAtTip(path)) continue // the missing FILE already errored above
|
|
344
|
-
if (isDirectoryAtTip(path)) {
|
|
345
|
-
anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `'${s.id}' puts a selector on a directory (${relation}: ${path}#${selectors[0]}) — a selector scopes ONE real file` } })
|
|
346
|
-
continue
|
|
347
|
-
}
|
|
348
|
-
let units
|
|
349
|
-
try {
|
|
350
|
-
const source = textAtTip(path)
|
|
351
|
-
if (source === null) throw new Error(`candidate tree has no file '${path}'`)
|
|
352
|
-
units = x.extract(source, path)
|
|
353
|
-
} catch (e: any) {
|
|
354
|
-
anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `anchor ${path}#${selectors.join(', #')} ('${s.id}') is unverifiable — the current file does not parse: ${e?.message ?? e}` } })
|
|
355
|
-
continue
|
|
356
|
-
}
|
|
357
|
-
// each selector resolves (or errors) on its own; only the live ones feed the window engine.
|
|
358
|
-
// The dead/ambiguous verdict itself comes from the ONE shared classifier ([[code-anchor]]); only the
|
|
359
|
-
// wording of the gate's findings lives here.
|
|
360
|
-
const live: string[] = []
|
|
361
|
-
for (const res of resolveSelectors(units, selectors)) {
|
|
362
|
-
const sym = res.selector
|
|
363
|
-
if ('dead' in res) {
|
|
364
|
-
anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `dead anchor: ${path}#${sym} ('${s.id}') names no unit on the current tree — the unit was deleted or renamed; update the spec's ${relation}: entry to follow it` } })
|
|
365
|
-
continue
|
|
366
|
-
}
|
|
367
|
-
if ('ambiguous' in res) {
|
|
368
|
-
anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `ambiguous anchor: ${path}#${sym} ('${s.id}') names ${res.ambiguous} same-named units in one file — an anchor must be unique; rename one unit` } })
|
|
369
|
-
continue
|
|
370
|
-
}
|
|
371
|
-
if (res.ok.typeOnly)
|
|
372
|
-
anchorSteps.push({ finding: { level: 'warn', rule: 'anchor', spec: s.id, file: path, msg: `${path}#${sym} anchors a ${res.ok.kind} — anchoring a type is usually wrong (types reshape with every refactor); anchor the behaviour-bearing unit instead` } })
|
|
373
|
-
live.push(sym)
|
|
374
|
-
}
|
|
375
|
-
if (!live.length) continue
|
|
376
|
-
const since = rowsFor(hidx, s.path)[0]?.hash || ''
|
|
377
|
-
const win = windowEvents(didx, since, path, s.id)
|
|
378
|
-
if (!win.length) continue
|
|
379
|
-
anchorSteps.push({ query: { id: s.id, version: s.version, relation, path, symbols: live, win } })
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
const anchorHits = await anchorHitQueries(root, anchorSteps.flatMap((step) => 'query' in step ? [{ win: step.query.win, symbols: step.query.symbols }] : []), regs)
|
|
384
|
-
let hitIndex = 0
|
|
385
|
-
for (const step of anchorSteps) {
|
|
386
|
-
if ('finding' in step) { out.push(step.finding); continue }
|
|
387
|
-
const { id, version, relation, path } = step.query
|
|
388
|
-
const hits = anchorHits[hitIndex++]
|
|
389
|
-
if (!hits.length) continue
|
|
390
|
-
const hitSyms = [...new Set(hits.flatMap((h) => h.selectors))]
|
|
391
|
-
const shas = hits.map((h) => h.commit.slice(0, 8)).join(', ')
|
|
392
|
-
const unparseable = hits.filter((h) => h.unparseable)
|
|
393
|
-
const parseNote = unparseable.length ? ` (${unparseable.length} of these could not be parsed at that commit — counted as hits conservatively)` : ''
|
|
394
|
-
if (relation === 'code') {
|
|
395
|
-
const current = pending && hits.some((hit) => hit.commit === tip)
|
|
396
|
-
const older = pending && hits.some((hit) => hit.commit !== tip)
|
|
397
|
-
const remedy = !pending
|
|
398
|
-
? `update the spec, or 'spex spec ack ${id} --reason "…"' if the contract still holds`
|
|
399
|
-
: current && older
|
|
400
|
-
? `update the spec in this commit; its own hit can be declared by retrying with 'git commit --trailer "Spec-OK: ${id}" …', but the listed older debt must be cleared first (an in-commit declaration never pardons ancestors)`
|
|
401
|
-
: current
|
|
402
|
-
? `update the spec in this commit, or retry with 'git commit --trailer "Spec-OK: ${id}" …'; a later empty ack cannot pre-author this candidate`
|
|
403
|
-
: `update the spec in this commit, or clear this older debt with 'spex spec ack ${id} --reason "…"' before retrying the candidate`
|
|
404
|
-
out.push({ level: 'error', rule: 'anchor-drift', spec: id, file: path, msg: `${path}#${hitSyms.join(', #')} was changed by ${hits.length} commit(s) since spec '${id}' v${version} [${shas}]${parseNote} — the anchored contract's code moved: ${remedy}` })
|
|
405
|
-
} else
|
|
406
|
-
out.push({ level: 'warn', rule: 'related-drift', spec: id, file: path, msg: `related ${path}#${hitSyms.join(', #')} ('${id}') was changed by ${hits.length} commit(s) since v${version} [${shas}]${parseNote} — a scoped dependency shifted, worth a glance (SOFT: never blocks, no ack, no eval staleness)` })
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// drift: a governed file has commits NOT yet reflected in its spec. Judged by true git ancestry —
|
|
410
|
-
// loadSpecs computes `driftFiles` via driftFor() over the one cached driftIndex walk (git.ts): a
|
|
411
|
-
// commit to the file counts iff it is NOT reachable from the spec's latest version (in-memory
|
|
412
|
-
// parent-edge reachability, the equivalent of `rev-list <version>..HEAD -- <file>`), never a
|
|
413
|
-
// log-position or timestamp guess.
|
|
414
|
-
// A selector-SCOPED code file keeps this file-level advisory by default (a miss still nudges); the
|
|
415
|
-
// committed `lint.scopedCodeMiss: "ignore"` silences ONLY this advisory for scoped entries — the
|
|
416
|
-
// anchor engine's verdicts above (hit = block, resolution failures = integrity) are untouched.
|
|
417
|
-
for (const s of specs) {
|
|
418
|
-
const scopedPaths = new Set(s.codeScoped.map((e) => e.path))
|
|
419
|
-
for (const d of s.driftFiles) {
|
|
420
|
-
if (cfg.scopedCodeMiss === 'ignore' && scopedPaths.has(d.file)) continue
|
|
421
|
-
out.push({ level: 'warn', rule: 'drift', spec: s.id, file: d.file, msg: `${d.file} is ${d.behind} commit(s) ahead of spec '${s.id}' (v${s.version}) — may be stale` })
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// related drift: the SOFT tier ([[governed-related]]). A referenced file moved ahead of the node's
|
|
426
|
-
// version — a nudge that a dependency shifted. Same ancestry basis as govern drift, but WARN-only,
|
|
427
|
-
// never reaching the commit gate (driftGate reads govern) or eval freshness. It is COMMON (shared substrate and
|
|
428
|
-
// faces change often without re-versioning every referrer), so per-file it is a wall; like
|
|
429
|
-
// too-many-owners it collapses to ONE summary line, with the per-file detail riding the board
|
|
430
|
-
// (relatedDriftFiles). It stays a soft edge, never a per-file interruption.
|
|
431
|
-
const rd = specs.flatMap((s) => s.relatedDriftFiles.map((d) => ({ id: s.id, behind: d.behind })))
|
|
432
|
-
if (rd.length) {
|
|
433
|
-
const byNode = new Map<string, number>()
|
|
434
|
-
for (const d of rd) byNode.set(d.id, (byNode.get(d.id) ?? 0) + 1)
|
|
435
|
-
const worst = [...byNode].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([id, n]) => `${id}(${n})`).join(', ')
|
|
436
|
-
out.push({ level: 'warn', rule: 'related-drift', msg: `${rd.length} related file(s) across ${byNode.size} node(s) drifted ahead of their spec (SOFT — a dependency shifted, worth a glance; never blocks, no ack, no eval staleness). Most: ${worst}` })
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return out
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
export const DRIFT_GUIDANCE = `DRIFT — a governed file has moved ahead of its spec. A CHECKPOINT, not a chore: find WHERE the truth
|
|
443
|
-
broke along raw intent → expanded spec → code: link → code structure → implementation, fix THAT layer.
|
|
444
|
-
|
|
445
|
-
Inspect: spex spec lint which files, against which spec
|
|
446
|
-
the node's spec.md its raw source + expanded spec
|
|
447
|
-
git diff $(git log -1 --format=%H -- <spec.md>)..HEAD -- <file> the code delta since the spec
|
|
448
|
-
|
|
449
|
-
Diagnose, then apply the one honest remedy:
|
|
450
|
-
• contract changed → rewrite the spec body to the new intent, commit it (re-versions the node)
|
|
451
|
-
• only mechanics changed → spex spec ack <node> "checked — spec still valid" (give a real reason)
|
|
452
|
-
• implementation is WRONG → the spec is right; fix the CODE back toward it, then ack
|
|
453
|
-
• wrong code: link → the node shouldn't own this file (or owns it too broadly); fix frontmatter
|
|
454
|
-
• expanded spec ≠ raw → the spec drifted from human intent; fix the expanded spec to serve the raw
|
|
455
|
-
• structural mismatch → one file owned by many specs / a feature with no home: refactor so a file
|
|
456
|
-
maps to a node, or file an issue and link it (defer honestly)
|
|
457
|
-
|
|
458
|
-
Never patch. A reasoned ack or a real fix are recorded and re-judged at review; a blind ack is a lie.`
|