carthagent 0.0.0-stage → 1.0.7
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/LICENSE +21 -0
- package/README.md +219 -2
- package/bin/ctg.mjs +736 -0
- package/docs/budgets.md +72 -0
- package/docs/compaction.md +34 -0
- package/docs/context-efficiency-plan.md +24 -0
- package/docs/context-efficiency-review.md +27 -0
- package/docs/context-efficiency.md +49 -0
- package/docs/deepseek-jev-benchmark-plan.md +70 -0
- package/docs/deepseek-jev-benchmark-repair-plan.md +20 -0
- package/docs/deepseek-jev-benchmark-restart.md +9 -0
- package/docs/deepseek-jev-benchmark-results.md +77 -0
- package/docs/deepseek-jev-benchmark-running.md +60 -0
- package/docs/deepseek-jev-benchmark-v3-results.md +41 -0
- package/docs/deepseek-jev-benchmark-v4-protocol.md +15 -0
- package/docs/deepseek-jev-benchmark-v4-results.md +119 -0
- package/docs/deepseek-jev-http400-diagnosis.md +45 -0
- package/docs/delivery-review-evidence.md +53 -0
- package/docs/delivery-timeout-repair-plan.md +51 -0
- package/docs/evaluation-readiness.md +31 -0
- package/docs/evaluation.md +70 -0
- package/docs/extending.md +354 -0
- package/docs/glm-pilot-execution.md +9 -0
- package/docs/glm-pilot-plan.md +15 -0
- package/docs/glm-pilot-results.md +71 -0
- package/docs/graph-context-optimization.md +15 -0
- package/docs/jev-repository-pilot-plan.md +25 -0
- package/docs/jev-repository-pilot-protocol.md +39 -0
- package/docs/jev-repository-pilot-restart.md +7 -0
- package/docs/jev-repository-pilot-results.md +79 -0
- package/docs/jev.md +32 -0
- package/docs/model-registry.md +25 -0
- package/docs/native-compatibility.md +117 -0
- package/docs/opencode-archive.md +9 -0
- package/docs/opencode-migration.md +115 -0
- package/docs/opencode-runtime.md +94 -0
- package/docs/opencode-workspace.md +31 -0
- package/docs/owned-runtime-workflow-registration.md +17 -0
- package/docs/plan-advisor-evaluation.md +38 -0
- package/docs/planning-startup-fix-plan.md +23 -0
- package/docs/planning-startup-fix-review.md +24 -0
- package/docs/planning.md +118 -0
- package/docs/production-merge-review.md +23 -0
- package/docs/quality.md +35 -0
- package/docs/repository-benchmark-design.md +111 -0
- package/docs/review-assurance.md +9 -0
- package/docs/review-experiment-results.md +59 -0
- package/docs/tested-planning-roadmap.md +58 -0
- package/docs/tested-planning.md +58 -0
- package/docs/tui-pickers.md +9 -0
- package/docs/webdriver.md +83 -0
- package/docs/workflow-modes.md +26 -0
- package/docs/workflow.d2 +17 -0
- package/docs/workflow.svg +149 -0
- package/docs/workspace-coordination.md +9 -0
- package/evaluate.mjs +249 -0
- package/extensions/codegraph.ts +36 -0
- package/extensions/delivery.ts +819 -0
- package/extensions/experiential.ts +21 -0
- package/extensions/subscriptions.ts +21 -0
- package/guidance/profiles/authenticated-web.json +24 -0
- package/guidance/profiles/data-visualization.json +25 -0
- package/guidance/profiles/database-change.json +24 -0
- package/guidance/profiles/external-api.json +24 -0
- package/guidance/profiles/transactional-data.json +25 -0
- package/guidance/profiles/web-application.json +26 -0
- package/lib/acceptance-review.mjs +4 -0
- package/lib/browser-bin.mjs +160 -0
- package/lib/browser-errors.mjs +54 -0
- package/lib/browser.mjs +219 -0
- package/lib/budget.mjs +37 -0
- package/lib/cdp.mjs +221 -0
- package/lib/cloud/client.mjs +275 -0
- package/lib/cloud/control-plane.mjs +350 -0
- package/lib/cloud/gateway.mjs +380 -0
- package/lib/cloud/http.mjs +135 -0
- package/lib/cloud/ledger.mjs +215 -0
- package/lib/codegraph-query.mjs +60 -0
- package/lib/codegraph.mjs +378 -0
- package/lib/context-archive.mjs +18 -0
- package/lib/delivery-context.mjs +132 -0
- package/lib/delivery-guidance.mjs +9 -0
- package/lib/delivery-review.mjs +94 -0
- package/lib/delivery.mjs +610 -0
- package/lib/engine.mjs +137 -0
- package/lib/guidance.mjs +71 -0
- package/lib/jev-advisor.mjs +47 -0
- package/lib/opencode/archive.mjs +122 -0
- package/lib/opencode/compatibility.mjs +19 -0
- package/lib/opencode/credentials.mjs +205 -0
- package/lib/opencode/evaluation-budget-plugin/index.ts +147 -0
- package/lib/opencode/evaluation.mjs +242 -0
- package/lib/opencode/file-lock.mjs +86 -0
- package/lib/opencode/headless.mjs +200 -0
- package/lib/opencode/review.mjs +213 -0
- package/lib/opencode/runtime.mjs +246 -0
- package/lib/opencode/service.mjs +249 -0
- package/lib/opencode/session-options.mjs +60 -0
- package/lib/opencode/workspace.mjs +133 -0
- package/lib/openrouter-jev.mjs +37 -0
- package/lib/outcome-contract.mjs +21 -0
- package/lib/plan-advisor.mjs +38 -0
- package/lib/planning-access.mjs +7 -0
- package/lib/planning-advice.mjs +28 -0
- package/lib/planning-discovery.mjs +14 -0
- package/lib/planning-guide.mjs +34 -0
- package/lib/planning-view.mjs +57 -0
- package/lib/planning.mjs +155 -0
- package/lib/probe-diagnostics.mjs +13 -0
- package/lib/providers/experiential.mjs +292 -0
- package/lib/providers/model-catalog.json +700 -0
- package/lib/providers/model-catalog.mjs +7 -0
- package/lib/providers/names.mjs +13 -0
- package/lib/providers/subscription-models.mjs +483 -0
- package/lib/reports.mjs +80 -0
- package/lib/review-state.mjs +49 -0
- package/lib/review.mjs +114 -0
- package/lib/scenarios.mjs +99 -0
- package/lib/ship-auth.mjs +44 -0
- package/lib/ship-jev.mjs +59 -0
- package/lib/ship-telemetry.mjs +183 -0
- package/lib/tui/ansi.mjs +255 -0
- package/lib/tui/app.mjs +2445 -0
- package/lib/tui/auth.mjs +249 -0
- package/lib/tui/carthage.mjs +109 -0
- package/lib/tui/clipboard.mjs +122 -0
- package/lib/tui/d2.mjs +431 -0
- package/lib/tui/feed.mjs +467 -0
- package/lib/tui/framing.mjs +80 -0
- package/lib/tui/glyphs.mjs +172 -0
- package/lib/tui/hash-worker.mjs +11 -0
- package/lib/tui/mock-engine.mjs +222 -0
- package/lib/tui/models.mjs +73 -0
- package/lib/tui/picker.mjs +18 -0
- package/lib/tui/theme.mjs +663 -0
- package/lib/tui/title.mjs +7 -0
- package/lib/update-channel.mjs +62 -0
- package/lib/update.mjs +230 -0
- package/lib/webdriver.mjs +82 -0
- package/lib/workflow.mjs +88 -0
- package/lib/workspace.mjs +39 -0
- package/metadata.json +8 -0
- package/package.json +94 -4
- package/plugins/carthagent/index.ts +3 -0
- package/plugins/carthagent/package.json +28 -0
- package/plugins/carthagent/src/archive-rpc.ts +46 -0
- package/plugins/carthagent/src/bootstrap.ts +38 -0
- package/plugins/carthagent/src/index.ts +62 -0
- package/plugins/carthagent/src/pull-request-rpc.ts +54 -0
- package/plugins/carthagent/src/rpc.ts +76 -0
- package/plugins/carthagent/src/server/archive-rpc.ts +327 -0
- package/plugins/carthagent/src/server/budgets.ts +21 -0
- package/plugins/carthagent/src/server/codegraph.ts +46 -0
- package/plugins/carthagent/src/server/delivery.ts +488 -0
- package/plugins/carthagent/src/server/device-auth.ts +91 -0
- package/plugins/carthagent/src/server/native-delivery.ts +26 -0
- package/plugins/carthagent/src/server/orchestration.ts +56 -0
- package/plugins/carthagent/src/server/policy.ts +24 -0
- package/plugins/carthagent/src/server/providers.ts +424 -0
- package/plugins/carthagent/src/server/pull-request-rpc.ts +387 -0
- package/plugins/carthagent/src/server/session-controls.ts +82 -0
- package/plugins/carthagent/src/server/telemetry.ts +92 -0
- package/plugins/carthagent/src/server/workspace-rpc.ts +349 -0
- package/plugins/carthagent/src/session-rpc.ts +31 -0
- package/plugins/carthagent/src/testing.ts +85 -0
- package/plugins/carthagent/src/workspace-rpc.ts +133 -0
- package/plugins/carthagent/test-plugin/index.ts +4 -0
- package/plugins/carthagent/test-plugin/package.json +1 -0
- package/plugins/carthagent/tsconfig.json +19 -0
- package/plugins/carthagent/tui.ts +1 -0
- package/prompts/delivery.md +7 -0
- package/public/index.html +983 -0
- package/scenarios/cli/grade_cli.py +69 -0
- package/scenarios/cli/scenario.json +21 -0
- package/scenarios/game/baseline/minecraft.py +193 -0
- package/scenarios/game/context.md +1 -0
- package/scenarios/game/grade_minecraft_clone.py +603 -0
- package/scenarios/game/requirements.txt +2 -0
- package/scenarios/game/scenario.json +33 -0
- package/server.mjs +341 -0
- package/skills/game-development/SKILL.md +36 -0
- package/skills/game-development/assets/ursina_starter.py +98 -0
- package/skills/game-development/scripts/verify_ursina.py +80 -0
- package/skills/software-delivery/SKILL.md +50 -0
- package/skills/typescript-delivery/SKILL.md +69 -0
- package/tests/acceptance-review.test.mjs +13 -0
- package/tests/auth.test.mjs +234 -0
- package/tests/bounded-summary.test.mjs +68 -0
- package/tests/browser-bin.test.mjs +91 -0
- package/tests/browser.test.mjs +96 -0
- package/tests/budget-engine.test.mjs +110 -0
- package/tests/budget.test.mjs +23 -0
- package/tests/cli.test.mjs +245 -0
- package/tests/clipboard.test.mjs +79 -0
- package/tests/cloud-client.test.mjs +129 -0
- package/tests/cloud-control-plane.test.mjs +144 -0
- package/tests/cloud-gateway.test.mjs +147 -0
- package/tests/cloud-http.test.mjs +102 -0
- package/tests/cloud-ledger.test.mjs +75 -0
- package/tests/codegraph-query.test.mjs +66 -0
- package/tests/codegraph.test.mjs +142 -0
- package/tests/delivery-context.test.mjs +66 -0
- package/tests/delivery-review.test.mjs +139 -0
- package/tests/delivery.test.mjs +343 -0
- package/tests/engine-api.mjs +24 -0
- package/tests/experiential-extension.test.mjs +37 -0
- package/tests/experiential.test.mjs +300 -0
- package/tests/extension.test.mjs +921 -0
- package/tests/fixtures/archive-plugin/index.ts +10 -0
- package/tests/guidance.test.mjs +78 -0
- package/tests/model-registry.test.mjs +68 -0
- package/tests/models.test.mjs +70 -0
- package/tests/openai-idempotency.test.mjs +99 -0
- package/tests/opencode-archive-rpc.test.mjs +82 -0
- package/tests/opencode-delivery.test.mjs +231 -0
- package/tests/opencode-providers.test.mjs +414 -0
- package/tests/opencode-runtime.test.mjs +455 -0
- package/tests/opencode-session-controls.test.mjs +115 -0
- package/tests/opencode-session-options.test.mjs +26 -0
- package/tests/opencode-workspace.test.mjs +339 -0
- package/tests/outcome-contract.test.mjs +17 -0
- package/tests/picker.test.mjs +34 -0
- package/tests/plan-advisor.test.mjs +88 -0
- package/tests/plan-review-loop.test.mjs +53 -0
- package/tests/planning-advice.test.mjs +27 -0
- package/tests/planning-contract.test.mjs +65 -0
- package/tests/planning-startup.test.mjs +38 -0
- package/tests/planning-view.test.mjs +33 -0
- package/tests/planning.test.mjs +223 -0
- package/tests/probe-diagnostics.test.mjs +21 -0
- package/tests/pull-request-rpc.test.mjs +366 -0
- package/tests/review-state.test.mjs +32 -0
- package/tests/review.test.mjs +127 -0
- package/tests/server.test.mjs +252 -0
- package/tests/ship-jev.test.mjs +77 -0
- package/tests/ship-telemetry.test.mjs +333 -0
- package/tests/standard-workflow.test.mjs +28 -0
- package/tests/subscription-models.test.mjs +416 -0
- package/tests/subscriptions-extension.test.mjs +27 -0
- package/tests/terminal-title.test.mjs +13 -0
- package/tests/test_grade_minecraft_clone.py +199 -0
- package/tests/test_starter.py +78 -0
- package/tests/theme.test.mjs +175 -0
- package/tests/tui-copy-paste.test.mjs +54 -0
- package/tests/tui.test.mjs +808 -0
- package/tests/update-channels.test.mjs +129 -0
- package/tests/update.test.mjs +175 -0
- package/tests/webdriver.test.mjs +31 -0
- package/tests/workflow.test.mjs +47 -0
- package/tests/workspace-rpc.test.mjs +236 -0
- package/tests/workspace.test.mjs +24 -0
- package/tools/benchmark/advisor.test.mjs +7 -0
- package/tools/benchmark/advisor.ts +27 -0
- package/tools/benchmark/campaign.mjs +45 -0
- package/tools/benchmark/diagnose-request.mjs +40 -0
- package/tools/benchmark/eval-common.mjs +32 -0
- package/tools/benchmark/eval-ledger.mjs +22 -0
- package/tools/benchmark/eval-shop.mjs +26 -0
- package/tools/benchmark/eval-voxel.mjs +20 -0
- package/tools/benchmark/evaluate.mjs +24 -0
- package/tools/benchmark/evaluators.test.mjs +13 -0
- package/tools/benchmark/fixtures/shop/server.mjs +18 -0
- package/tools/benchmark/fixtures/shop/ui.html +8 -0
- package/tools/benchmark/freeze.mjs +30 -0
- package/tools/benchmark/gateway.mjs +91 -0
- package/tools/benchmark/gateway.test.mjs +38 -0
- package/tools/benchmark/generator.mjs +11 -0
- package/tools/benchmark/generator.test.mjs +26 -0
- package/tools/benchmark/guard.test.mjs +133 -0
- package/tools/benchmark/guard.ts +136 -0
- package/tools/benchmark/native-plugin/index.ts +24 -0
- package/tools/benchmark/planning-startup.test.mjs +47 -0
- package/tools/benchmark/preflight-live.mjs +31 -0
- package/tools/benchmark/preflight.mjs +22 -0
- package/tools/benchmark/replay-diagnostic.mjs +23 -0
- package/tools/benchmark/runner.mjs +141 -0
- package/tools/benchmark/runner.test.mjs +93 -0
- package/tools/benchmark/runtime.mjs +23 -0
- package/tools/benchmark/selftest.mjs +47 -0
- package/tools/benchmark/smoke-jev.mjs +5 -0
- package/tools/benchmark/tasks.mjs +20 -0
- package/tools/benchmark/transport.mjs +65 -0
- package/tools/benchmark/transport.test.mjs +44 -0
- package/tools/benchmark/upstream-error.mjs +18 -0
- package/tools/benchmark/upstream-error.test.mjs +10 -0
- package/tools/browser-check.mjs +109 -0
- package/tools/check-syntax.mjs +27 -0
- package/tools/evaluate-plan-advisor.mjs +20 -0
- package/tools/native-fixture.mjs +194 -0
- package/tools/native-pty-smoke.py +108 -0
- package/tools/native-report-plugin/index.ts +50 -0
- package/tools/native-report-plugin/package.json +7 -0
- package/tools/patch-compaction.mjs +24 -0
- package/tools/patch-model-catalog.mjs +12 -0
- package/tools/patch-print-shutdown.mjs +39 -0
- package/tools/repo-pilot/evaluator.cjs +32 -0
- package/tools/repo-pilot/reference.mjs +43 -0
- package/tools/repo-pilot/runner.mjs +85 -0
- package/tools/repo-pilot/sandbox-entry.mjs +12 -0
- package/tools/repo-pilot/tasks.mjs +20 -0
- package/tools/repo-pilot/type-evaluator.cjs +6 -0
- package/tools/test-native-controls.mjs +84 -0
- package/tools/test-native-delivery-host.mjs +307 -0
- package/tools/test-native-evaluation.mjs +46 -0
- package/tools/test-native-host.mjs +385 -0
- package/tools/test-native-outcomes.mjs +73 -0
- package/tools/test-native-package.mjs +70 -0
- package/tools/test-native-pty.mjs +20 -0
- package/tools/test-native-report.mjs +88 -0
- package/tools/test-native-swe-host.mjs +132 -0
- package/tools/test-native-swe.mjs +122 -0
- package/tools/test-picker-pty.py +45 -0
- package/tools/test-python.mjs +29 -0
- package/tools/test-webdriver.mjs +54 -0
- package/tools/verify-release-version.mjs +37 -0
- package/tools/webdriver-check.mjs +52 -0
- package/vendor/agent/bounded-summary.mjs +88 -0
- package/vendor/agent/chunks/anthropic-messages-VWZZOSJQ.js +6 -0
- package/vendor/agent/chunks/anthropic.js +75 -0
- package/vendor/agent/chunks/azure-openai-responses-BJLAM6CD.js +2 -0
- package/vendor/agent/chunks/bedrock-converse-stream.js +74 -0
- package/vendor/agent/chunks/chord-context.js +96 -0
- package/vendor/agent/chunks/chunk-4L3WN2XY.js +2 -0
- package/vendor/agent/chunks/chunk-6CYCI3EJ.js +12 -0
- package/vendor/agent/chunks/chunk-6R7UI4IY.js +2 -0
- package/vendor/agent/chunks/chunk-7ZC4UXSL.js +2 -0
- package/vendor/agent/chunks/chunk-AXIIZGTV.js +2 -0
- package/vendor/agent/chunks/chunk-CLNPYIDP.js +42 -0
- package/vendor/agent/chunks/chunk-DRQDKNXA.js +2 -0
- package/vendor/agent/chunks/chunk-GMWCTUPB.js +2 -0
- package/vendor/agent/chunks/chunk-IDDQWTHI.js +2 -0
- package/vendor/agent/chunks/chunk-JVUZSMYM.js +1548 -0
- package/vendor/agent/chunks/chunk-L2ZPNLYD.js +2 -0
- package/vendor/agent/chunks/chunk-NUHFSC37.js +25 -0
- package/vendor/agent/chunks/chunk-OUXLLA64.js +11 -0
- package/vendor/agent/chunks/chunk-PGXDEOVV.js +2 -0
- package/vendor/agent/chunks/chunk-PJACZCIR.js +2 -0
- package/vendor/agent/chunks/chunk-PRRNXB7S.js +2 -0
- package/vendor/agent/chunks/chunk-TEPMHNKQ.js +2 -0
- package/vendor/agent/chunks/chunk-TRDNDS6A.js +4 -0
- package/vendor/agent/chunks/chunk-U6ZMQKGD.js +50 -0
- package/vendor/agent/chunks/chunk-UAQELI3K.js +2 -0
- package/vendor/agent/chunks/chunk-USJZMAIR.js +2 -0
- package/vendor/agent/chunks/chunk-XDUWOHPL.js +6 -0
- package/vendor/agent/chunks/chunk-XNGRGP62.js +2 -0
- package/vendor/agent/chunks/github-copilot.js +2 -0
- package/vendor/agent/chunks/google-generative-ai-Q2K7FBDI.js +2 -0
- package/vendor/agent/chunks/google-vertex-7UJBF4S7.js +2 -0
- package/vendor/agent/chunks/https-proxy-agent-2VXB7436.js +13 -0
- package/vendor/agent/chunks/image-resize-worker.js +2 -0
- package/vendor/agent/chunks/kimi-coding.js +2 -0
- package/vendor/agent/chunks/lib-HGQMNYVT.js +9 -0
- package/vendor/agent/chunks/mistral-conversations-4M6BZBX6.js +5 -0
- package/vendor/agent/chunks/multipart-parser-SQ6GDJL7.js +3 -0
- package/vendor/agent/chunks/node-KG362ECQ.js +16 -0
- package/vendor/agent/chunks/node-TWVVAGPO.js +2 -0
- package/vendor/agent/chunks/openai-codex-responses-R6VYZTVY.js +10 -0
- package/vendor/agent/chunks/openai-codex.js +75 -0
- package/vendor/agent/chunks/openai-completions-EKZT2IH2.js +7 -0
- package/vendor/agent/chunks/openai-responses-TFDINO6W.js +2 -0
- package/vendor/agent/chunks/openrouter-images-N4XVVFSD.js +2 -0
- package/vendor/agent/chunks/openrouter.js +75 -0
- package/vendor/agent/chunks/pi-messages-PML4SPVJ.js +8 -0
- package/vendor/agent/chunks/radius.js +75 -0
- package/vendor/agent/chunks/src-UK77SYZE.js +4 -0
- package/vendor/agent/chunks/xai.js +2 -0
- package/vendor/agent/cli.js +3 -0
- package/vendor/agent/dist/modes/interactive/theme/dark.json +91 -0
- package/vendor/agent/dist/modes/interactive/theme/light.json +90 -0
- package/vendor/agent/dist/modes/interactive/theme/theme-schema.json +356 -0
- package/vendor/agent/index.js +2 -0
- package/vendor/agent/modes/interactive/theme/dark.json +91 -0
- package/vendor/agent/modes/interactive/theme/light.json +90 -0
- package/vendor/agent/modes/interactive/theme/theme-schema.json +356 -0
- package/vendor/agent/package.json +14 -0
- package/vendor/agent/rpc-entry.js +3 -0
- package/vendor/agent/theme/dark.json +91 -0
- package/vendor/agent/theme/light.json +90 -0
- package/vendor/agent/theme/theme-schema.json +356 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, writeFileSync, mkdirSync, rmSync, utimesSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { createCodeIndex } from '../lib/codegraph.mjs';
|
|
7
|
+
|
|
8
|
+
const fixture = () => {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), 'codegraph-'));
|
|
10
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
11
|
+
writeFileSync(join(dir, 'src', 'a.ts'), [
|
|
12
|
+
`import { helper } from './b';`,
|
|
13
|
+
`import './side';`,
|
|
14
|
+
`export function main() { helper(); thing.h(); }`,
|
|
15
|
+
`export class K { m() { helper(); } }`,
|
|
16
|
+
`export const arrow = () => helper();`,
|
|
17
|
+
].join('\n'));
|
|
18
|
+
writeFileSync(join(dir, 'src', 'b.ts'), `export function helper() {}\n`);
|
|
19
|
+
writeFileSync(join(dir, 'src', 'side.ts'), `export const sideEffect = 1;\n`);
|
|
20
|
+
writeFileSync(join(dir, 'src', 'c.py'), [
|
|
21
|
+
`import os`,
|
|
22
|
+
`def f():`,
|
|
23
|
+
` helper()`,
|
|
24
|
+
`class K:`,
|
|
25
|
+
` def m(self):`,
|
|
26
|
+
` main()`,
|
|
27
|
+
].join('\n'));
|
|
28
|
+
writeFileSync(join(dir, 'src', 'd.rs'), [
|
|
29
|
+
`fn f() { g(); obj.h(); }`,
|
|
30
|
+
`struct S {}`,
|
|
31
|
+
`impl S { fn m(&self) { main(); } }`,
|
|
32
|
+
].join('\n'));
|
|
33
|
+
return dir;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
test('codegraph indexes definitions across languages', async t => {
|
|
37
|
+
const dir = fixture();
|
|
38
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
39
|
+
const ix = await createCodeIndex(dir);
|
|
40
|
+
const stats = ix.stats();
|
|
41
|
+
assert.equal(stats.files, 5);
|
|
42
|
+
assert.equal(stats.parseErrors, 0);
|
|
43
|
+
|
|
44
|
+
const helper = ix.definition('helper');
|
|
45
|
+
assert.equal(helper.length, 1);
|
|
46
|
+
assert.equal(helper[0].file, 'src/b.ts');
|
|
47
|
+
assert.equal(helper[0].kind, 'function');
|
|
48
|
+
|
|
49
|
+
// Qualified lookup finds methods under their container in every language.
|
|
50
|
+
const methods = ix.definition('K.m');
|
|
51
|
+
assert.equal(methods.length, 2);
|
|
52
|
+
assert.deepEqual(new Set(methods.map(d => d.container)), new Set(['K']));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('codegraph traces callers and callees', async t => {
|
|
56
|
+
const dir = fixture();
|
|
57
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
58
|
+
const ix = await createCodeIndex(dir);
|
|
59
|
+
|
|
60
|
+
const callers = ix.callers('helper').map(c => c.caller?.name).sort();
|
|
61
|
+
assert.deepEqual(callers, ['arrow', 'f', 'm', 'main'].sort());
|
|
62
|
+
// Member calls record the method name.
|
|
63
|
+
assert.ok(ix.callers('h').length >= 2);
|
|
64
|
+
|
|
65
|
+
const callees = ix.callees('main').map(c => c.name).sort();
|
|
66
|
+
assert.deepEqual(callees, ['h', 'helper']);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('codegraph resolves import edges both directions', async t => {
|
|
70
|
+
const dir = fixture();
|
|
71
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
72
|
+
const ix = await createCodeIndex(dir);
|
|
73
|
+
|
|
74
|
+
assert.deepEqual(ix.imports('src/a.ts').sort(), ['./b', './side']);
|
|
75
|
+
const importers = ix.importers('src/b.ts');
|
|
76
|
+
assert.equal(importers.length, 1);
|
|
77
|
+
assert.equal(importers[0].file, 'src/a.ts');
|
|
78
|
+
assert.deepEqual(ix.importers('src/side.ts'), [{ file: 'src/a.ts', spec: './side' }]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('codegraph outlines a file and finds symbols by substring', async t => {
|
|
82
|
+
const dir = fixture();
|
|
83
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
84
|
+
const ix = await createCodeIndex(dir);
|
|
85
|
+
|
|
86
|
+
assert.deepEqual(ix.outline('src/b.ts').map(d => d.name), ['helper']);
|
|
87
|
+
const outline = ix.outline('src/a.ts').map(d => d.name);
|
|
88
|
+
assert.deepEqual(outline, ['main', 'K', 'm', 'arrow']);
|
|
89
|
+
|
|
90
|
+
const matches = ix.symbols('arr');
|
|
91
|
+
assert.equal(matches.length, 1);
|
|
92
|
+
assert.equal(matches[0].displayName, 'arrow');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('codegraph refreshes incrementally on mtime change', async t => {
|
|
96
|
+
const dir = fixture();
|
|
97
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
98
|
+
const ix = await createCodeIndex(dir);
|
|
99
|
+
assert.equal(ix.definition('added').length, 0);
|
|
100
|
+
|
|
101
|
+
writeFileSync(join(dir, 'src', 'b.ts'), `export function helper() {}\nexport function added() { helper(); }\n`);
|
|
102
|
+
const future = new Date(Date.now() + 2000);
|
|
103
|
+
utimesSync(join(dir, 'src', 'b.ts'), future, future);
|
|
104
|
+
await ix.refresh();
|
|
105
|
+
|
|
106
|
+
assert.equal(ix.definition('added').length, 1);
|
|
107
|
+
assert.ok(ix.callers('helper').some(c => c.caller?.name === 'added'));
|
|
108
|
+
|
|
109
|
+
rmSync(join(dir, 'src', 'c.py'));
|
|
110
|
+
await ix.refresh();
|
|
111
|
+
assert.equal(ix.stats().files, 4);
|
|
112
|
+
assert.equal(ix.outline('src/c.py').length, 0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('codegraph callees distinguishes same-named methods in one file', async t => {
|
|
116
|
+
const dir = fixture();
|
|
117
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
118
|
+
writeFileSync(join(dir, 'src', 'two.ts'), [
|
|
119
|
+
`class A { run() { alpha(); } }`,
|
|
120
|
+
`class B { run() { beta(); } }`,
|
|
121
|
+
].join('\n'));
|
|
122
|
+
const ix = await createCodeIndex(dir);
|
|
123
|
+
|
|
124
|
+
assert.deepEqual(ix.callees('A.run').map(c => c.name), ['alpha']);
|
|
125
|
+
assert.deepEqual(ix.callees('B.run').map(c => c.name), ['beta']);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('codegraph drops stale entries when a file grows past maxBytes', async t => {
|
|
129
|
+
const dir = fixture();
|
|
130
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
131
|
+
const ix = await createCodeIndex(dir, { maxBytes: 80 });
|
|
132
|
+
|
|
133
|
+
writeFileSync(join(dir, 'src', 'small.ts'), `export function tiny() { helper(); }\n`);
|
|
134
|
+
await ix.refresh();
|
|
135
|
+
assert.equal(ix.definition('tiny').length, 1);
|
|
136
|
+
|
|
137
|
+
writeFileSync(join(dir, 'src', 'small.ts'), `export function tiny() { helper(); } ${'// pad'.repeat(30)}\n`);
|
|
138
|
+
const future = new Date(Date.now() + 2000);
|
|
139
|
+
utimesSync(join(dir, 'src', 'small.ts'), future, future);
|
|
140
|
+
await ix.refresh();
|
|
141
|
+
assert.equal(ix.definition('tiny').length, 0);
|
|
142
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, readFileSync, writeFileSync, rmSync, symlinkSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { deliveryRecovery, archivalGroups, replaceArchivedGroups, contextMetrics, jsonBytes, designReceipt } from '../lib/delivery-context.mjs';
|
|
7
|
+
import { archiveContext } from '../lib/context-archive.mjs';
|
|
8
|
+
import { approveFixture } from './helpers/tested-design.mjs';
|
|
9
|
+
import { planningStatus } from '../lib/planning.mjs';
|
|
10
|
+
import { fingerprint, revisePlan } from '../lib/delivery.mjs';
|
|
11
|
+
import { recoveryMessages } from '../lib/delivery-context.mjs';
|
|
12
|
+
import { DELIVERY_INVARIANTS, deliveryPhaseGuidance } from '../lib/delivery-guidance.mjs';
|
|
13
|
+
const plan = { goal:'Exact obligations',assumptions:[],artifacts:['.'],outputs:['release.txt'],steps:['Implement'],acceptance:[{requirement:'Keep every cent exactly',checks:['required_money']}],checks:[{id:'required_money',kind:'test',argv:['node','verify.mjs'],timeoutSeconds:5}] };
|
|
14
|
+
function turn(id,name='delivery_status',isError=false){return [{role:'assistant',stopReason:'toolUse',timestamp:1,content:[{type:'thinking',thinking:'x'.repeat(3000),thinkingSignature:'unchanged'},{type:'toolCall',id,name,arguments:{plan:'x'.repeat(5000)}}]},{role:'toolResult',toolCallId:id,toolName:name,isError,content:[{type:'text',text:'x'.repeat(5000)}]}];}
|
|
15
|
+
test('outbound archives preserve users/failures/pairing and leave stored history untouched',t=>{
|
|
16
|
+
const root=mkdtempSync(join(tmpdir(),'context-'));t.after(()=>rmSync(root,{recursive:true,force:true}));
|
|
17
|
+
const messages=[{role:'user',content:'Keep this exact request'},...turn('1'),...turn('2','delivery_check',true),...turn('3','write'),...turn('4'),...turn('5'),...turn('6')];const before=structuredClone(messages);
|
|
18
|
+
const groups=archivalGroups(messages,{allow:true});assert.deepEqual(groups.map(g=>g.start),[1,7]);
|
|
19
|
+
for(const group of groups)group.archive=archiveContext(root,join(root,'.harness'),group.messages);
|
|
20
|
+
const projected=replaceArchivedGroups(messages,groups);assert.deepEqual(messages,before);assert.equal(projected[0].content,messages[0].content);assert.ok(projected.some(m=>m.isError));assert.ok(projected.some(m=>m.role==='assistant'&&m.content.some(c=>c.name==='write')));assert.ok(jsonBytes(projected)<jsonBytes(messages)*.75);
|
|
21
|
+
for(const group of groups)assert.deepEqual(JSON.parse(readFileSync(group.archive.path)),group.messages);
|
|
22
|
+
writeFileSync(groups[0].archive.path,'corrupted');assert.throws(()=>archiveContext(root,join(root,'.harness'),groups[0].messages),/mismatch/);
|
|
23
|
+
const linked=join(root,'linked');symlinkSync(join(root,'.harness'),linked,'dir');assert.throws(()=>archiveContext(root,linked,{test:true}),/symlink/i);
|
|
24
|
+
assert.deepEqual(archivalGroups(messages,{allow:false}),[]);
|
|
25
|
+
const orphan=turn('7');orphan[1].toolCallId='wrong';assert.deepEqual(archivalGroups(orphan,{allow:true,keepRecent:0}),[]);
|
|
26
|
+
const blocked=turn('8');blocked[1].content=[{type:'text',text:JSON.stringify({findings:[{severity:'blocking',description:'must remain'}]})}];assert.deepEqual(archivalGroups(blocked,{allow:true,keepRecent:0}),[]);
|
|
27
|
+
});
|
|
28
|
+
test('recovery preserves exact obligations, failed evidence/blockers; cannot revive stale approval',t=>{
|
|
29
|
+
const cwd=mkdtempSync(join(tmpdir(),'context-state-'));t.after(()=>rmSync(cwd,{recursive:true,force:true}));writeFileSync(join(cwd,'source'),'a');
|
|
30
|
+
const state={runId:'r',revision:1,plan:structuredClone(plan),evidence:{required_money:{passed:false,code:9,executedRevision:1,outputTail:'lost cent'}},status:'implementing',stepStatus:{}};approveFixture(state,cwd);state.planning.openFindings=[{id:'blocker',revision:1,severity:'blocking',description:'Do not forget'}];
|
|
31
|
+
const before=structuredClone(state),summary=deliveryRecovery(state);assert.deepEqual(summary.acceptance,plan.acceptance);assert.deepEqual(summary.checks,plan.checks);assert.deepEqual(summary.outputs,plan.outputs);assert.equal(summary.planning.openFindings[0].id,'blocker');assert.equal(summary.evidence.required_money.passed,false);assert.deepEqual(state,before);
|
|
32
|
+
writeFileSync(join(cwd,'source'),'b');assert.equal(planningStatus(state,fingerprint(cwd,['.'])).locked,true);
|
|
33
|
+
const revised=revisePlan(state,{assumptions:['new fact']},{cwd,reason:'Changed assumption',hash:fingerprint(cwd,['.'])});deliveryRecovery(revised);assert.equal(planningStatus(revised,fingerprint(cwd,['.'])).locked,true);assert.equal(revised.planning.openFindings[0].id,'blocker');
|
|
34
|
+
});
|
|
35
|
+
test('compact review receipt retains findings/limitations and metrics never copy secret payloads',()=>{
|
|
36
|
+
const receipt={runId:'r',walkthroughs:[{trace:'x'.repeat(10000)}],challenges:['long'],findings:[{description:'failure'}],limitations:['model authored']};const compact=designReceipt('review',receipt);assert.deepEqual(compact.findings,receipt.findings);assert.equal(compact.walkthroughCount,1);assert.ok(jsonBytes(compact)<jsonBytes(receipt)/10);
|
|
37
|
+
const payload={tools:[{function:{name:'read'}}],messages:[{role:'system',content:'SECRET'},{role:'assistant',reasoning_details:[{text:'秘密'}],content:'hi'},{role:'tool',content:'SECRET'}]};const m=contextMetrics(payload);assert.equal(Object.values(m.categories).reduce((a,b)=>a+b,0),m.totalBytes);assert.ok(m.categories.reasoning>0);assert.ok(m.categories.envelope>=0);assert.ok(!JSON.stringify(m).includes('SECRET'));
|
|
38
|
+
});
|
|
39
|
+
test('checkpoint is not repeated; compaction/revision restore exact obligations and current failures', () => {
|
|
40
|
+
const state={runId:'r',revision:1,plan:structuredClone(plan),status:'implementing',evidence:{required_money:{passed:false,code:9}},stepStatus:{}};
|
|
41
|
+
const summary=deliveryRecovery(state);
|
|
42
|
+
const first=recoveryMessages([],summary);
|
|
43
|
+
assert.equal(first.filter(m=>m.customType==='delivery-checkpoint').length,1);
|
|
44
|
+
assert.deepEqual(JSON.parse(first[0].content).acceptance,plan.acceptance);
|
|
45
|
+
const second=recoveryMessages(first,summary);
|
|
46
|
+
assert.equal(second.length,2); assert.equal(second[0],first[0]);
|
|
47
|
+
assert.equal(JSON.parse(second[1].content).evidence.required_money.passed,false);
|
|
48
|
+
const compacted=recoveryMessages([{role:'user',content:'summary without checkpoint'}],summary);
|
|
49
|
+
assert.deepEqual(JSON.parse(compacted[1].content).checks,plan.checks);
|
|
50
|
+
const changed=recoveryMessages(second,{...summary,revision:2,acceptance:[...summary.acceptance,{requirement:'New',checks:['required_money']}]});
|
|
51
|
+
assert.equal(changed.filter(m=>m.customType==='delivery-checkpoint').length,1);
|
|
52
|
+
assert.equal(JSON.parse(changed[0].content).acceptance.length,2);
|
|
53
|
+
assert.ok(DELIVERY_INVARIANTS.length<2000); assert.match(deliveryPhaseGuidance(null),/code_nav/);
|
|
54
|
+
assert.match(deliveryPhaseGuidance(state),/Design:/);
|
|
55
|
+
assert.match(deliveryPhaseGuidance({...state,planning:{approval:{}}}),/snapshot|delivery_review/);
|
|
56
|
+
});
|
|
57
|
+
test('outbound-only checkpoints retain their position until their history anchor disappears', () => {
|
|
58
|
+
const summary=deliveryRecovery({runId:'r',revision:1,plan,status:'implementing',evidence:{}});
|
|
59
|
+
const cache={}; const anchor={role:'toolResult',toolCallId:'plan',content:'created'};
|
|
60
|
+
const first=recoveryMessages([anchor],summary,cache);
|
|
61
|
+
const checkpoint=first[1];
|
|
62
|
+
const second=recoveryMessages([anchor,{role:'assistant',content:'next'}],summary,cache);
|
|
63
|
+
assert.equal(second[1],checkpoint);
|
|
64
|
+
const third=recoveryMessages([{role:'user',content:'compacted'}],summary,cache);
|
|
65
|
+
assert.notEqual(third[1],checkpoint); assert.deepEqual(JSON.parse(third[1].content).acceptance,plan.acceptance);
|
|
66
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, writeFileSync, rmSync, readFileSync, symlinkSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join, dirname, basename } from 'node:path';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { captureDeliveryReview, recordDeliveryReview } from '../lib/delivery-review.mjs';
|
|
8
|
+
import { evidenceIdentity, fingerprint, completionIssues, revisePlan } from '../lib/delivery.mjs';
|
|
9
|
+
|
|
10
|
+
function fixture(t) {
|
|
11
|
+
const cwd = mkdtempSync(join(tmpdir(), 'delivery review café '));
|
|
12
|
+
t.after(() => rmSync(cwd, { recursive: true, force: true }));
|
|
13
|
+
const git = args => execFileSync('git', args, { cwd, stdio: 'pipe' });
|
|
14
|
+
writeFileSync(join(cwd, '.gitignore'), '.harness/\n');
|
|
15
|
+
writeFileSync(join(cwd, 'app.mjs'), 'export const amount = 1;\n');
|
|
16
|
+
git(['init', '-q']); git(['add', '.']);
|
|
17
|
+
git(['-c', 'user.name=Test', '-c', 'user.email=test@localhost', 'commit', '-qm', 'Baseline']);
|
|
18
|
+
writeFileSync(join(cwd, 'app.mjs'), 'export const amount = 2;\n');
|
|
19
|
+
writeFileSync(join(cwd, 'new.mjs'), 'export const result = 2;\n');
|
|
20
|
+
const check = { id: 'unit', kind: 'test', argv: ['node', '--test'], timeoutSeconds: 10 };
|
|
21
|
+
const state = { runId: 'a', revision: 1, plan: { goal: 'Repair money', artifacts: ['.'], steps: [{ id: 'fix', title: 'Fix' }], acceptance: [{ requirement: 'Correct amount', checks: ['unit'] }], checks: [check] }, evidence: {}, stepStatus: { step0: 'done' } };
|
|
22
|
+
state.evidence.unit = { ...evidenceIdentity(state, check), passed: true, fingerprint: fingerprint(cwd, ['.']) };
|
|
23
|
+
const params = captureId => ({ captureId, coverage: [{ requirement: 'Correct amount', assertions: 'app amount equals 2' }], probes: ['node assert amount: 2, passed'], findings: [], limitations: ['Fixture assertion review'] });
|
|
24
|
+
return { cwd, state, params, dir: join(cwd, '.harness', 'review') };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test('review captures real diff/new files and binds complete assertions to final snapshot', async t => {
|
|
28
|
+
const { cwd, state, params, dir } = fixture(t);
|
|
29
|
+
const capture = await captureDeliveryReview(state, cwd, dir);
|
|
30
|
+
assert.match(capture.diff, /-export const amount = 1/);
|
|
31
|
+
assert.match(capture.diff, /\+export const amount = 2/);
|
|
32
|
+
assert.match(capture.diff, /new.mjs/);
|
|
33
|
+
assert.equal(readFileSync(capture.path, 'utf8'), capture.diff);
|
|
34
|
+
assert.throws(() => recordDeliveryReview(state, cwd, { ...params(capture.id), coverage: [] }), /every exact/);
|
|
35
|
+
assert.throws(() => recordDeliveryReview(state, cwd, { ...params(capture.id), probes: [] }), /boundary/);
|
|
36
|
+
assert.match(capture.diff, /export const result = 2/);
|
|
37
|
+
recordDeliveryReview(state, cwd, params(capture.id));
|
|
38
|
+
assert.deepEqual(state.reviewEvidence.evidence, state.evidence);
|
|
39
|
+
assert.ok(Object.values(completionIssues(state, cwd, fingerprint(cwd, ['.']))).every(a => !a.length));
|
|
40
|
+
const revised = revisePlan(state, { assumptions: ['Additional investigation'] }, { cwd, hash: fingerprint(cwd, ['.']), reason: 'Refine' });
|
|
41
|
+
assert.equal(revised.reviewEvidence, undefined);
|
|
42
|
+
assert.ok(revised.revisions[0].reviewEvidence);
|
|
43
|
+
writeFileSync(join(cwd, 'new.mjs'), 'changed');
|
|
44
|
+
assert.throws(() => recordDeliveryReview(state, cwd, params(capture.id)), /stale/);
|
|
45
|
+
assert.equal(completionIssues(state, cwd, fingerprint(cwd, ['.'])).review.length, 1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('findings survive empty submissions, recapture, restart and plan revisions until explicitly resolved', async t => {
|
|
49
|
+
const { cwd, state, params, dir } = fixture(t);
|
|
50
|
+
let capture = await captureDeliveryReview(state, cwd, dir);
|
|
51
|
+
recordDeliveryReview(state, cwd, params(capture.id));
|
|
52
|
+
const rejected = recordDeliveryReview(state, cwd, { ...params(capture.id), findings: ['Movement integration untested'] });
|
|
53
|
+
assert.equal(rejected.recorded, false);
|
|
54
|
+
assert.equal(state.reviewEvidence, undefined);
|
|
55
|
+
assert.match(completionIssues(state, cwd, fingerprint(cwd, ['.'])).review[0], /Movement integration/);
|
|
56
|
+
const restored = JSON.parse(JSON.stringify(state));
|
|
57
|
+
capture = await captureDeliveryReview(restored, cwd, dir);
|
|
58
|
+
assert.equal(recordDeliveryReview(restored, cwd, { ...params(capture.id), limitations: ['Movement integration untested'] }).recorded, false);
|
|
59
|
+
const revised = revisePlan(restored, { assumptions: ['More evidence needed'] }, { cwd, hash: fingerprint(cwd, ['.']), reason: 'Refine' });
|
|
60
|
+
assert.equal(revised.reviewFindings[0].id, rejected.unresolvedFindings[0].id);
|
|
61
|
+
assert.throws(() => recordDeliveryReview(restored, cwd, { ...params(capture.id), resolutions: [{ id: 'invented', change: 'fixed', evidence: 'test passed' }] }), /Unknown/);
|
|
62
|
+
recordDeliveryReview(restored, cwd, { ...params(capture.id), resolutions: [{ id: rejected.unresolvedFindings[0].id, change: 'Added input-to-state integration probe', evidence: 'W at five headings matches camera-forward displacement' }] });
|
|
63
|
+
assert.ok(restored.reviewEvidence.findingHistory[0].resolution);
|
|
64
|
+
const retry = { ...params(capture.id), findings: ['Movement integration untested'], resolutions: [{ id: rejected.unresolvedFindings[0].id, change: 'Added input-to-state integration probe', evidence: 'W at five headings matches camera-forward displacement' }] };
|
|
65
|
+
recordDeliveryReview(restored, cwd, retry);
|
|
66
|
+
assert.equal(restored.reviewFindings.length, 1);
|
|
67
|
+
recordDeliveryReview(restored, cwd, retry);
|
|
68
|
+
assert.equal(restored.reviewFindings.length, 1);
|
|
69
|
+
assert.deepEqual(completionIssues(restored, cwd, fingerprint(cwd, ['.'])).review, []);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('explicit unverified coverage blocks acceptance even with passing checks', async t => {
|
|
73
|
+
const { cwd, state, params, dir } = fixture(t);
|
|
74
|
+
const capture = await captureDeliveryReview(state, cwd, dir);
|
|
75
|
+
const p = params(capture.id); p.coverage[0].status = 'unverified';
|
|
76
|
+
assert.equal(recordDeliveryReview(state, cwd, p).recorded, false);
|
|
77
|
+
assert.match(state.reviewFindings[0].description, /Acceptance gap/);
|
|
78
|
+
assert.equal(recordDeliveryReview(state, cwd, params(capture.id)).recorded, false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('outcome coverage binds actual linked receipts and retains missing user behavior', async t => {
|
|
82
|
+
const { cwd, state, params, dir } = fixture(t);
|
|
83
|
+
state.plan.outcomes = [{ id:'launch',requirement:'Correct amount',entrypoint:'node app.mjs',observable:'Output is 2',substitutions:[],missedFailure:'Internal function works but launch fails',checks:['unit'] }];
|
|
84
|
+
const capture = await captureDeliveryReview(state,cwd,dir);
|
|
85
|
+
assert.throws(()=>recordDeliveryReview(state,cwd,params(capture.id)),/exactly one outcome/);
|
|
86
|
+
const outcome = {id:'launch',check:'missing',observation:'printed 2',challenge:'bad input rejected',status:'demonstrated'};
|
|
87
|
+
assert.throws(()=>recordDeliveryReview(state,cwd,{...params(capture.id),outcomes:[outcome]}),/linked check receipt/);
|
|
88
|
+
outcome.check='unit';outcome.status='unverified';outcome.observation='Only alternate test mode exercised';
|
|
89
|
+
assert.equal(recordDeliveryReview(state,cwd,{...params(capture.id),outcomes:[outcome]}).recorded,false);
|
|
90
|
+
outcome.status='demonstrated';outcome.observation='Normal launch printed 2';
|
|
91
|
+
const result=recordDeliveryReview(state,cwd,{...params(capture.id),outcomes:[outcome],resolutions:[{id:state.reviewFindings[0].id,change:'Executed normal launch',evidence:'Launch printed 2 and rejected malformed input'}]});
|
|
92
|
+
assert.deepEqual(result.outcomes[0].receipt,state.evidence.unit);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('capture includes new unicode source contents but excludes unignored dependency and generated trees', async t => {
|
|
96
|
+
const { cwd, state, dir } = fixture(t);
|
|
97
|
+
for (const name of ['.venv', 'node_modules', 'artifacts', '.harness']) {
|
|
98
|
+
mkdirSync(join(cwd, name), { recursive: true });
|
|
99
|
+
writeFileSync(join(cwd, name, 'noise.txt'), 'DO_NOT_CAPTURE_DEPENDENCY_CONTENT');
|
|
100
|
+
}
|
|
101
|
+
writeFileSync(join(cwd, 'new café.txt'), 'critical integration assertion');
|
|
102
|
+
const capture = await captureDeliveryReview(state, cwd, dir);
|
|
103
|
+
assert.match(capture.diff, /critical integration assertion/);
|
|
104
|
+
assert.doesNotMatch(capture.diff, /DO_NOT_CAPTURE_DEPENDENCY_CONTENT|noise.txt/);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('review rejects modified receipts, stale coverage, and cancellation', async t => {
|
|
108
|
+
const { cwd, state, params, dir } = fixture(t);
|
|
109
|
+
const capture = await captureDeliveryReview(state, cwd, dir);
|
|
110
|
+
state.requirementRevisions = { 'Correct amount': 2 };
|
|
111
|
+
assert.throws(() => recordDeliveryReview(state, cwd, params(capture.id)), /fresh checks/);
|
|
112
|
+
delete state.requirementRevisions;
|
|
113
|
+
writeFileSync(capture.path, 'forged diff');
|
|
114
|
+
assert.throws(() => recordDeliveryReview(state, cwd, params(capture.id)), /modified/);
|
|
115
|
+
await assert.rejects(captureDeliveryReview(state, cwd, dir, AbortSignal.abort()), /cancelled/);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('review accepts filesystem aliases of the Git root but rejects ancestor repositories', async t => {
|
|
119
|
+
const { cwd, state } = fixture(t);
|
|
120
|
+
const parent = mkdtempSync(join(tmpdir(), 'review-alias-'));
|
|
121
|
+
t.after(() => rmSync(parent, { recursive: true, force: true }));
|
|
122
|
+
const link = join(parent, 'alias');
|
|
123
|
+
symlinkSync(dirname(cwd), link, process.platform === 'win32' ? 'junction' : 'dir');
|
|
124
|
+
const alias = join(link, basename(cwd));
|
|
125
|
+
const capture = await captureDeliveryReview(state, alias, join(alias, '.harness', 'review'));
|
|
126
|
+
assert.match(capture.diff, /diff --git/);
|
|
127
|
+
const child = join(cwd, 'nested');
|
|
128
|
+
mkdirSync(child);
|
|
129
|
+
await assert.rejects(captureDeliveryReview(state, child, join(child, '.harness')), /workspace Git root/);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('large diffs retain full receipt instead of silently using output tail', async t => {
|
|
133
|
+
const { cwd, state, dir } = fixture(t);
|
|
134
|
+
writeFileSync(join(cwd, 'app.mjs'), 'export const amount = 2;\n' + '// meaningful review line\n'.repeat(900));
|
|
135
|
+
const capture = await captureDeliveryReview(state, cwd, dir);
|
|
136
|
+
assert.equal(capture.truncated, true);
|
|
137
|
+
assert.ok(readFileSync(capture.path, 'utf8').length > 12000);
|
|
138
|
+
assert.match(capture.diff, /diff --git/);
|
|
139
|
+
});
|