diff --git a/ci/source-shape-test-budget.json b/ci/source-shape-test-budget.json index e9041fb0191..0a91a860de7 100644 --- a/ci/source-shape-test-budget.json +++ b/ci/source-shape-test-budget.json @@ -226,6 +226,11 @@ "test": "should return actionable unknown target error", "category": "compatibility" }, + { + "file": "test/e2e/support/e2e-semantic-phase-check.test.ts", + "test": "builds the policy boundary before semantic collection and CLI compilation", + "category": "compatibility" + }, { "file": "test/e2e/support/e2e-workflow.test.ts", "test": "derives test selectors from code and workflow jobs from workflow metadata", diff --git a/package.json b/package.json index fa4c37d2cad..8ce34c91511 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "test:watch": "vitest watch --project cli --project plugin --project e2e-support", "test:shuffle": "vitest run --project cli --project plugin --project e2e-support --sequence.shuffle.tests --coverage=false", "test:diagnose:leaks": "vitest run --project cli --project plugin --project e2e-support --detectAsyncLeaks --coverage=false --reporter=default --reporter=hanging-process", - "test:e2e-phases:check": "node --experimental-strip-types --no-warnings tools/e2e/check-semantic-phases.mts", + "test:e2e-phases:check": "npm run build:policy-boundary && node --experimental-strip-types --no-warnings tools/e2e/check-semantic-phases.mts", "test:runtime-audit": "tsx scripts/audit-test-runtime.mts", "test:integration": "npm run clean:cli && npm run build:cli && vitest run --project integration --project installer-integration", "test:package": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project package-contract", @@ -52,7 +52,8 @@ "format:ts": "cd nemoclaw && npm run lint:fix && npm run format", "check:installer-hash": "bash scripts/check-installer-hash.sh", "typecheck": "tsc -p jsconfig.json", - "build:cli": "tsc -p nemoclaw/tsconfig.shared.json && tsc -p tsconfig.src.json && node dist/lib/cli/generate-oclif-metadata-manifest.js && if find nemoclaw-blueprint/scripts -name '*.ts' -print -quit | grep -q .; then tsc -p nemoclaw-blueprint/tsconfig.json; fi", + "build:policy-boundary": "tsc -p nemoclaw/tsconfig.shared.json", + "build:cli": "npm run build:policy-boundary && tsc -p tsconfig.src.json && node dist/lib/cli/generate-oclif-metadata-manifest.js && if find nemoclaw-blueprint/scripts -name '*.ts' -print -quit | grep -q .; then tsc -p nemoclaw-blueprint/tsconfig.json; fi", "clean:cli": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"", "typecheck:cli": "tsc -p tsconfig.cli.json", "validate:configs": "tsx scripts/validate-configs.mts", diff --git a/test/e2e/support/e2e-semantic-phase-check.test.ts b/test/e2e/support/e2e-semantic-phase-check.test.ts index 18ce989de04..e73d4e75913 100644 --- a/test/e2e/support/e2e-semantic-phase-check.test.ts +++ b/test/e2e/support/e2e-semantic-phase-check.test.ts @@ -31,6 +31,23 @@ const TEST_PROGRESS_SOURCE = path.join(REPO_ROOT, "test/e2e/fixtures/progress.ts const FAKE_OPENAI_SOURCE = path.join(REPO_ROOT, "test/e2e/fixtures/fake-openai-compatible.ts"); describe("semantic E2E phase checker", () => { + // source-shape-contract: compatibility -- Generated policy output must precede semantic collection and remain shared with the canonical CLI build + test("builds the policy boundary before semantic collection and CLI compilation", () => { + const scripts = ( + JSON.parse(fs.readFileSync(path.join(REPO_ROOT, "package.json"), "utf8")) as { + scripts: Record; + } + ).scripts; + + expect(scripts["build:policy-boundary"]).toBe("tsc -p nemoclaw/tsconfig.shared.json"); + expect(scripts["test:e2e-phases:check"]).toMatch( + /^npm run build:policy-boundary && node .*tools\/e2e\/check-semantic-phases\.mts$/u, + ); + expect(scripts["build:cli"]).toMatch( + /^npm run build:policy-boundary && tsc -p tsconfig\.src\.json &&/u, + ); + }); + test("derives coverage from registry, free-standing, shared, and forwarding workflow paths", () => { const modules = semanticPhaseCoverageModules( {