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
|
@@ -1,557 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { createHash } from 'node:crypto'
|
|
3
|
-
import { join, resolve } from 'node:path'
|
|
4
|
-
import { gitA, gitTry, headSha, currentGitBuildAbortSignal, gitAbortError, ancestorsOf, inAncestors, commitReachable, pathEvents, type DriftIndex, type DriftPathEvent, eventsSince } from '../../spec-cli/src/git.js'
|
|
5
|
-
import { anchorHitExists, extOf, extractorFor, extractors, resolveAnchor, resolveSelectors, type AnchorHitQuery, type Extractor, type RelationEntry, type Unit } from '../../spec-cli/src/anchors.js'
|
|
6
|
-
import type { Reading } from './sidecar.js'
|
|
7
|
-
import { scenarioCodeAxis, scenarioHash, type Scenario, type ScenarioCodeAxisSource } from './scenarios.js'
|
|
8
|
-
import { scenarioChangeCommits, scenarioBlocksAt, primeScenarioBlocks, type ScenarioIndex } from './scenariofresh.js'
|
|
9
|
-
|
|
10
|
-
// the CODE axis is touch-based (DriftIndex), so a code-file rename is out of scope — the same blind spot lint's code-drift has
|
|
11
|
-
|
|
12
|
-
export type StaleAxis = 'code' | 'scenario' | 'remark' | 'anchor'
|
|
13
|
-
|
|
14
|
-
// @@@ off-history content fallback - ancestry can't testify for a codeSha that isn't reachable from HEAD
|
|
15
|
-
// (fold/rebase/squash-merge/cherry-pick all orphan the anchor), but the TREES still can: while the anchor
|
|
16
|
-
// commit object exists locally, a pathspec-scoped `git diff <anchor> HEAD -- :(literal)<path>…` answers,
|
|
17
|
-
// for exactly the governed paths a caller asks about, whether content differs — so a history rewrite that
|
|
18
|
-
// left governed content byte-identical reads FRESH instead of false-positive stale. The question is per
|
|
19
|
-
// REQUESTED path on purpose: one whole-repo changed-path set per anchor is what melted a production fold
|
|
20
|
-
// (522 anchors × ~6k retained paths ≈ 520MB of Node heap), while the verdicts actually consumed are a
|
|
21
|
-
// handful of governed files plus the eval.md. The probe is fed at the call sites (like the remark track) so
|
|
22
|
-
// the decision functions stay pure over their inputs and the in-history fast path pays no extra git call;
|
|
23
|
-
// only when the commit object is truly gone (gc'd orphan) does the conservative rule remain — surfaced as
|
|
24
|
-
// the 'anchor' axis, so "anchor lost" reads differently from "content moved". No probe fed → the old
|
|
25
|
-
// always-conservative rule.
|
|
26
|
-
export type ContentProbe = {
|
|
27
|
-
// did THIS path's content change between the anchor tree and HEAD's? null = can't testify (anchor gone,
|
|
28
|
-
// or the path never primed) — callers stay conservative. Only primed paths are ever retained.
|
|
29
|
-
changed(anchorSha: string, path: string): boolean | null
|
|
30
|
-
// content CAN testify for this anchor: the object is readable and at least one verdict has settled
|
|
31
|
-
canTestify(anchorSha: string): boolean
|
|
32
|
-
// did THIS scenario's semantic block (description+expected) move between anchor and HEAD ([[scenariofresh]])
|
|
33
|
-
scenarioDiffers(anchorSha: string, evalPath: string, scenario: string): boolean
|
|
34
|
-
// codeDrift's display detail: commits in anchor..HEAD touching path (floored at 1 — the content differs)
|
|
35
|
-
behind(anchorSha: string, path: string): number
|
|
36
|
-
prime?(anchorSha: string, paths: string[], evalPath: string): Promise<void>
|
|
37
|
-
// answer every scenario-block demand `prime` recorded, in ONE batched pair of children. Optional and
|
|
38
|
-
// idempotent: an unflushed demand degrades to the singular sync lookup, never to a wrong block.
|
|
39
|
-
primeBlocks?(): Promise<void>
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// (anchor, HEAD) name two immutable trees, so a settled verdict never invalidates — but "never invalidates"
|
|
43
|
-
// is not "keep forever". A checkout answers freshness questions at ONE head at a time, so a root owns
|
|
44
|
-
// exactly one head's verdicts ([[source-of-truth]]'s current-root rule, the same one the history and drift
|
|
45
|
-
// indices follow). Keying the head into a shared memo instead made every rebuild ADD a generation: three
|
|
46
|
-
// full invalidations left three heads' worth of anchors resident, and the cache grew with rebuild count
|
|
47
|
-
// rather than with corpus size. A head move therefore swaps the root's scope atomically. An in-flight batch
|
|
48
|
-
// keeps the entry object its caller already holds, so that caller still settles, but a detached entry can
|
|
49
|
-
// never be read back through the root's current scope — an old flight cannot backfill the new head.
|
|
50
|
-
// An anchor entry holds only PER-REQUESTED-PATH verdicts (plus the gone bit and the batch bookkeeping),
|
|
51
|
-
// never a whole-repo path set.
|
|
52
|
-
type AnchorVerdicts = {
|
|
53
|
-
verdicts: Map<string, boolean> // requested path → content differs between the two immutable trees
|
|
54
|
-
gone: boolean // the anchor commit object is locally unreadable — content can't testify
|
|
55
|
-
pending: Set<string> // paths awaiting the next batch child
|
|
56
|
-
flight: Promise<void> | null // the single in-flight batch for this anchor
|
|
57
|
-
}
|
|
58
|
-
type RootScope = {
|
|
59
|
-
head: string
|
|
60
|
-
anchors: Map<string, AnchorVerdicts>
|
|
61
|
-
behind: Map<string, number>
|
|
62
|
-
behindFlight: Map<string, Promise<number>> // the single in-flight count per (anchor, path)
|
|
63
|
-
blockDemands: Set<string> // (rev, evalPath) awaiting the one batched scenario-block read
|
|
64
|
-
}
|
|
65
|
-
const rootScopes = new Map<string, RootScope>()
|
|
66
|
-
// Roots come and go (a closed worktree never asks again), so cap how many stay warm — the same bounded-slot
|
|
67
|
-
// guard the index caches use, and the only bound needed once per-root cardinality is the corpus, not history.
|
|
68
|
-
const ROOT_SLOTS = Math.max(4, Number(process.env.SPEXCODE_FRESHNESS_ROOT_SLOTS || 64))
|
|
69
|
-
|
|
70
|
-
function scopeFor(rootKey: string, head: string): RootScope {
|
|
71
|
-
const current = rootScopes.get(rootKey)
|
|
72
|
-
if (current?.head === head) {
|
|
73
|
-
rootScopes.delete(rootKey)
|
|
74
|
-
rootScopes.set(rootKey, current)
|
|
75
|
-
return current
|
|
76
|
-
}
|
|
77
|
-
const scope: RootScope = { head, anchors: new Map(), behind: new Map(), behindFlight: new Map(), blockDemands: new Set() }
|
|
78
|
-
rootScopes.set(rootKey, scope)
|
|
79
|
-
while (rootScopes.size > ROOT_SLOTS) {
|
|
80
|
-
const oldest = rootScopes.keys().next().value
|
|
81
|
-
if (oldest === undefined || oldest === rootKey) break
|
|
82
|
-
rootScopes.delete(oldest)
|
|
83
|
-
}
|
|
84
|
-
return scope
|
|
85
|
-
}
|
|
86
|
-
// a read only ever sees the root's CURRENT head — a probe pinned to a superseded head can't testify
|
|
87
|
-
function currentScope(rootKey: string, head: string): RootScope | undefined {
|
|
88
|
-
const scope = rootScopes.get(rootKey)
|
|
89
|
-
return scope?.head === head ? scope : undefined
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// the cardinality invariant, made observable: how many anchor entries are resident at a root's CURRENT head
|
|
93
|
-
// (every root when none is named). It must track the corpus's anchors, never how many times the board has
|
|
94
|
-
// been rebuilt — one retained generation, not one per rebuild.
|
|
95
|
-
export function freshnessCacheSize(root?: string): number {
|
|
96
|
-
if (root !== undefined) return rootScopes.get(resolve(root))?.anchors.size ?? 0
|
|
97
|
-
let total = 0
|
|
98
|
-
for (const scope of rootScopes.values()) total += scope.anchors.size
|
|
99
|
-
return total
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function touchAnchor(scope: RootScope, sha: string): AnchorVerdicts {
|
|
103
|
-
const hit = scope.anchors.get(sha)
|
|
104
|
-
if (hit) return hit
|
|
105
|
-
const entry: AnchorVerdicts = { verdicts: new Map(), gone: false, pending: new Set(), flight: null }
|
|
106
|
-
scope.anchors.set(sha, entry)
|
|
107
|
-
return entry
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
type HeavyDiffWaiter = {
|
|
111
|
-
signal?: AbortSignal
|
|
112
|
-
resolve: (release: () => void) => void
|
|
113
|
-
reject: (error: Error) => void
|
|
114
|
-
onAbort: () => void
|
|
115
|
-
}
|
|
116
|
-
type HeavyDiffScope = { active: boolean; waiting: HeavyDiffWaiter[] }
|
|
117
|
-
const heavyDiffScopes = new Map<string, HeavyDiffScope>()
|
|
118
|
-
|
|
119
|
-
// One tree comparison at a time is enough for a production repo's pack/index memory. The graph-wide git
|
|
120
|
-
// permits still bound every child; this narrower domain scheduler keeps these immutable-tree
|
|
121
|
-
// comparisons serial per (repo, HEAD), without teaching the git transport what a content fallback is.
|
|
122
|
-
function acquireHeavyDiff(scopeKey: string, signal?: AbortSignal): Promise<() => void> {
|
|
123
|
-
if (signal?.aborted) return Promise.reject(gitAbortError())
|
|
124
|
-
let scope = heavyDiffScopes.get(scopeKey)
|
|
125
|
-
if (!scope) {
|
|
126
|
-
scope = { active: false, waiting: [] }
|
|
127
|
-
heavyDiffScopes.set(scopeKey, scope)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const releaseFor = (): (() => void) => {
|
|
131
|
-
let released = false
|
|
132
|
-
return () => {
|
|
133
|
-
if (released) return
|
|
134
|
-
released = true
|
|
135
|
-
scope!.active = false
|
|
136
|
-
while (scope!.waiting.length) {
|
|
137
|
-
const waiter = scope!.waiting.shift()!
|
|
138
|
-
waiter.signal?.removeEventListener('abort', waiter.onAbort)
|
|
139
|
-
if (waiter.signal?.aborted) {
|
|
140
|
-
waiter.reject(gitAbortError())
|
|
141
|
-
continue
|
|
142
|
-
}
|
|
143
|
-
scope!.active = true
|
|
144
|
-
waiter.resolve(releaseFor())
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
if (heavyDiffScopes.get(scopeKey) === scope) heavyDiffScopes.delete(scopeKey)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (!scope.active) {
|
|
152
|
-
scope.active = true
|
|
153
|
-
return Promise.resolve(releaseFor())
|
|
154
|
-
}
|
|
155
|
-
return new Promise((resolvePermit, reject) => {
|
|
156
|
-
const waiter: HeavyDiffWaiter = {
|
|
157
|
-
signal,
|
|
158
|
-
resolve: resolvePermit,
|
|
159
|
-
reject,
|
|
160
|
-
onAbort: () => {
|
|
161
|
-
const index = scope!.waiting.indexOf(waiter)
|
|
162
|
-
if (index < 0) return
|
|
163
|
-
scope!.waiting.splice(index, 1)
|
|
164
|
-
signal?.removeEventListener('abort', waiter.onAbort)
|
|
165
|
-
reject(gitAbortError())
|
|
166
|
-
if (!scope!.active && scope!.waiting.length === 0 && heavyDiffScopes.get(scopeKey) === scope)
|
|
167
|
-
heavyDiffScopes.delete(scopeKey)
|
|
168
|
-
},
|
|
169
|
-
}
|
|
170
|
-
scope!.waiting.push(waiter)
|
|
171
|
-
signal?.addEventListener('abort', waiter.onAbort, { once: true })
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// @@@ one batch per anchor, only the paths actually asked about - the tree comparison is pathspec-scoped
|
|
176
|
-
// (`:(literal)` so a path holding a glob char, a space or a leading colon is matched verbatim, `-z` so the
|
|
177
|
-
// answer needs no unquoting), and the answer retained is one boolean per REQUESTED path. Concurrent primes
|
|
178
|
-
// on the same anchor therefore union their paths BEFORE the child starts: a caller records what it needs in
|
|
179
|
-
// `pending`, then either starts the batch that drains pending or joins the running one and re-checks after —
|
|
180
|
-
// so a path requested mid-flight rides the NEXT batch instead of racing this one, and a settled path is
|
|
181
|
-
// never asked again. Different anchors stay serial through the same scope permit.
|
|
182
|
-
function startAnchorBatch(root: string, rootKey: string, sha: string, current: string, entry: AnchorVerdicts): Promise<void> {
|
|
183
|
-
const run = async (): Promise<void> => {
|
|
184
|
-
const release = await acquireHeavyDiff(`${rootKey}\x1f${current}`, currentGitBuildAbortSignal())
|
|
185
|
-
// drain AFTER the permit, so every caller that registered while this batch was waiting rides THIS
|
|
186
|
-
// child instead of paying for another one; anything registered after this line is the next batch.
|
|
187
|
-
const batch = [...entry.pending]
|
|
188
|
-
entry.pending.clear()
|
|
189
|
-
try {
|
|
190
|
-
if (!batch.length) return
|
|
191
|
-
const result = await gitTry(['-C', root, 'diff', '--name-only', '-z', '--no-renames', sha, current,
|
|
192
|
-
'--', ...batch.map((path) => `:(literal)${path}`)])
|
|
193
|
-
if (!result.ok && result.failure !== 'exit')
|
|
194
|
-
throw new Error(`git content diff failed (${result.failure ?? 'unknown'}): ${result.stderr.trim() || 'unknown git error'}`)
|
|
195
|
-
if (!result.ok) { entry.gone = true; return } // the anchor commit object is unreadable — content can't testify
|
|
196
|
-
const changed = new Set(result.stdout.split('\0').filter(Boolean))
|
|
197
|
-
for (const path of batch) entry.verdicts.set(path, changed.has(path))
|
|
198
|
-
} finally {
|
|
199
|
-
release()
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
const flight = run().finally(() => { if (entry.flight === flight) entry.flight = null })
|
|
203
|
-
entry.flight = flight
|
|
204
|
-
return flight
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// @@@ join the in-flight count, don't re-fork it - `behind` holds only SETTLED counts, so N concurrent
|
|
208
|
-
// primes naming one (anchor, path) all missed and each forked its own `rev-list --count`. Measured on
|
|
209
|
-
// z-code's 415-node session scope: 1532 children for 806 distinct counts. `sha..current` names two
|
|
210
|
-
// immutable commits, so a joiner can never be handed a different question's answer. A rejected flight is
|
|
211
|
-
// cached nowhere and every joiner sees the same failure, matching the anchor batch above.
|
|
212
|
-
function behindCount(root: string, scope: RootScope, sha: string, current: string, path: string): Promise<number> {
|
|
213
|
-
const key = `${sha}\x1f${path}`
|
|
214
|
-
const settled = scope.behind.get(key)
|
|
215
|
-
if (settled !== undefined) return Promise.resolve(settled)
|
|
216
|
-
const joined = scope.behindFlight.get(key)
|
|
217
|
-
if (joined) return joined
|
|
218
|
-
const run = async (): Promise<number> => {
|
|
219
|
-
const n = Number((await gitA(['-C', root, 'rev-list', '--count', `${sha}..${current}`, '--', path])).trim())
|
|
220
|
-
const count = Number.isFinite(n) && n > 0 ? n : 1
|
|
221
|
-
scope.behind.set(key, count)
|
|
222
|
-
return count
|
|
223
|
-
}
|
|
224
|
-
const flight = run().finally(() => { if (scope.behindFlight.get(key) === flight) scope.behindFlight.delete(key) })
|
|
225
|
-
scope.behindFlight.set(key, flight)
|
|
226
|
-
return flight
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function contentProbeFor(root: string): ContentProbe {
|
|
230
|
-
const rootKey = resolve(root)
|
|
231
|
-
let head: string | undefined
|
|
232
|
-
const headOf = () => (head ??= headSha(root))
|
|
233
|
-
return {
|
|
234
|
-
async prime(sha, paths, evalPath) {
|
|
235
|
-
const current = headOf()
|
|
236
|
-
const wanted = [...new Set([...paths, evalPath])].filter(Boolean)
|
|
237
|
-
// one scope resolve per prime: the entry stays this call's to settle even if the root's head moves
|
|
238
|
-
// under it, but the swap means nothing it writes afterwards can be read back at the new head.
|
|
239
|
-
const scope = scopeFor(rootKey, current)
|
|
240
|
-
const entry = touchAnchor(scope, sha)
|
|
241
|
-
while (!entry.gone) {
|
|
242
|
-
const missing = wanted.filter((path) => !entry.verdicts.has(path))
|
|
243
|
-
if (!missing.length) break
|
|
244
|
-
for (const path of missing) entry.pending.add(path)
|
|
245
|
-
// record first, then join: whoever starts the next batch drains everything pending by now.
|
|
246
|
-
if (entry.flight) await entry.flight
|
|
247
|
-
else await startAnchorBatch(root, rootKey, sha, current, entry)
|
|
248
|
-
}
|
|
249
|
-
if (entry.gone) return
|
|
250
|
-
for (const path of new Set(paths)) {
|
|
251
|
-
if (entry.verdicts.get(path) !== true) continue
|
|
252
|
-
await behindCount(root, scope, sha, current, path)
|
|
253
|
-
}
|
|
254
|
-
// RECORD the scenario-block demand; primeBlocks() below answers the whole read's worth in two
|
|
255
|
-
// children. A caller that never flushes still reads correct blocks — scenarioDiffers falls back to
|
|
256
|
-
// the singular sync lookup — so this is a cost seam, not a correctness one.
|
|
257
|
-
if (entry.verdicts.get(evalPath) === true) {
|
|
258
|
-
scope.blockDemands.add(`${sha}\x1f${evalPath}`)
|
|
259
|
-
scope.blockDemands.add(`${current}\x1f${evalPath}`)
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
async primeBlocks() {
|
|
263
|
-
const scope = currentScope(rootKey, headOf())
|
|
264
|
-
if (!scope?.blockDemands.size) return
|
|
265
|
-
const demands = [...scope.blockDemands].map((k) => {
|
|
266
|
-
const cut = k.indexOf('\x1f')
|
|
267
|
-
return { rev: k.slice(0, cut), path: k.slice(cut + 1) }
|
|
268
|
-
})
|
|
269
|
-
scope.blockDemands.clear()
|
|
270
|
-
await primeScenarioBlocks(root, demands)
|
|
271
|
-
},
|
|
272
|
-
changed(sha, path) {
|
|
273
|
-
// The async prime owns all I/O. An unprimed path is 'can't testify', never a fresh sync diff: a miss
|
|
274
|
-
// stays conservative and no abort/transient failure can turn into an unbounded synchronous fallback.
|
|
275
|
-
const entry = currentScope(rootKey, headOf())?.anchors.get(sha)
|
|
276
|
-
if (!entry || entry.gone) return null
|
|
277
|
-
return entry.verdicts.get(path) ?? null
|
|
278
|
-
},
|
|
279
|
-
canTestify(sha) {
|
|
280
|
-
// a settled verdict — of either polarity — is the proof the anchor's tree was readable
|
|
281
|
-
const entry = currentScope(rootKey, headOf())?.anchors.get(sha)
|
|
282
|
-
return !!entry && !entry.gone && entry.verdicts.size > 0
|
|
283
|
-
},
|
|
284
|
-
scenarioDiffers(sha, evalPath, scenario) {
|
|
285
|
-
const a = scenarioBlocksAt(root, sha, evalPath)
|
|
286
|
-
if (!a) return true // eval.md unreadable at the anchor (renamed/absent) → can't prove → stale
|
|
287
|
-
return a.get(scenario) !== scenarioBlocksAt(root, headOf(), evalPath)?.get(scenario)
|
|
288
|
-
},
|
|
289
|
-
behind(sha, path) {
|
|
290
|
-
return currentScope(rootKey, headOf())?.behind.get(`${sha}\x1f${path}`) ?? 1
|
|
291
|
-
},
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// @@@ the code axis's SPATIAL narrowing ([[code-anchor]]'s path#symbol, reused whole) - a shared FILE is not a
|
|
296
|
-
// shared BEHAVIOUR: harness.ts carries eight adapters, so a one-adapter edit re-flagged every other adapter's
|
|
297
|
-
// reading, and those refresh only through a real dispatched session of that harness. An anchored entry
|
|
298
|
-
// therefore asks the spatial question instead of the file question — did a commit in codeSha..HEAD intersect
|
|
299
|
-
// one of the named units? — through the SAME parse/extract/resolve/hunk∩range engine spec drift runs. Two
|
|
300
|
-
// deliberate differences from that engine: the window carries NO ack filter (an ack vindicates a spec, not a
|
|
301
|
-
// reading), and it never widens — the file question runs FIRST and the anchor can only subtract from it.
|
|
302
|
-
// Fed at the call sites like the ContentProbe, so the decision functions stay pure over their inputs.
|
|
303
|
-
export type AnchorProbe = {
|
|
304
|
-
// did any commit in sinceSha..HEAD touch one of THESE anchored units?
|
|
305
|
-
// null = cannot testify (unprimed, off-history, no usable extractor) — callers stay conservatively stale.
|
|
306
|
-
hit(sinceSha: string, path: string, selectors: readonly string[]): boolean | null
|
|
307
|
-
// The demand set is PLURAL because the engine underneath is: every window's Git images and ordinary hunks
|
|
308
|
-
// are immutable, so one call owns them once for the whole read. A caller that primes one reading at a time
|
|
309
|
-
// re-forks that batch per reading, which on this corpus was ~2.5k children for ~800 verdicts.
|
|
310
|
-
prime?(demands: readonly AnchorDemand[]): Promise<void>
|
|
311
|
-
}
|
|
312
|
-
export type AnchorDemand = { sinceSha: string; entries: readonly RelationEntry[] }
|
|
313
|
-
|
|
314
|
-
// a verdict answers ONE selector set, so the set is part of its identity: several scenarios anchoring
|
|
315
|
-
// DIFFERENT units of one shared file is the whole point of narrowing, and keying only by (sha, path) would
|
|
316
|
-
// hand the first one's answer to all the others — silently, and in the fresh direction.
|
|
317
|
-
const anchorKey = (sinceSha: string, path: string, selectors: readonly string[]) =>
|
|
318
|
-
`${sinceSha}\x1f${path}\x1f${[...selectors].sort().join('\x1e')}`
|
|
319
|
-
|
|
320
|
-
// @@@ one parse per CONTENT, not per selector entry - a node's `code:` entries are asked one at a time, and
|
|
321
|
-
// the same working-tree file backs many of them, so the unmemoized read parsed 46 distinct files 922 times
|
|
322
|
-
// per build (40.4 MB through the TypeScript parser) and did it again on every rebuild. `extract` is a pure
|
|
323
|
-
// function of (text, path, extractor), so its result is reusable exactly as far as the CONTENT is unchanged.
|
|
324
|
-
// The key is a content digest and never mtime/size: this gate decides whether a reading may testify, so a
|
|
325
|
-
// stale unit list would let a dead selector read as alive — the precise failure the comment below warns
|
|
326
|
-
// about. Digesting is ~10x cheaper than parsing, so the read stays and only the parse is saved. Bounded like
|
|
327
|
-
// the historical-revision memo it mirrors ([[code-anchor]]), and it caches the extractor's REJECTION too, so
|
|
328
|
-
// an unparseable file does not re-parse once per entry.
|
|
329
|
-
const CURRENT_TREE_MEMO_MAX = 4096
|
|
330
|
-
const currentTreeUnitMemo = new Map<string, { units: Unit[] } | { failed: string }>()
|
|
331
|
-
function currentTreeUnits(root: string, x: Extractor, path: string): Unit[] {
|
|
332
|
-
const source = readFileSync(join(root, path), 'utf8')
|
|
333
|
-
const key = `${x.memoKey(path)}\0${createHash('sha1').update(source).digest('hex')}`
|
|
334
|
-
const hit = currentTreeUnitMemo.get(key)
|
|
335
|
-
if (hit) { if ('failed' in hit) throw new Error(hit.failed); return hit.units }
|
|
336
|
-
let entry: { units: Unit[] } | { failed: string }
|
|
337
|
-
try { entry = { units: x.extract(source, path) } }
|
|
338
|
-
catch (err: any) { entry = { failed: err?.message ?? String(err) } }
|
|
339
|
-
if (currentTreeUnitMemo.size >= CURRENT_TREE_MEMO_MAX) currentTreeUnitMemo.clear()
|
|
340
|
-
currentTreeUnitMemo.set(key, entry)
|
|
341
|
-
if ('failed' in entry) throw new Error(entry.failed)
|
|
342
|
-
return entry.units
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// every selector of one entry resolves to exactly one unit in the CURRENT tree, or the entry cannot testify.
|
|
346
|
-
// This gate is what stops a DEAD selector from reading fresh: the hit engine answers "no commit touched a
|
|
347
|
-
// unit of that name", which for a name that exists nowhere is a vacuous no — true of spec drift, where the
|
|
348
|
-
// dead anchor is a separate blocking error, and dangerously false here, where the same silence would retire
|
|
349
|
-
// a reading's whole code axis. `problem` names the repair for lint; null means the entry is verifiable.
|
|
350
|
-
function entryUnverifiable(root: string, regs: Extractor[], entry: RelationEntry): string | null {
|
|
351
|
-
const x = extractorFor(regs, extOf(entry.path))
|
|
352
|
-
if (!x) return `\`code\` selector \`${entry.path}#${entry.selectors[0]}\` — no designated extractor for that language; drop the #anchor or add a language row`
|
|
353
|
-
const ready = x.ready()
|
|
354
|
-
if (ready !== true) return `\`code\` anchors on ${entry.path} are unverified: ${ready}`
|
|
355
|
-
let units
|
|
356
|
-
try { units = currentTreeUnits(root, x, entry.path) }
|
|
357
|
-
catch (err: any) { return `\`code\` anchors on ${entry.path} are unverified: ${err?.message ?? String(err)}` }
|
|
358
|
-
// the SAME classifier the gate uses ([[code-anchor]]'s resolveSelectors) — only the wording is ours, so a
|
|
359
|
-
// selector can never be verifiable to one reader and dead to the other.
|
|
360
|
-
for (const r of resolveSelectors(units, entry.selectors)) {
|
|
361
|
-
if ('dead' in r) return `\`code\` selector \`${entry.path}#${r.selector}\` names no unit in that file — follow the rename or drop the selector (evals stay stale until then)`
|
|
362
|
-
if ('ambiguous' in r) return `\`code\` selector \`${entry.path}#${r.selector}\` is ambiguous — ${r.ambiguous} units share that name; pin a unique one`
|
|
363
|
-
}
|
|
364
|
-
return null
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// @@@ the verify sweep must reach the MACROTASK queue - resolving every reading's `code:` selectors against
|
|
368
|
-
// the working tree is a doubly-nested SYNCHRONOUS sweep (4,384 demands on this corpus), and nothing in it
|
|
369
|
-
// awaits, so it ran as one uninterruptible stretch: measured, it held the loop for 1,104ms, which is the
|
|
370
|
-
// `/health` p99 this gate is judged by. The bound defended here is a LIVENESS signal, not a latency taste —
|
|
371
|
-
// a probe that cannot answer is indistinguishable from a dead backend, and the CLI allows it 600ms while the
|
|
372
|
-
// supervisor allows 1,000ms before it keeps the old child. `setImmediate` is what actually returns to the
|
|
373
|
-
// loop's I/O phase (awaiting a synchronous-bodied async fn only drains microtasks). A time budget rather
|
|
374
|
-
// than every-iteration keeps the common cheap iteration from paying a turn: the sweep's own longest single
|
|
375
|
-
// step is one file parse, so the worst hold stays near budget + that step.
|
|
376
|
-
const VERIFY_YIELD_BUDGET_MS = Number(process.env.SPEXCODE_VERIFY_YIELD_BUDGET_MS || 50)
|
|
377
|
-
const yieldToEventLoop = (): Promise<void> => new Promise<void>((resolve) => { setImmediate(resolve) })
|
|
378
|
-
|
|
379
|
-
export function anchorProbeFor(root: string, idx: DriftIndex): AnchorProbe {
|
|
380
|
-
const regs = extractors(root)
|
|
381
|
-
const verdicts = new Map<string, boolean>()
|
|
382
|
-
return {
|
|
383
|
-
async prime(demands) {
|
|
384
|
-
const keys: string[] = []
|
|
385
|
-
const queries: AnchorHitQuery[] = []
|
|
386
|
-
const queued = new Set<string>()
|
|
387
|
-
let sinceYield = Date.now()
|
|
388
|
-
for (const { sinceSha, entries } of demands) for (const e of entries) {
|
|
389
|
-
if (Date.now() - sinceYield >= VERIFY_YIELD_BUDGET_MS) { await yieldToEventLoop(); sinceYield = Date.now() }
|
|
390
|
-
if (!e.selectors.length) continue
|
|
391
|
-
const key = anchorKey(sinceSha, e.path, e.selectors)
|
|
392
|
-
if (verdicts.has(key) || queued.has(key)) continue
|
|
393
|
-
if (entryUnverifiable(root, regs, e)) continue // no verdict → conservative stale (lint says why)
|
|
394
|
-
const win = eventsSince(idx, sinceSha, e.path)
|
|
395
|
-
if (win === null) continue
|
|
396
|
-
if (!win.length) { verdicts.set(key, false); continue }
|
|
397
|
-
queued.add(key)
|
|
398
|
-
keys.push(key)
|
|
399
|
-
queries.push({ win, symbols: [...e.selectors] })
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (!queries.length) return
|
|
403
|
-
const results = await anchorHitExists(root, queries, regs)
|
|
404
|
-
results.forEach((hit, index) => verdicts.set(keys[index], hit))
|
|
405
|
-
},
|
|
406
|
-
hit(sinceSha, path, selectors) {
|
|
407
|
-
return verdicts.get(anchorKey(sinceSha, path, selectors)) ?? null
|
|
408
|
-
},
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// the LOUD half: a selector is a claim that a named unit EXISTS, held to the same standard as a ghost path.
|
|
413
|
-
// Dead, ambiguous, unparseable, or no designated extractor — each names itself and its repair, and until
|
|
414
|
-
// repaired the probe issues no verdict, so the reading stays stale. Over-warn, never a silent pass.
|
|
415
|
-
export function anchorProblems(root: string, entries: readonly RelationEntry[]): string[] {
|
|
416
|
-
const regs = extractors(root)
|
|
417
|
-
const out: string[] = []
|
|
418
|
-
for (const e of entries) {
|
|
419
|
-
if (!e.selectors.length) continue
|
|
420
|
-
const problem = entryUnverifiable(root, regs, e)
|
|
421
|
-
if (problem) out.push(problem)
|
|
422
|
-
}
|
|
423
|
-
return out
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// the REMARK axis's input ([[remark-teeth]]): the teeth read only the resolvable bit + when it was resolved,
|
|
427
|
-
// not the whole remark — so freshness stays a PURE function, fed the scenario's remark track at the call
|
|
428
|
-
// sites (never reaching into the issue store). One signal per remark on the (node, scenario).
|
|
429
|
-
export type RemarkSignal = { resolved: boolean; resolvedAt?: string }
|
|
430
|
-
|
|
431
|
-
// the teeth (T1): a scenario is remark-stale unless EVERY remark is resolved AND this reading post-dates
|
|
432
|
-
// every resolution. So an UNRESOLVED remark ages it; a RESOLVED remark keeps it stale until a reading taken
|
|
433
|
-
// strictly after the resolve (reading.ts > resolvedAt) exists — you can't out-run a remark by re-measuring
|
|
434
|
-
// before the resolve, nor clear it by passive receipt. A resolved bit with no timestamp stays conservatively
|
|
435
|
-
// stale (defensive: resolveRemark always stamps one).
|
|
436
|
-
export function remarkStale(reading: { ts: string }, remarks: RemarkSignal[]): boolean {
|
|
437
|
-
return remarks.some((r) => !r.resolved || !(r.resolvedAt && reading.ts > r.resolvedAt))
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// true iff some commit touched `path` that is NOT an ancestor of `sinceSha` — i.e. it lies in
|
|
441
|
-
// `sinceSha..HEAD` by true DAG reachability, never a log-position/date compare (which under-reports on
|
|
442
|
-
// branchy history). An off-history `sinceSha` falls back to the content probe when one is fed (see
|
|
443
|
-
// ContentProbe above); without a probe — or when the anchor object is gone — freshness can't be proven
|
|
444
|
-
// from HEAD's history, so it reads stale rather than silently pass.
|
|
445
|
-
export function changedSince(idx: DriftIndex, sinceSha: string, path: string, probe?: ContentProbe): boolean {
|
|
446
|
-
const events = eventsSince(idx, sinceSha, path)
|
|
447
|
-
// null = ancestry cannot testify for this anchor; only then does content get a say ([[root-lru]]'s sibling
|
|
448
|
-
// rule: one meaning of changed-since, each layer's own fallback on top).
|
|
449
|
-
if (events) return events.length > 0
|
|
450
|
-
return probe?.changed(sinceSha, path) ?? true
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// the code axis's DISPLAY detail: which governed files drifted since a reading, and by HOW MANY commits — so
|
|
454
|
-
// a stale eval can say "EvalsFeed.jsx +3" instead of a bare "code moved". Same DAG reachability as
|
|
455
|
-
// changedSince (a commit touching the file that is NOT an ancestor of the reading's sha lies in sinceSha..HEAD);
|
|
456
|
-
// an off-history sinceSha reports through the same content fallback (only files whose content differs, counted
|
|
457
|
-
// by rev-list); with no probe or a gone anchor it counts every touch (conservative, matching changedSince).
|
|
458
|
-
// Reporting only — it never decides freshness (staleAxes does); it explains a decision already made.
|
|
459
|
-
export function codeDrift(idx: DriftIndex, sinceSha: string, codeAxis: ScenarioCodeAxisSource, probe?: ContentProbe): { file: string; behind: number }[] {
|
|
460
|
-
// an entry may be anchored (`path#symbol`); drift is reported per BASE FILE — a raw selector string names
|
|
461
|
-
// no real path, so counting commits against it would silently report nothing.
|
|
462
|
-
const codeFiles = scenarioCodeAxis(undefined, codeAxis).paths
|
|
463
|
-
const out: { file: string; behind: number }[] = []
|
|
464
|
-
for (const f of codeFiles) {
|
|
465
|
-
const since = eventsSince(idx, sinceSha, f)
|
|
466
|
-
const events = pathEvents(idx, f)
|
|
467
|
-
const differs = since ? undefined : probe?.changed(sinceSha, f)
|
|
468
|
-
const behind = since ? new Set(since.map((event) => event.commit)).size
|
|
469
|
-
: differs === true ? probe!.behind(sinceSha, f)
|
|
470
|
-
: differs === false ? 0
|
|
471
|
-
: new Set(events.map((event) => event.commit)).size
|
|
472
|
-
if (behind > 0) out.push({ file: f, behind })
|
|
473
|
-
}
|
|
474
|
-
return out
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// @@@scenario axis decides by stored contract hash - a reading filed since #61 carries `scenarioHash`, the
|
|
478
|
-
// content hash of the semantic text it was measured against (scenarios.ts scenarioHash — normalized
|
|
479
|
-
// description+expected). For such a reading the scenario axis is a PURE TEXT COMPARE: recorded hash vs the
|
|
480
|
-
// CURRENT declaration's hash — no git walk, no chain, no ancestry. That is what makes it converge under
|
|
481
|
-
// fleet-parallel filing+merging: a sibling scenario's edit, a sidecar-only commit, a merge's textual shift,
|
|
482
|
-
// a whitespace re-wrap — none of them move THIS scenario's hash, so none can re-stale its reading; only the
|
|
483
|
-
// contract actually changing (or the scenario disappearing from eval.md — `current` undefined) does. A
|
|
484
|
-
// LEGACY reading without the hash is decided by the git-derived per-scenario rule below, unchanged — the
|
|
485
|
-
// one-shot degradation: exactly ONE track decides each reading (hash if present, else git), never both
|
|
486
|
-
// OR-ed together.
|
|
487
|
-
function scenarioStaleByHash(reading: Reading, current: Scenario | undefined): boolean | undefined {
|
|
488
|
-
if (reading.scenarioHash === undefined) return undefined // legacy → the git rule decides
|
|
489
|
-
return current ? scenarioHash(current) !== reading.scenarioHash : true
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// LEGACY scenario freshness (readings filed before the stored contract hash) — PER-SCENARIO and SEMANTIC,
|
|
493
|
-
// not per-file: a reading stales only when ITS OWN scenario's semantic block (description+expected —
|
|
494
|
-
// [[scenariofresh]]'s blockContent projection) moved in scenarioSha..HEAD — never when a sibling in the
|
|
495
|
-
// same eval.md did, and never on a metadata-only edit (tags/test/code/related). Reads like the code axis's
|
|
496
|
-
// changedSince — the per-scenario change-commits ([[scenariofresh]], rename-followed so a bare git-mv
|
|
497
|
-
// reparent isn't a change) tested for ancestry — and an off-history codeSha takes the same content fallback
|
|
498
|
-
// at the same granularity and the same projection. Known limit (#61, why the hash replaced it for new
|
|
499
|
-
// readings): the change-commit chain is built off a LINEARIZED log walk, so parallel branches editing one
|
|
500
|
-
// eval.md cross-attribute each other's edits and can false-stale a sibling's reading across a merge.
|
|
501
|
-
function scenarioMoved(scIdx: ScenarioIndex, didx: DriftIndex, sinceSha: string, evalPath: string, scenario: string, probe?: ContentProbe): boolean {
|
|
502
|
-
const anc = ancestorsOf(didx, sinceSha)
|
|
503
|
-
if (anc) return scenarioChangeCommits(scIdx, evalPath, scenario).some((h) => !inAncestors(didx, anc, h))
|
|
504
|
-
const differs = probe?.changed(sinceSha, evalPath)
|
|
505
|
-
if (differs == null) return true
|
|
506
|
-
if (!differs) return false // whole file byte-identical → this block too
|
|
507
|
-
return probe!.scenarioDiffers(sinceSha, evalPath, scenario)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// one declared entry's contribution to the code axis. The FILE question runs first and is unchanged; only an
|
|
511
|
-
// ANCHORED entry whose file really moved asks the narrower spatial one, so an anchor can subtract from the
|
|
512
|
-
// file verdict but never add to it. No verdict (unprimed, off-history, unverifiable selector) → conservative.
|
|
513
|
-
function entryMoved(idx: DriftIndex, sinceSha: string, entry: RelationEntry, probe?: ContentProbe, anchors?: AnchorProbe): boolean {
|
|
514
|
-
if (!changedSince(idx, sinceSha, entry.path, probe)) return false
|
|
515
|
-
if (!entry.selectors.length) return true
|
|
516
|
-
return anchors?.hit(sinceSha, entry.path, entry.selectors) ?? true
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
export function staleAxes(
|
|
520
|
-
reading: Reading,
|
|
521
|
-
codeAxis: ScenarioCodeAxisSource,
|
|
522
|
-
evalPath: string,
|
|
523
|
-
didx: DriftIndex,
|
|
524
|
-
scIdx: ScenarioIndex,
|
|
525
|
-
remarks: RemarkSignal[] = [],
|
|
526
|
-
probe?: ContentProbe,
|
|
527
|
-
current?: Scenario, // the scenario's CURRENT declaration (undefined = gone from eval.md) — the hash compare's other side
|
|
528
|
-
anchors?: AnchorProbe,
|
|
529
|
-
): StaleAxis[] {
|
|
530
|
-
const axes: StaleAxis[] = []
|
|
531
|
-
const byHash = scenarioStaleByHash(reading, current)
|
|
532
|
-
if (probe && !commitReachable(didx, reading.codeSha) && !probe.canTestify(reading.codeSha)) {
|
|
533
|
-
// the anchor commit object is GONE — neither git axis can testify; say that, not "content changed".
|
|
534
|
-
// The stored contract hash needs no anchor, so it still decides the scenario axis when present.
|
|
535
|
-
axes.push('anchor')
|
|
536
|
-
if (byHash) axes.push('scenario')
|
|
537
|
-
} else {
|
|
538
|
-
if (scenarioCodeAxis(undefined, codeAxis).entries.some((e) => entryMoved(didx, reading.codeSha, e, probe, anchors))) axes.push('code')
|
|
539
|
-
if (byHash ?? scenarioMoved(scIdx, didx, reading.codeSha, evalPath, reading.scenario, probe)) axes.push('scenario')
|
|
540
|
-
}
|
|
541
|
-
if (remarkStale(reading, remarks)) axes.push('remark')
|
|
542
|
-
return axes
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
export function isStale(
|
|
546
|
-
reading: Reading,
|
|
547
|
-
codeAxis: ScenarioCodeAxisSource,
|
|
548
|
-
evalPath: string,
|
|
549
|
-
didx: DriftIndex,
|
|
550
|
-
scIdx: ScenarioIndex,
|
|
551
|
-
remarks: RemarkSignal[] = [],
|
|
552
|
-
probe?: ContentProbe,
|
|
553
|
-
current?: Scenario,
|
|
554
|
-
anchors?: AnchorProbe,
|
|
555
|
-
): boolean {
|
|
556
|
-
return staleAxes(reading, codeAxis, evalPath, didx, scIdx, remarks, probe, current, anchors).length > 0
|
|
557
|
-
}
|
package/spec-eval/src/humanok.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { relative } from 'node:path'
|
|
2
|
-
import { repoRoot } from '../../spec-cli/src/git.js'
|
|
3
|
-
import { commitTrunkData } from '../../spec-cli/src/localIssues.js'
|
|
4
|
-
import { evalNodes, resolveEvalNode } from './scenarios.js'
|
|
5
|
-
import { readReadings, readSidecar, appendHumanOk, humanOkFor, type HumanOk } from './sidecar.js'
|
|
6
|
-
|
|
7
|
-
export type OkResult =
|
|
8
|
-
| { ok: true; humanOk: HumanOk; already: boolean; landed: 'committed' | 'uncommitted' }
|
|
9
|
-
| { ok: false; error: string }
|
|
10
|
-
|
|
11
|
-
export function fileHumanOk(nodeId: string, scenario: string, by: string): OkResult {
|
|
12
|
-
const root = repoRoot()
|
|
13
|
-
const res = resolveEvalNode(evalNodes(root), nodeId)
|
|
14
|
-
if (!res.ok) return { ok: false, error: res.error }
|
|
15
|
-
const node = res.node
|
|
16
|
-
if (!node.scenarios.some((s) => s.name === scenario) &&
|
|
17
|
-
!readSidecar(node.sidecarPath).readings.some((r) => r.scenario === scenario))
|
|
18
|
-
return { ok: false, error: `'${node.id}' has no scenario '${scenario}'` }
|
|
19
|
-
const forScenario = readReadings(node.sidecarPath).filter((r) => r.scenario === scenario)
|
|
20
|
-
if (!forScenario.length) return { ok: false, error: `'${node.id}' scenario '${scenario}' has no effective eval — nothing to ok` }
|
|
21
|
-
const latest = forScenario[forScenario.length - 1]
|
|
22
|
-
const existing = humanOkFor(readSidecar(node.sidecarPath).oks, scenario, latest.ts)
|
|
23
|
-
if (existing) return { ok: true, humanOk: existing, already: true, landed: 'committed' }
|
|
24
|
-
const row: HumanOk = { kind: 'human-ok', scenario, okTs: latest.ts, okSha: latest.codeSha, by, ts: new Date().toISOString() }
|
|
25
|
-
appendHumanOk(node.sidecarPath, row)
|
|
26
|
-
const landed = commitTrunkData(relative(root, node.sidecarPath), `eval(${node.id}): human-ok '${scenario}' @ ${latest.ts} by ${by}`)
|
|
27
|
-
return { ok: true, humanOk: row, already: false, landed: landed === 'not-primary' ? 'uncommitted' : 'committed' }
|
|
28
|
-
}
|