proof-harness 0.1.0-next.10 → 0.1.0-next.11
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/COMPATIBILITY.md +37 -2
- package/PROOF_SDK_CONTRACT.md +12 -0
- package/README.md +45 -10
- package/cli/engines/proof_coverage.mjs +19 -5
- package/cli/engines/scan_proof_capabilities.mjs +49 -1
- package/dist/node.d.ts +5 -0
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/proof-consumer-fixtures/capabilities/service-rpc.json +4 -0
package/COMPATIBILITY.md
CHANGED
|
@@ -27,9 +27,44 @@ versions rather than guessing how to interpret them.
|
|
|
27
27
|
|
|
28
28
|
## Prerelease migration notes
|
|
29
29
|
|
|
30
|
-
### 0.1.0-next.
|
|
30
|
+
### 0.1.0-next.11 (release candidate) — conservative service RPC assessment
|
|
31
|
+
|
|
32
|
+
Prepared for release. After publication, pin exactly `proof-harness@0.1.0-next.11`.
|
|
33
|
+
Regenerate
|
|
34
|
+
capabilities and rerun the complete baseline, coverage, inventory, and mutation
|
|
35
|
+
validation after adoption. Trace, mission, and health versions are unchanged.
|
|
36
|
+
|
|
37
|
+
Capability schema v1 gains additive `serviceRoleRpcCalls` entries with a literal
|
|
38
|
+
`function` and `assessment: "potential_privileged_write"`. Direct const-bound
|
|
39
|
+
service-client `.rpc("name", args)` calls support awaiting/returning the result
|
|
40
|
+
and zero-argument `.single()` / `.maybeSingle()` result chains. Dynamic names,
|
|
41
|
+
escaped builders, unsupported chains, and RLS-client RPCs remain unassessed.
|
|
42
|
+
An RLS client can invoke a SECURITY DEFINER function; its privilege is not inferred
|
|
43
|
+
from the client name. Result shaping does not establish read-only behavior.
|
|
44
|
+
|
|
45
|
+
This is conservative action-boundary assessment, not SQL function-body analysis.
|
|
46
|
+
It does not infer affected tables, effective grants, overloads, nested functions,
|
|
47
|
+
or transactional correctness. Consumer SQL/function privilege proofs and mutation
|
|
48
|
+
catalogs remain necessary; do not interpret this field as complete SQL write coverage.
|
|
49
|
+
|
|
50
|
+
Every service RPC action requires passing primary denial and allowed control
|
|
51
|
+
assertions targeting the exact `module:action`, both with `operation: "invoke"`.
|
|
52
|
+
The requirement applies without workspace inputs and to `_BOT` actions. Existing
|
|
53
|
+
assertion-origin rules remain in force; explicit authorization assertions retain
|
|
54
|
+
the consumer-owned trust boundary. RPC requirements cannot be waived through
|
|
55
|
+
`acceptedActionGaps`. No new runtime helper or caller-to-callee evidence mapping
|
|
56
|
+
is introduced.
|
|
57
|
+
|
|
58
|
+
Evidence for a signed webhook caller does not automatically satisfy a separately
|
|
59
|
+
callable write action. Protect and test the actual action boundary, including
|
|
60
|
+
alternative invocation paths; a staged route, `_BOT` suffix, or a generic 500
|
|
61
|
+
response is not proof of authorization. If that boundary cannot be established,
|
|
62
|
+
coverage remains blocked. Do not hide RPCs from discovery to bypass the gate.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### 0.1.0-next.10 — explicit primary mutation evidence
|
|
31
66
|
|
|
32
|
-
|
|
67
|
+
Published release. Pin exactly
|
|
33
68
|
`proof-harness@0.1.0-next.10` and rerun a fresh baseline before mutation checks.
|
|
34
69
|
Trace, mission, and health protocol versions remain unchanged.
|
|
35
70
|
|
package/PROOF_SDK_CONTRACT.md
CHANGED
|
@@ -1278,3 +1278,15 @@ specs and protected catalogs remain responsible for these assertions' meaning.
|
|
|
1278
1278
|
Control sensitivity requires recognized SDK-helper provenance and cannot count
|
|
1279
1279
|
as primary detection. Skipped, incomplete, and setup-only failures are not
|
|
1280
1280
|
mapped decisive primary evidence. No trace protocol change is introduced.
|
|
1281
|
+
|
|
1282
|
+
## Conservative service RPC assessment (0.1.0-next.11)
|
|
1283
|
+
|
|
1284
|
+
Capability schema v1 adds `serviceRoleRpcCalls: { function: string,
|
|
1285
|
+
assessment: "potential_privileged_write" }[]`. Missing fields in older artifacts
|
|
1286
|
+
are not evidence that a fresh source scan contains no RPCs. Regenerate artifacts
|
|
1287
|
+
when adopting this change. The scanner deliberately over-approximates service
|
|
1288
|
+
RPC privilege without inventing SQL table effects. Coverage requires an exact
|
|
1289
|
+
action-targeted primary denial and allowed control with operation `invoke`,
|
|
1290
|
+
including internal actions and actions without workspace inputs. Caller evidence
|
|
1291
|
+
is not transitively inherited. See COMPATIBILITY.md for supported syntax and
|
|
1292
|
+
remaining unassessable cases.
|
package/README.md
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
# proof-harness
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Verification tools for making specific claims about agent-generated code—and
|
|
4
|
+
checking that deliberately broken behavior makes those claims fail.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
Developed alongside a consumer template, this is an opinionated prerelease,
|
|
7
|
+
not a universal agent acceptance system. It can help teams that already have
|
|
8
|
+
executable requirements and want structured evidence, coverage checks, and
|
|
9
|
+
mutation checks around them.
|
|
10
|
+
|
|
11
|
+
## Try it without the template
|
|
12
|
+
|
|
13
|
+
The [standalone authorization example](https://github.com/TiKa-Nexus/proof-harness/tree/main/examples/authorization)
|
|
14
|
+
uses a tiny JavaScript rule and the public package. It needs Node 24 and pnpm
|
|
15
|
+
11.9.0; no database, credentials, app server, or browser download.
|
|
8
16
|
|
|
9
17
|
```sh
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
git clone https://github.com/TiKa-Nexus/proof-harness.git
|
|
19
|
+
cd proof-harness/examples/authorization
|
|
20
|
+
pnpm install
|
|
21
|
+
pnpm demo
|
|
12
22
|
```
|
|
13
23
|
|
|
24
|
+
You will see a passing proof, an intentionally failed proof after the rule is
|
|
25
|
+
broken, and confirmation that the source was restored. The example validates
|
|
26
|
+
recorded evidence; it is an introduction, not the full CLI mutation workflow.
|
|
27
|
+
|
|
28
|
+
## What it does—and what it establishes
|
|
29
|
+
|
|
30
|
+
The harness records structured proof traces, validates evidence and missions,
|
|
31
|
+
and provides coverage, drift, mutation, and control-sensitivity engines.
|
|
32
|
+
It can establish that a configured claim was observed and that a mapped defect
|
|
33
|
+
was detected. The consuming project supplies the requirements and trust inputs.
|
|
34
|
+
|
|
35
|
+
A passing trace does not establish overall correctness, adequate requirements,
|
|
36
|
+
or security against an agent that can rewrite the acceptance gate. Keep code
|
|
37
|
+
review, conventional tests, and trusted CI alongside it. See the
|
|
38
|
+
[integration guide](https://github.com/TiKa-Nexus/proof-harness/blob/main/docs/INTEGRATING.md)
|
|
39
|
+
for ownership, adoption steps, and current environment constraints, and
|
|
40
|
+
[contributor guide](https://github.com/TiKa-Nexus/proof-harness/blob/main/CONTRIBUTING.md)
|
|
41
|
+
for setup and approachable contributions.
|
|
42
|
+
|
|
14
43
|
## Installation
|
|
15
44
|
|
|
16
45
|
Public prereleases are published under the npm `next` tag. Pin an exact
|
|
@@ -37,10 +66,11 @@ and consumer pinning requirements.
|
|
|
37
66
|
The package has two layers; consumers should know which one they are leaning
|
|
38
67
|
on:
|
|
39
68
|
|
|
40
|
-
- **
|
|
69
|
+
- **Reusable verification mechanics** — trace recording and the assertion
|
|
41
70
|
vocabulary, mission validation, coverage and its ratchet, drift, mutation
|
|
42
71
|
testing, and the CLI. These read your generated schema and config, or take
|
|
43
|
-
what you give them.
|
|
72
|
+
what you give them. CLI discovery supports specific source and SQL patterns;
|
|
73
|
+
configurable paths do not make every framework compatible.
|
|
44
74
|
- **Assumes the conventional Supabase/workspace layout** — `seed.*` (a
|
|
45
75
|
`workspaces` table with a `name` column, a `workspace_members` join table,
|
|
46
76
|
and a `public.users` mirror maintained by a `handle_new_user` trigger) and
|
|
@@ -118,6 +148,11 @@ separate positive-control sensitivity runner. It also distinguishes caller
|
|
|
118
148
|
workspace inputs from generated result IDs. See [next.9 setup and migration](COMPATIBILITY.md)
|
|
119
149
|
before upgrading; consumer invoker and catalog changes are required.
|
|
120
150
|
|
|
121
|
-
Release
|
|
151
|
+
Release `0.1.0-next.10` restores explicit consumer primary assertions
|
|
122
152
|
for mutation detection while retaining strict control provenance. See the
|
|
123
|
-
[next.10 migration notes](COMPATIBILITY.md) before adopting it
|
|
153
|
+
[next.10 migration notes](COMPATIBILITY.md) before adopting it.
|
|
154
|
+
|
|
155
|
+
Release candidate `0.1.0-next.11` discovers direct service RPC calls as potentially
|
|
156
|
+
privileged writes and requires action-boundary evidence even without workspace
|
|
157
|
+
inputs or for internal actions. See [required migration steps](COMPATIBILITY.md)
|
|
158
|
+
before adopting the exact version after publication.
|
|
@@ -367,17 +367,23 @@ function buildReport() {
|
|
|
367
367
|
for (const capability of [...(capabilities.capabilities ?? [])].sort((a, b) =>
|
|
368
368
|
`${a.module}:${a.name}`.localeCompare(`${b.module}:${b.name}`),
|
|
369
369
|
)) {
|
|
370
|
+
const rpcCalls = capability.serviceRoleRpcCalls ?? [];
|
|
370
371
|
if (
|
|
371
|
-
|
|
372
|
+
!rpcCalls.length &&
|
|
373
|
+
((capability.acceptsWorkspaceId !== true &&
|
|
372
374
|
!(capability.serviceRoleAuthOperations ?? []).length) ||
|
|
373
|
-
|
|
375
|
+
capability.internalOnly === true)
|
|
374
376
|
) {
|
|
375
377
|
continue;
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
const workspaceMutations = capability.serviceRoleMutations ?? [];
|
|
379
381
|
const authOperations = capability.serviceRoleAuthOperations ?? [];
|
|
380
|
-
if (
|
|
382
|
+
if (
|
|
383
|
+
workspaceMutations.length === 0 &&
|
|
384
|
+
authOperations.length === 0 &&
|
|
385
|
+
rpcCalls.length === 0
|
|
386
|
+
)
|
|
381
387
|
continue;
|
|
382
388
|
|
|
383
389
|
const action = `${capability.module}:${capability.name}`;
|
|
@@ -385,6 +391,7 @@ function buildReport() {
|
|
|
385
391
|
(assertion) =>
|
|
386
392
|
assertion.passed === true &&
|
|
387
393
|
assertion.target === action &&
|
|
394
|
+
(!rpcCalls.length || assertion.operation === "invoke") &&
|
|
388
395
|
normalizedRole(assertion) === "primary" &&
|
|
389
396
|
denialKinds.has(assertion.kind) &&
|
|
390
397
|
verifiedOrigin(assertion, { isTable: false }),
|
|
@@ -393,13 +400,16 @@ function buildReport() {
|
|
|
393
400
|
(assertion) =>
|
|
394
401
|
assertion.passed === true &&
|
|
395
402
|
assertion.target === action &&
|
|
403
|
+
(!rpcCalls.length || assertion.operation === "invoke") &&
|
|
396
404
|
normalizedRole(assertion) === "control" &&
|
|
397
405
|
controlKinds.has(assertion.kind),
|
|
398
406
|
);
|
|
399
407
|
const denialProven = denialEvidence.length > 0;
|
|
400
408
|
const controlProven = controlEvidence.length > 0;
|
|
401
409
|
const proven = denialProven && controlProven;
|
|
402
|
-
const acceptance =
|
|
410
|
+
const acceptance = rpcCalls.length
|
|
411
|
+
? undefined
|
|
412
|
+
: acceptedActionGaps.get(action);
|
|
403
413
|
if (!proven && acceptance) usedActionAcceptances.add(action);
|
|
404
414
|
|
|
405
415
|
// These targets are now resolved by a derived action requirement.
|
|
@@ -410,6 +420,7 @@ function buildReport() {
|
|
|
410
420
|
tables: [...new Set(workspaceMutations.map((m) => m.table))].sort(),
|
|
411
421
|
mutations: workspaceMutations,
|
|
412
422
|
authOperations,
|
|
423
|
+
rpcCalls,
|
|
413
424
|
denial: denialProven ? "proven" : "gap",
|
|
414
425
|
control: controlProven ? "proven" : "gap",
|
|
415
426
|
status: proven ? "proven" : acceptance ? "accepted_gap" : "gap",
|
|
@@ -599,7 +610,7 @@ function printReport(report) {
|
|
|
599
610
|
);
|
|
600
611
|
heading(
|
|
601
612
|
"SERVICE-ROLE ACTION BOUNDARIES",
|
|
602
|
-
"Derived from workspace-targeted service-role writes
|
|
613
|
+
"Derived from workspace-targeted service-role writes, privileged Auth operations, or service RPC calls (including internal actions). Each action needs both an unauthorized/cross-tenant refusal and an allowed-path control; withProof metadata is not consulted.",
|
|
603
614
|
);
|
|
604
615
|
console.log("");
|
|
605
616
|
printTable(
|
|
@@ -611,6 +622,9 @@ function printReport(report) {
|
|
|
611
622
|
[
|
|
612
623
|
...r.tables,
|
|
613
624
|
...r.authOperations.map((op) => `auth.admin.${op}`),
|
|
625
|
+
...r.rpcCalls.map(
|
|
626
|
+
(call) => `rpc.${call.function} (potential write)`,
|
|
627
|
+
),
|
|
614
628
|
].join(","),
|
|
615
629
|
},
|
|
616
630
|
{ header: "denial", get: (r) => r.denial },
|
|
@@ -317,6 +317,7 @@ function scanFile(file, constants) {
|
|
|
317
317
|
: undefined;
|
|
318
318
|
}
|
|
319
319
|
const mutations = new Map();
|
|
320
|
+
const rpcCalls = new Set();
|
|
320
321
|
visit(source, (n) => {
|
|
321
322
|
if (ts.isIdentifier(n) && clientKind(n)) {
|
|
322
323
|
const parent = n.parent;
|
|
@@ -324,7 +325,7 @@ function scanFile(file, constants) {
|
|
|
324
325
|
const query =
|
|
325
326
|
ts.isPropertyAccessExpression(parent) &&
|
|
326
327
|
parent.expression === n &&
|
|
327
|
-
parent.name.text
|
|
328
|
+
["from", "rpc"].includes(parent.name.text) &&
|
|
328
329
|
ts.isCallExpression(parent.parent) &&
|
|
329
330
|
parent.parent.expression === parent;
|
|
330
331
|
const adminOperation =
|
|
@@ -360,6 +361,49 @@ function scanFile(file, constants) {
|
|
|
360
361
|
}
|
|
361
362
|
if (!ts.isCallExpression(n) || !ts.isPropertyAccessExpression(n.expression))
|
|
362
363
|
return;
|
|
364
|
+
if (n.expression.name.text === "rpc") {
|
|
365
|
+
const kind = clientKind(n.expression.expression);
|
|
366
|
+
const target = literal(n.arguments[0]);
|
|
367
|
+
if (kind !== "service")
|
|
368
|
+
problems.push(
|
|
369
|
+
"RPC privilege/effects cannot be resolved: only direct service-client RPC assessment is supported",
|
|
370
|
+
);
|
|
371
|
+
if (!target || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(target))
|
|
372
|
+
problems.push("RPC requires a literal unqualified function name");
|
|
373
|
+
let chain = n;
|
|
374
|
+
while (
|
|
375
|
+
ts.isPropertyAccessExpression(chain.parent) &&
|
|
376
|
+
chain.parent.expression === chain
|
|
377
|
+
) {
|
|
378
|
+
const member = chain.parent;
|
|
379
|
+
if (
|
|
380
|
+
!["single", "maybeSingle"].includes(member.name.text) ||
|
|
381
|
+
!ts.isCallExpression(member.parent) ||
|
|
382
|
+
member.parent.expression !== member ||
|
|
383
|
+
member.parent.arguments.length !== 0
|
|
384
|
+
) {
|
|
385
|
+
problems.push("RPC result chain is unsupported");
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
chain = member.parent;
|
|
389
|
+
}
|
|
390
|
+
let parent = chain.parent;
|
|
391
|
+
let awaited = false;
|
|
392
|
+
while (
|
|
393
|
+
ts.isAwaitExpression(parent) ||
|
|
394
|
+
ts.isParenthesizedExpression(parent)
|
|
395
|
+
) {
|
|
396
|
+
if (ts.isAwaitExpression(parent)) awaited = true;
|
|
397
|
+
parent = parent.parent;
|
|
398
|
+
}
|
|
399
|
+
if (
|
|
400
|
+
!awaited &&
|
|
401
|
+
!ts.isReturnStatement(parent) &&
|
|
402
|
+
!(ts.isArrowFunction(parent) && parent.body === chain)
|
|
403
|
+
)
|
|
404
|
+
problems.push("RPC query builder escapes its recognized chain");
|
|
405
|
+
if (kind === "service" && target) rpcCalls.add(target);
|
|
406
|
+
}
|
|
363
407
|
if (
|
|
364
408
|
n.expression.name.text === "from" &&
|
|
365
409
|
ts.isIdentifier(n.expression.expression) &&
|
|
@@ -507,6 +551,10 @@ function scanFile(file, constants) {
|
|
|
507
551
|
`${a.table}:${a.operation}`.localeCompare(`${b.table}:${b.operation}`),
|
|
508
552
|
),
|
|
509
553
|
serviceRoleAuthOperations: [...authOperations].sort(),
|
|
554
|
+
serviceRoleRpcCalls: [...rpcCalls].sort().map((name) => ({
|
|
555
|
+
function: name,
|
|
556
|
+
assessment: "potential_privileged_write",
|
|
557
|
+
})),
|
|
510
558
|
middleware: {
|
|
511
559
|
auth: /\bwithAuth\s*\(/.test(body),
|
|
512
560
|
tenantIsolation: /\bwithTenantIsolation\s*\(/.test(body),
|
package/dist/node.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ interface CapabilitiesArtifact {
|
|
|
26
26
|
}>;
|
|
27
27
|
/** Direct supported privileged Auth API calls, separate from SQL writes. */
|
|
28
28
|
serviceRoleAuthOperations?: readonly "deleteUser"[];
|
|
29
|
+
/** Conservative RPC assessment; does not infer SQL tables, grants or read-only behavior. */
|
|
30
|
+
serviceRoleRpcCalls?: ReadonlyArray<{
|
|
31
|
+
function: string;
|
|
32
|
+
assessment: "potential_privileged_write";
|
|
33
|
+
}>;
|
|
29
34
|
/** Relevant guards observed in the action pipeline. */
|
|
30
35
|
middleware?: {
|
|
31
36
|
auth: boolean;
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/node/source-hash.ts","../src/node/evidence.ts","../src/shared/vocabulary.ts","../src/shared/portable-vocabulary.mjs","../src/shared/trace-types.ts","../src/shared/mission-types.ts","../src/node/validate-mission.ts","../src/node/postgres.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { spawnSync } from \"node:child_process\";\n/** Content identity for a git worktree, including untracked nonignored inputs. */\nexport function sourceHash(\n rootDir = process.cwd(),\n excludedPaths: readonly string[] = [],\n): string | undefined {\n const listed = spawnSync(\n \"git\",\n [\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\"],\n { cwd: rootDir, encoding: \"utf8\" },\n );\n if (listed.status !== 0) return undefined;\n const exclusions = [\n \".proof/traces\",\n \".proof/mutations\",\n \".proof/schema.json\",\n \".proof/capabilities.json\",\n \".proof/drift.json\",\n \".proof/mutation-recovery.json\",\n ...excludedPaths,\n ].map((p) => path.resolve(rootDir, p));\n const digest = crypto.createHash(\"sha256\");\n for (const file of [\n ...new Set(listed.stdout.split(\"\\0\").filter(Boolean)),\n ].sort()) {\n const full = path.join(rootDir, file);\n if (exclusions.some((p) => full === p || full.startsWith(p + path.sep)))\n continue;\n digest.update(file).update(\"\\0\");\n if (!fs.existsSync(full)) {\n digest.update(\"<deleted>\");\n continue;\n }\n const stat = fs.lstatSync(full);\n if (stat.isSymbolicLink()) digest.update(fs.readlinkSync(full));\n else if (stat.isFile())\n digest.update(String(stat.mode & 0o777)).update(fs.readFileSync(full));\n digest.update(\"\\0\");\n }\n return digest.digest(\"hex\");\n}\n","import { sourceHash } from \"./source-hash\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { spawnSync } from \"node:child_process\";\nimport { isProofKind } from \"../shared/vocabulary\";\nimport {\n normalizeAssertionRole,\n normalizeAssertionStatus,\n type TraceArtifact,\n type TraceAssertion,\n} from \"../shared/trace-types\";\n\nexport const ASSERTION_HELPERS = [\n \"assert.tenantIsolation\",\n \"assert.authorization\",\n \"assert.actionSucceeds\",\n \"assert.httpResponse\",\n] as const;\nconst object = (v: unknown): v is Record<string, unknown> =>\n !!v && typeof v === \"object\" && !Array.isArray(v);\nconst text = (v: unknown): v is string =>\n typeof v === \"string\" && v.trim().length > 0;\nconst duration = (v: unknown) =>\n typeof v === \"number\" && Number.isFinite(v) && v >= 0;\nexport function isArtifactId(v: unknown): v is string {\n return (\n typeof v === \"string\" &&\n /^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(v) &&\n v !== \".\" &&\n v !== \"..\"\n );\n}\nexport function assertionPassed(a: TraceAssertion): boolean {\n return (\n a.passed === true &&\n normalizeAssertionStatus(a.status, a.passed) === \"passed\" &&\n normalizeAssertionRole(a.role) !== null\n );\n}\nexport function traceShapeIssues(value: unknown): string[] {\n const issues: string[] = [];\n const bad = (where: string) => issues.push(`trace_shape: invalid ${where}`);\n if (!object(value)) return [\"trace_shape: expected an object\"];\n // Explicit legacy support: v1 and unversioned traces retain the original required fields.\n if (\n value.schemaVersion !== undefined &&\n value.schemaVersion !== 1 &&\n value.schemaVersion !== 2\n )\n bad(\"schemaVersion\");\n if (!isArtifactId(value.proofId)) bad(\"proofId\");\n if (value.missionId !== undefined && !isArtifactId(value.missionId))\n bad(\"missionId\");\n if (!text(value.timestamp) || !Number.isFinite(Date.parse(value.timestamp)))\n bad(\"timestamp\");\n if (!duration(value.durationMs)) bad(\"durationMs\");\n if (typeof value.passed !== \"boolean\") bad(\"passed\");\n if (\n value.mutation !== undefined &&\n (!object(value.mutation) ||\n !isArtifactId(value.mutation.id) ||\n value.mutation.planted !== true)\n )\n bad(\"mutation\");\n for (const key of [\n \"specFile\",\n \"specHash\",\n \"commit\",\n \"sourceHash\",\n \"executionId\",\n ]) {\n if (value[key] !== undefined && !text(value[key])) bad(key);\n }\n if (value.dirty !== undefined && typeof value.dirty !== \"boolean\")\n bad(\"dirty\");\n if (\n value.retry !== undefined &&\n (!Number.isInteger(value.retry) || Number(value.retry) < 0)\n )\n bad(\"retry\");\n if (!Array.isArray(value.steps))\n return [...issues, \"trace_shape: steps must be an array\"];\n for (const [i, step] of value.steps.entries()) {\n if (!object(step)) {\n bad(`steps[${i}]`);\n continue;\n }\n if (\n !isProofKind(step.kind) ||\n !text(step.target) ||\n !text(step.intent) ||\n typeof step.observation !== \"string\" ||\n typeof step.passed !== \"boolean\" ||\n !duration(step.durationMs)\n )\n bad(`steps[${i}] fields`);\n if (value.passed === true && step.passed !== true)\n bad(`steps[${i}] contradicts proof verdict`);\n if (step.assertions === undefined) continue;\n if (!Array.isArray(step.assertions)) {\n bad(`steps[${i}].assertions`);\n continue;\n }\n for (const [j, a] of step.assertions.entries()) {\n const where = `steps[${i}].assertions[${j}]`;\n if (!object(a)) {\n bad(where);\n continue;\n }\n if (\n !isProofKind(a.kind) ||\n !text(a.target) ||\n typeof a.passed !== \"boolean\" ||\n normalizeAssertionRole(a.role) === null\n )\n bad(where);\n if (\n a.operation !== undefined &&\n ![\"select\", \"insert\", \"update\", \"delete\", \"invoke\", \"request\"].includes(\n String(a.operation),\n )\n )\n bad(`${where}.operation`);\n if (\n a.emittedBy !== undefined &&\n !ASSERTION_HELPERS.includes(\n a.emittedBy as (typeof ASSERTION_HELPERS)[number],\n )\n )\n bad(`${where}.emittedBy`);\n if (\n a.status !== undefined &&\n (![\"passed\", \"failed\", \"skipped\", \"incomplete\"].includes(\n String(a.status),\n ) ||\n (a.status === \"passed\") !== a.passed)\n )\n bad(`${where}.status`);\n if (\n step.passed === true &&\n a.passed === false &&\n (a.status === undefined ||\n a.status === \"failed\" ||\n a.status === \"incomplete\")\n )\n bad(`${where} contradicts step verdict`);\n }\n }\n return issues;\n}\nexport function decodeTrace(value: unknown): TraceArtifact {\n const issues = traceShapeIssues(value);\n if (issues.length) throw new Error(`[PROOF_FAIL] ${issues.join(\"; \")}`);\n return value as TraceArtifact;\n}\nexport function decodeTraceBundle(values: readonly unknown[]): TraceArtifact[] {\n const seen = new Set<string>();\n return values.map((value) => {\n const t = decodeTrace(value);\n if (seen.has(t.proofId))\n throw new Error(`[PROOF_FAIL] duplicate_proof_id: ${t.proofId}`);\n seen.add(t.proofId);\n return t;\n });\n}\nexport function fileHash(file: string): string {\n return crypto\n .createHash(\"sha256\")\n .update(fs.readFileSync(file))\n .digest(\"hex\")\n .slice(0, 12);\n}\nexport function readTraceDirectory(\n directory: string,\n {\n rootDir = process.cwd(),\n checkFreshness = true,\n excludedPaths = [] as readonly string[],\n } = {},\n): TraceArtifact[] {\n if (!fs.existsSync(directory)) return [];\n const values: unknown[] = [];\n for (const entry of fs\n .readdirSync(directory, { withFileTypes: true })\n .sort((a, b) => a.name.localeCompare(b.name))) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n let value: unknown;\n try {\n value = JSON.parse(\n fs.readFileSync(path.join(directory, entry.name), \"utf8\"),\n );\n } catch {\n throw new Error(`[PROOF_FAIL] invalid_json: ${entry.name}`);\n }\n if (\n object(value) &&\n value.schemaVersion === 1 &&\n isArtifactId(value.missionId) &&\n typeof value.passed === \"boolean\" &&\n Array.isArray(value.proofs) &&\n Array.isArray(value.traces) &&\n Array.isArray(value.issues) &&\n Array.isArray(value.requirementEvidence) &&\n value.steps === undefined\n )\n continue;\n values.push(value);\n }\n const traces = decodeTraceBundle(values);\n const git = spawnSync(\"git\", [\"rev-parse\", \"HEAD\"], {\n cwd: rootDir,\n encoding: \"utf8\",\n });\n const commit = git.status === 0 ? git.stdout.trim() : undefined;\n const hash = checkFreshness\n ? sourceHash(rootDir, [...excludedPaths, directory])\n : undefined;\n for (const t of traces) {\n if (!checkFreshness || t.mutation) continue;\n if (!text(t.specFile) || !text(t.specHash))\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} lacks spec provenance; rerun proofs`,\n );\n const file = path.resolve(rootDir, t.specFile);\n const relative = path.relative(rootDir, file);\n if (\n relative.startsWith(\"..\") ||\n path.isAbsolute(relative) ||\n !fs.existsSync(file) ||\n fileHash(file) !== t.specHash\n )\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} spec changed or disappeared`,\n );\n if (hash && t.sourceHash !== hash)\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} source contents changed or lack content identity`,\n );\n if (commit && t.commit !== commit)\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} does not describe current commit`,\n );\n }\n return traces;\n}\nexport function baselineAssertions(traces: readonly TraceArtifact[]) {\n return traces\n .filter((t) => t.passed && !t.mutation)\n .flatMap((t) =>\n t.steps\n .filter((s) => s.passed)\n .flatMap((s) =>\n (s.assertions ?? [])\n .filter(assertionPassed)\n .map((a) => ({ ...a, proofId: t.proofId, specFile: t.specFile })),\n ),\n );\n}\n","// Import External Packages\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Closed Vocabularies\n//\n// These are the ONLY allowed values for verb, kind, and rls_classification\n// across the Proof system. Keeping them closed lets the scanner, manifest\n// validator, and trace consumers all speak the same language.\n// ---------------------------------------------------------------------------\n\n/**\n * Verb — the action a capability performs.\n * Used as metadata in `withProof({ verb: ... })`.\n */\nexport const PROOF_VERBS = [\n \"create\",\n \"read\",\n \"update\",\n \"delete\",\n \"invoke\",\n \"transfer\",\n] as const;\n\nexport type ProofVerb = (typeof PROOF_VERBS)[number];\n\n/**\n * Kind — the category of a trace step / assertion.\n * Used in `trace_must_prove` manifest entries and emitted by `assert.*`\n * helpers and `trace.step({ kind })` calls.\n */\nexport const PROOF_KINDS = [\n \"happy_path\",\n \"tenant_isolation\",\n \"authorization\",\n \"validation\",\n \"error_handling\",\n \"idempotency\",\n] as const;\n\nexport type ProofKind = (typeof PROOF_KINDS)[number];\n\n/**\n * RLS classification — how a table is scoped under Row Level Security.\n * Emitted by the migration parser (v1). Defined here so v0.5 helpers and\n * future tooling share the same vocabulary.\n */\nexport const RLS_CLASSIFICATIONS = [\n \"workspace_scoped\",\n \"user_scoped\",\n \"admin_only\",\n \"public_read\",\n \"service_only\",\n \"unclassified\",\n] as const;\n\nexport type RlsClassification = (typeof RLS_CLASSIFICATIONS)[number];\n\n/**\n * The commands a Postgres RLS policy can cover, as the migration parser reports\n * them.\n *\n * `rls_classification` gives a table one label, which is a useful summary and a\n * poor description: real policies are per-command, so a table can be readable\n * only by admins while being writable only by the service role. `ALL` is Postgres'\n * own wildcard and matches every command, which is why a prohibition on any single\n * command has to consider it.\n */\nexport const POLICY_COMMANDS = [\n \"SELECT\",\n \"INSERT\",\n \"UPDATE\",\n \"DELETE\",\n \"ALL\",\n] as const;\n\nexport type PolicyCommand = (typeof POLICY_COMMANDS)[number];\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\nexport function isProofVerb(value: unknown): value is ProofVerb {\n return (\n typeof value === \"string\" &&\n (PROOF_VERBS as readonly string[]).includes(value)\n );\n}\n\nexport function isProofKind(value: unknown): value is ProofKind {\n return (\n typeof value === \"string\" &&\n (PROOF_KINDS as readonly string[]).includes(value)\n );\n}\n\nexport function isRlsClassification(\n value: unknown,\n): value is RlsClassification {\n return (\n typeof value === \"string\" &&\n (RLS_CLASSIFICATIONS as readonly string[]).includes(value)\n );\n}\n\nexport function isPolicyCommand(value: unknown): value is PolicyCommand {\n return (\n typeof value === \"string\" &&\n (POLICY_COMMANDS as readonly string[]).includes(value)\n );\n}\n","// Import External Packages\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Portable proof vocabulary — the single source of truth for closed value\n// lists that BOTH the TypeScript SDK and the dependency-free Node scripts\n// (`proof_verify.mjs`, `proof_drift.mjs`, `proof_coverage.mjs`) must agree on.\n//\n// Why a .mjs file inside a TypeScript package: those scripts run under plain\n// `node` with no compile step and must stay fast-starting, so they cannot\n// import `vocabulary.ts`. Everything here is therefore plain JS, and the\n// TypeScript side derives its union types from these arrays\n// (`(typeof ACTION_CHANGE_KINDS)[number]` in mission-types.ts), so adding or\n// renaming a value in exactly one place updates the validators, the drift\n// script, coverage, and the types together. The JSDoc `@type {const}` casts\n// are what preserve the literal element types for that derivation.\n//\n// Only vocabularies needed by the .mjs scripts live here; TS-only\n// vocabularies stay in `vocabulary.ts`.\n// ---------------------------------------------------------------------------\n\n/**\n * Change kinds an `expectedChanges.actions[]` declaration may allow, and the\n * facets a modified action decomposes into. Like tables, EVERY facet of a\n * change must be declared for the change to be covered — a mission expecting\n * an invariant declaration to change does not thereby authorize an RBAC/auth\n * middleware change on the same action.\n *\n * - `middleware_changed`: a scanner-observed guard (auth / tenantIsolation\n * / rbac) appeared, disappeared, or flipped.\n * - `invariants_changed`: the `withProof({ invariants })` list changed.\n * - `service_role_mutations_changed`: the set of tables the action writes\n * through the service-role client changed.\n * - `metadata_changed`: verb, object, acceptsWorkspaceId, internalOnly, or\n * usesDirectUpdateTag changed.\n */\nexport const ACTION_CHANGE_KINDS = /** @type {const} */ ([\n \"added\",\n \"removed\",\n \"middleware_changed\",\n \"invariants_changed\",\n \"service_role_mutations_changed\",\n \"metadata_changed\",\n]);\n\n/**\n * Change kinds an `expectedChanges.tables[]` declaration may allow, and the\n * facets a modified table decomposes into. Same coverage rule as actions:\n * declaring `columns_added` on a table does not authorize a policy change\n * that rode along on the same table.\n */\nexport const TABLE_CHANGE_KINDS = /** @type {const} */ ([\n \"added\",\n \"removed\",\n \"columns_added\",\n \"columns_removed\",\n \"rls_classification_changed\",\n \"policies_changed\",\n]);\n\n/**\n * The only helpers whose `emittedBy` stamp counts as tenant-isolation\n * evidence, split by target.\n *\n * Identity matters because helpers differ in who chooses the `kind`:\n * `assert.httpResponse` records the caller's kind verbatim, so its stamp on a\n * `tenant_isolation` assertion would prove only that an HTTP probe ran — not\n * that anything checked cross-tenant visibility. Isolation OF A TABLE is\n * exactly what `assert.tenantIsolation` proves, so table targets accept only\n * it; action-ref targets also accept `assert.authorization`, whose action\n * probes record the cross-tenant invariant they actually establish.\n */\nexport const TENANT_ISOLATION_TABLE_HELPERS = /** @type {const} */ ([\n \"assert.tenantIsolation\",\n]);\n\n/** Allowed stamps for tenant-isolation claims against non-table targets (action refs). */\nexport const TENANT_ISOLATION_ACTION_HELPERS = /** @type {const} */ ([\n \"assert.tenantIsolation\",\n \"assert.authorization\",\n]);\n","// Import External Packages\n// Import Local Imports\nimport type { ProofKind } from \"./vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Trace artifact types\n//\n// These are the durable shape written to `.proof/traces/<proofId>.json`.\n//\n// Two separate identifiers on purpose:\n// - `proofId` — per-file id; what _this_ proof verifies (e.g. \"auth-login-admin\",\n// \"workspace-invite-tenant-isolation\"). The filename is keyed on it,\n// so proofs never collide.\n// - `missionId` — optional external ticket reference (e.g. \"M-042\"). A single mission\n// typically has multiple proofs (happy_path + tenant_isolation +\n// authorization), each with its own file. The v1 mission-manifest\n// validator groups by `missionId` and checks that every\n// `trace_must_prove` entry has at least one matching passing step\n// across the group.\n// ---------------------------------------------------------------------------\n\n/**\n * The evidential role an assertion plays.\n *\n * - `primary` — the claim the proof exists to make. For an authorization or\n * tenant-isolation proof that is a refusal (\"the outsider could\n * NOT read the row\"); for a happy_path proof it is a success\n * (\"the user reached their workspace\"). This is what a mission's\n * `trace_must_prove` entry matches by default.\n * - `control` — a companion assertion proving the primary probe was capable of\n * failing at all: \"the operation that SHOULD be permitted still\n * succeeded\", or \"the row the primary probe looked for genuinely\n * exists and is readable by someone.\"\n *\n * The distinction is load-bearing, not cosmetic. A refusal probe alone cannot\n * tell \"RLS correctly hid this row\" apart from \"there was no row / the grant\n * denies everyone / the table name was a typo\" — every one of those yields zero\n * rows and would record a passing assertion. The control is what makes the zero\n * mean something.\n *\n * Because the validator matches `trace_must_prove` on `{ kind, target,\n * passed: true }`, controls MUST be labelled: otherwise a control (which by\n * design passes) could satisfy a requirement all on its own and the primary\n * probe would never be checked at all.\n *\n * Naming note: an earlier draft called these `negative` / `positive_control`.\n * That reads backwards for `happy_path`, whose primary claim is a success, so\n * the roles are named by evidential function instead. Both older names are\n * accepted as deprecated aliases wherever a role is parsed.\n */\nexport type AssertionRole = \"primary\" | \"control\";\n\n/**\n * Concrete operation exercised by an assertion. Mutation inventory keys claims\n * by kind + target + operation so a SELECT mutation cannot silently stand in\n * for an untested UPDATE/DELETE or action invocation.\n */\nexport type ProofOperation =\n | \"select\"\n | \"insert\"\n | \"update\"\n | \"delete\"\n | \"invoke\"\n | \"request\";\n\n/** Deprecated role spellings accepted on input and normalized to AssertionRole. */\nexport const ASSERTION_ROLE_ALIASES: Readonly<Record<string, AssertionRole>> = {\n negative: \"primary\",\n positive_control: \"control\",\n};\n\n/**\n * Normalize a role from an artifact or manifest. `undefined` means `primary`:\n * traces written before schemaVersion 2 carry no role, and their assertions\n * were all primary claims.\n */\nexport function normalizeAssertionRole(role: unknown): AssertionRole | null {\n if (role === undefined || role === null) return \"primary\";\n if (role === \"primary\" || role === \"control\") return role;\n if (typeof role === \"string\" && role in ASSERTION_ROLE_ALIASES) {\n return ASSERTION_ROLE_ALIASES[role];\n }\n return null;\n}\n\n/**\n * What an assertion concluded.\n *\n * `passed` / `failed` are the two verdicts a boolean can carry. The other two\n * exist because \"we did not find out\" is a real outcome that a boolean has to\n * round to one side or the other, and rounding it to `false` is indistinguishable\n * from a genuine failure while rounding it to `true` is how a suite quietly stops\n * proving anything:\n *\n * - `incomplete` — the probe ran but could not reach a verdict (a precondition\n * was missing, a dependency was unavailable).\n * - `skipped` — the probe was deliberately not run, e.g. a tenant-isolation\n * direction with no fixture rows on that side.\n *\n * Neither counts as evidence: both carry `passed: false`, so neither can satisfy\n * a mission requirement. The status is what lets a consumer tell them apart from\n * a real failure, and stops a skip from disappearing into \"one fewer assertion\".\n */\nexport type AssertionStatus = \"passed\" | \"failed\" | \"incomplete\" | \"skipped\";\n\n/** Derive the status of an assertion that predates the field. */\nexport function normalizeAssertionStatus(\n status: unknown,\n passed: boolean,\n): AssertionStatus {\n if (\n status === \"passed\" ||\n status === \"failed\" ||\n status === \"incomplete\" ||\n status === \"skipped\"\n ) {\n return status;\n }\n return passed ? \"passed\" : \"failed\";\n}\n\n/**\n * A single structured assertion recorded during a step. Produced by the\n * `assert.*` helpers via `recordAssertion(...)`. Hand-written `expect(...)`\n * calls inside a step do NOT appear here — only helper-driven assertions do,\n * because the v1 mission-manifest validator matches on\n * `{ kind, target, passed: true, role }` and needs a closed, machine-readable\n * surface.\n */\nexport interface TraceAssertion {\n /** Category from the closed `PROOF_KINDS` vocabulary. */\n kind: ProofKind;\n /** The thing asserted against: table, route, action, etc. */\n target: string;\n /** Concrete operation the probe exercised. */\n operation?: ProofOperation;\n /**\n * Whether this specific assertion held. Remains the authoritative field for\n * the validator and for consumers: only `true` is evidence.\n */\n passed: boolean;\n /**\n * Why `passed` has the value it has. Omitted is derived from `passed`, so\n * traces written before this field stay readable.\n */\n status?: AssertionStatus;\n /**\n * Evidential role. Omitted is treated as `\"primary\"` by the validator for\n * backward compatibility with traces written before schemaVersion 2.\n */\n role?: AssertionRole;\n /** Optional human-readable note for failure triage. */\n detail?: string;\n /**\n * Which SDK helper produced this assertion, e.g. `\"assert.tenantIsolation\"`.\n *\n * Stamped by the SDK itself through an internal channel that the public\n * `recordAssertion(...)` entry point cannot reach: a value supplied by spec\n * code is stripped before the assertion is recorded. Absent means the\n * assertion was recorded directly by a spec via `recordAssertion(...)`.\n *\n * The distinction is evidential. A helper-emitted assertion went through the\n * helper's vacuity controls, ground-truth re-reads, and role bookkeeping; a\n * spec-recorded assertion asserts whatever the spec chose to write into it.\n * The mission validator therefore requires helper provenance for\n * `tenant_isolation` requirements (a hand-rolled isolation probe has none of\n * `assert.tenantIsolation`'s protections), and a mission can demand it for\n * any requirement via `trace_must_prove[].evidence: \"helper\"`.\n *\n * For `tenant_isolation` the stamp is also identity-checked, not merely\n * present-checked — see TENANT_ISOLATION_TABLE_HELPERS. `assert.httpResponse`\n * records whatever `kind` its caller passes, so \"some helper emitted this\"\n * is not the same claim as \"the isolation probe ran\".\n */\n emittedBy?: string;\n}\n\n/**\n * The only helpers whose stamp counts as tenant-isolation evidence, split by\n * target (tables are stricter than action refs). The single runtime source is\n * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the\n * dependency-free Node scripts (`proof_verify.mjs`, `proof_coverage.mjs`)\n * import directly; see there for the identity rationale.\n */\nexport {\n TENANT_ISOLATION_ACTION_HELPERS,\n TENANT_ISOLATION_TABLE_HELPERS,\n} from \"./portable-vocabulary.mjs\";\n\n/**\n * A single step inside a proof.\n * Produced by `t.step({ intent, kind, target }, async () => { ... })`.\n */\nexport interface TraceStep {\n /** Human-readable description of what the step attempted. */\n intent: string;\n /** Category of the step, from closed vocabulary. */\n kind: ProofKind;\n /** The thing the step targets: action name, table, route, etc. */\n target: string;\n /** What the step observed (returned by the step callback). */\n observation: string;\n /** Whether the step's assertions held. */\n passed: boolean;\n /** Optional: who the step acted as (email, role, seed identifier). */\n actor?: string;\n /** Optional: which workspace the step operated in. */\n workspaceId?: string;\n /** Wall-clock duration of the step callback. */\n durationMs: number;\n /** On failure, the error message captured. */\n error?: string;\n /**\n * Structured assertions recorded by `assert.*` helpers during this step.\n * Empty or omitted for steps that only use `expect(...)` directly.\n * The v1 mission-manifest validator matches `trace_must_prove` entries\n * against this array.\n */\n assertions?: TraceAssertion[];\n}\n\n/**\n * Current per-proof trace artifact schema version.\n *\n * 2 — adds `role` and `status` to assertions and the `schemaVersion` /\n * `specFile` / `specHash` / `commit` / `dirty` provenance fields below.\n * Consumers written against v1 keep working: every v1 field is still present\n * with the same meaning, and both new assertion fields are derivable when\n * absent.\n *\n * `commit`, `dirty`, and `mutation` arrived after v2 shipped and did not bump\n * it: they are optional additions, and the contract's rule is that additive\n * fields do not move a version. A consumer that needs them must treat absence\n * as \"unknown\" anyway, since normal runs have no mutation and a non-git\n * checkout cannot supply code provenance.\n */\nexport const TRACE_ARTIFACT_SCHEMA_VERSION = 2;\n\n/**\n * Provenance attached only when the mutation harness deliberately plants a\n * defect. A red mutation trace means the check detected the named defect; it\n * must never be mistaken for a real application regression.\n */\nexport interface TraceMutation {\n /** Stable mutation identifier from the repository-owned mutation catalog. */\n id: string;\n /** Always true; makes the intentional nature explicit to copied consumers. */\n planted: true;\n}\n\n/**\n * A trace artifact — one proof's worth of evidence.\n * Written atomically to `.proof/traces/<proofId>.json` when the proof\n * completes (pass or fail).\n */\nexport interface TraceArtifact {\n /**\n * Pinned to TRACE_ARTIFACT_SCHEMA_VERSION. Absent on traces written by\n * SDK versions before provenance was added.\n */\n schemaVersion?: number;\n /**\n * Repo-relative path of the spec file that produced this trace, e.g.\n * `e2e/proofs/workspace-invite.proof.ts`. Lets a reviewer go from a claim\n * straight to the code that made it.\n */\n specFile?: string;\n /**\n * First 12 hex chars of the SHA-256 of the spec file's contents. Two runs\n * claiming the same proof with different hashes were produced by different\n * code — which is the whole question when a previously-red proof turns\n * green.\n */\n specHash?: string;\n /**\n * Full SHA of the commit that was checked out while the proof ran. `specHash`\n * pins the spec that made the claim; this pins the application it was made\n * against, which is what lets an external ledger say \"red at X, green at Y\".\n *\n * Absent outside a git checkout. On a CI `pull_request` run this is the\n * ephemeral merge commit the checkout created — accurate about what was\n * tested, but not a commit you can return to later.\n */\n commit?: string;\n /**\n * Whether the working tree had uncommitted changes when the proof ran. A pass\n * from a dirty tree is evidence about code that exists nowhere but one laptop,\n * so a consumer gating on traces should be able to refuse it. Absent when it\n * could not be determined, which is not the same as `false`.\n */\n dirty?: boolean;\n /** Hash of git-tracked and untracked nonignored source inputs. */\n sourceHash?: string;\n /** Writer identity for distinguishing retries from duplicate proof IDs. */\n executionId?: string;\n retry?: number;\n /**\n * Present only for intentional red runs produced by `proof:mutate`.\n * Stored inside the trace so provenance survives artifact renames/copies.\n */\n mutation?: TraceMutation;\n /**\n * Per-file identifier for this proof. Used as the filename stem and as the\n * join key when the v1 mission-manifest validator looks for a matching\n * passing step for a `trace_must_prove` entry.\n */\n proofId: string;\n /**\n * Optional reference to the external mission (ticket) this proof belongs\n * to. Multiple proofs share one `missionId`. Absent in v0.5 local runs\n * that are not tied to a mission.\n */\n missionId?: string;\n /** ISO 8601 timestamp of proof start. */\n timestamp: string;\n /** Total wall-clock duration across all steps. */\n durationMs: number;\n /** True only if every step passed. */\n passed: boolean;\n /** Ordered list of steps as they were executed. */\n steps: TraceStep[];\n}\n\n/**\n * Options accepted by `trace.proof(...)`. A bare string is treated as\n * `{ proofId: string }` — the common v0.5 case where no mission is attached.\n */\nexport interface ProofOptions {\n proofId: string;\n missionId?: string;\n}\n\n/**\n * The handle passed to a proof callback. Provides the step recorder.\n */\nexport interface TraceRecorder {\n step<T extends StepResult | void>(\n opts: StepOptions,\n fn: () => Promise<T>,\n ): Promise<T>;\n}\n\nexport interface StepOptions {\n intent: string;\n kind: ProofKind;\n target: string;\n actor?: string;\n workspaceId?: string;\n}\n\n/**\n * Optional payload returned by a step callback. If the callback returns\n * `void` the step is still recorded with `observation: \"\"`.\n */\nexport interface StepResult {\n observation?: string;\n}\n","// ---------------------------------------------------------------------------\n// Mission manifest types.\n//\n// A *mission manifest* is the JSON artifact an external orchestrator or a\n// developer in manual mode writes to `.proof/current-mission.json` to\n// tell `pnpm proof:verify` what the PR must prove. The validator at\n// `src/node/validate-mission.ts` cross-references each\n// requirement against the fresh `.proof/capabilities.json`, `.proof/schema.json`,\n// and the aggregated trace.\n//\n// Shape is pinned by contract — additive changes bump nothing, breaking\n// changes bump `schemaVersion` at the top of the manifest. Consumers should\n// assert `schemaVersion === 1` and fail loudly otherwise.\n// ---------------------------------------------------------------------------\n\nimport type { AssertionRole } from \"./trace-types\";\nimport {\n ACTION_CHANGE_KINDS,\n TABLE_CHANGE_KINDS,\n} from \"./portable-vocabulary.mjs\";\nimport type {\n PolicyCommand,\n ProofKind,\n ProofVerb,\n RlsClassification,\n} from \"./vocabulary\";\n\n/**\n * Manifest schema version. Deliberately still 1 after the July 2026 validator\n * work: `trace_must_prove[].role` and `.proofId` are OPTIONAL additions, so\n * every v1 manifest is still shape-valid and consumers need not regenerate\n * anything. Note that validation did get stricter — a `control` assertion can\n * no longer satisfy a requirement — so a manifest that passed on a vacuous\n * probe before may now (correctly) fail. That is a behaviour tightening, not a\n * shape change, and bumping the version would have forced a rewrite of every\n * existing manifest for no benefit.\n */\nexport const MISSION_MANIFEST_SCHEMA_VERSION = 1;\n\nexport interface CapabilityRequirement {\n /** Matches `createAction({ functionName: <name> })` exactly. */\n name: string;\n /** Module folder name (e.g. `\"issues\"`, not `\"__business-logic/issues\"`). */\n module: string;\n /** One of PROOF_VERBS. */\n verb: ProofVerb;\n /** Free-form noun, lowercased by convention (e.g. `\"issue\"`, `\"workspace\"`). */\n object: string;\n /** Optional: invariants that the action's `withProof({ invariants })` must advertise. */\n invariants?: readonly string[];\n}\n\nexport interface SchemaRequirement {\n /** Unqualified public-schema table name. */\n table: string;\n /** Columns that must exist on the table. Order-insensitive. */\n required_columns: readonly string[];\n /** One of RLS_CLASSIFICATIONS. */\n rls_classification: RlsClassification;\n}\n\n/**\n * \"No policy on this table targets this role for these commands.\"\n *\n * Exists because `rls_classification` is one label per table and Postgres RLS is\n * per-command: `audit_logs` being \"admin readable, service writable\" cannot be\n * said with a single label, and the label a manifest picks would have to be a\n * lie in one direction or the other.\n *\n * Checks reachability, not the predicate — `USING` and `WITH CHECK` are not read.\n * A policy `TO authenticated USING (is_super_admin())` counts as targeting\n * `authenticated`, so \"this role may act only under condition X\" is not\n * expressible here and belongs in `trace_must_prove`, where something exercises\n * it. Write prohibitions (\"members never INSERT into the audit log\") are the\n * natural fit.\n *\n * Deliberately only the prohibition, never \"a policy must exist\". A policy's\n * existence is weak evidence — it says a rule was written, not that it works —\n * and the thing that makes a permission real is a passing proof, which belongs in\n * `trace_must_prove`. A prohibition is different: it is a claim that can fail\n * dangerously and silently, and it is checkable from the migrations alone.\n */\nexport interface PolicyProhibition {\n /** Unqualified public-schema table name. */\n table: string;\n /**\n * Database role that must not be granted access, e.g. `\"authenticated\"` or\n * `\"anon\"`. A policy with no `TO` clause defaults to `PUBLIC` in Postgres and\n * therefore counts as granting every role.\n */\n role: string;\n /**\n * Commands the prohibition covers. Omit to mean \"any command\". A policy\n * declared `FOR ALL` matches whatever is listed here, because that is what it\n * does in the database.\n */\n commands?: readonly PolicyCommand[];\n /** Human-readable statement of the rule, echoed in the failure message. */\n description: string;\n}\n\nexport interface TraceRequirement {\n /** One of PROOF_KINDS. */\n kind: ProofKind;\n /**\n * Free-form identifier the assertion emits. Must match an assertion's\n * `target` exactly (e.g. table name for `tenant_isolation`, capability\n * name for `authorization`).\n */\n target: string;\n /** Human-readable statement of what should be proved. */\n description: string;\n /**\n * Which evidential role must satisfy this requirement. Defaults to\n * `\"primary\"`, which is almost always what you want: a `\"control\"`\n * assertion passes by design and proves only that the probe was capable of\n * failing, so it must never stand in for the claim itself.\n *\n * Accepts the deprecated spellings `\"negative\"` / `\"positive_control\"`.\n */\n role?: AssertionRole;\n /**\n * Optional: restrict matching to assertions emitted by this proof.\n *\n * By default a requirement may be satisfied by a matching assertion from\n * ANY proof in the run. That is convenient but means an unrelated spec can\n * unknowingly satisfy a mission's requirement. Set this when the mission\n * cares that a SPECIFIC proof made the claim. Either way, the aggregated\n * trace records which proof actually supplied the evidence.\n */\n proofId?: string;\n /**\n * What origin of assertion may satisfy this requirement.\n *\n * - `\"helper\"` — only assertions stamped `emittedBy` by an SDK `assert.*`\n * helper count. Helper assertions went through the SDK's vacuity\n * controls and ground-truth re-reads; a spec-recorded assertion asserts\n * whatever the spec wrote into it.\n * - `\"any\"` — spec-recorded assertions (raw `recordAssertion`) count too.\n * Those claims remain backstopped by the mutation inventory, which is\n * why bespoke probes (RPC grants, column guards) stay expressible.\n *\n * `tenant_isolation` is always `\"helper\"`, and the stamp is additionally\n * identity-checked (see TENANT_ISOLATION_TABLE_HELPERS in trace-types):\n * declaring `evidence: \"any\"` for it is a `manifest_shape` error, so the\n * mission gate and strict coverage can never disagree about whether a raw\n * isolation assertion is admissible. Every other kind defaults to `\"any\"`.\n */\n evidence?: EvidenceOrigin;\n}\n\n/** Origin of assertion allowed to satisfy a `trace_must_prove` entry. */\nexport type EvidenceOrigin = \"helper\" | \"any\";\n\n/**\n * The evidence origin a requirement demands, applying the kind-based default.\n * Consumed directly by the compiled validator used by `proof-harness verify`.\n */\nexport function requiredEvidenceOrigin(\n kind: ProofKind,\n evidence: EvidenceOrigin | undefined,\n): EvidenceOrigin {\n // Tenant isolation is helper-only regardless of what the manifest says: the\n // shape validator rejects `evidence: \"any\"` for it, and this belt-and-braces\n // keeps the matching honest even for a manifest that skipped shape checks.\n if (kind === \"tenant_isolation\") return \"helper\";\n return evidence ?? \"any\";\n}\n\nexport interface MissionRequirements {\n capabilities_must_exist: readonly CapabilityRequirement[];\n schema_must_contain: readonly SchemaRequirement[];\n trace_must_prove: readonly TraceRequirement[];\n /**\n * Optional, so every existing v1 manifest stays shape-valid. Omitting it means\n * \"no policy-level claims\", not \"policies are fine\".\n */\n policies_must_not_allow?: readonly PolicyProhibition[];\n}\n\n/**\n * Change kinds an action declaration may allow. Like tables, a modified\n * action is decomposed into facets and EVERY facet must be covered — a\n * mission expecting an invariant declaration to change does not thereby\n * authorize an RBAC/auth middleware change on the same action.\n *\n * Derived from the single runtime source in\n * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the\n * dependency-free Node scripts import directly; see there for the per-facet\n * meanings.\n */\nexport type ActionChangeKind = (typeof ACTION_CHANGE_KINDS)[number];\n\n/**\n * Change kinds a table declaration may allow. A \"modified\" table entry is\n * decomposed into facets, and EVERY facet must be covered by the declaration —\n * declaring `columns_added` on a table does not authorize a policy change on\n * the same table. Derived from `portable-vocabulary.mjs`.\n */\nexport type TableChangeKind = (typeof TABLE_CHANGE_KINDS)[number];\n\n// Re-exported so TS consumers get the runtime lists from the same import\n// site as the types they parameterize.\nexport { ACTION_CHANGE_KINDS, TABLE_CHANGE_KINDS };\n\n/** Exact-ref action declaration. A bare string ref means \"any change\". */\nexport interface ExpectedActionChange {\n /** `<module>:<name>`, the same ref capabilities.json uses. */\n ref: string;\n /** Omit for \"any change to this action\". */\n changes?: readonly ActionChangeKind[];\n}\n\n/** Table declaration. A bare string name means \"any tracked change\". */\nexport interface ExpectedTableChange {\n name: string;\n /** Omit for \"any tracked change to this table\". */\n changes?: readonly TableChangeKind[];\n}\n\n/**\n * The proof-surface change budget a mission grants its PR.\n *\n * Everything else in the manifest states what MUST exist; this block bounds\n * what else MAY change on the surfaces drift tracks. Read by\n * the package drift engine, which diffs the regenerated\n * `capabilities.json` / `schema.json` / `package.json` / lockfile against the\n * PR's base and fails (`drift_undeclared`) on a high-severity delta outside\n * the declared budget.\n *\n * Scope is deliberately narrow-by-default:\n *\n * - `modules` covers only ADDED actions in the named module (the\n * new-module convenience). Modifying or removing an existing action is\n * never authorized by a module declaration — a mission that expects a\n * guard or signature change on an existing action must name the exact\n * ref in `actions`, so \"add a small feature\" can never quietly license\n * weakening an unrelated action in the same module.\n * - String shorthands (\"issues\" in `tables`, \"issues:createIssue\" in\n * `actions`) mean \"any change\" and are the coarse form; the object forms\n * narrow to specific change kinds.\n *\n * Omitting the block keeps drift report-only, so every existing v1 manifest\n * behaves exactly as before.\n */\nexport interface ExpectedChanges {\n /**\n * Module leaf names (e.g. `\"issues\"`) in which NEW actions may appear.\n * Does not authorize modification or removal of existing actions.\n */\n modules?: readonly string[];\n /** Exact action refs that may change; see {@link ExpectedActionChange}. */\n actions?: readonly (string | ExpectedActionChange)[];\n /** Tables that may change; see {@link ExpectedTableChange}. */\n tables?: readonly (string | ExpectedTableChange)[];\n /**\n * Runtime dependencies (`package.json` `dependencies`) that may be added,\n * removed, or version-changed. Every runtime dependency delta is\n * high-severity — a swap or downgrade changes shipped behavior as surely as\n * an addition.\n */\n dependencies?: readonly string[];\n /**\n * Expect a lockfile-only resolution change (pnpm-lock.yaml differs while\n * the package.json dependency surface does not). Rare and suspicious by\n * default, hence explicitly declared rather than inferred.\n */\n lockfile?: boolean;\n}\n\nexport interface MissionManifest {\n /** Pinned to MISSION_MANIFEST_SCHEMA_VERSION. */\n schemaVersion: number;\n /** Ticket id from the consumer's kanban (e.g. `\"M-042\"`). */\n missionId: string;\n /** Short human-readable title. */\n missionTitle: string;\n /** Product repo slug (informational). */\n productRepo?: string;\n /** PR branch name (informational). */\n prBranch?: string;\n /** ISO-8601 timestamp the manifest was generated. */\n createdAt: string;\n requirements: MissionRequirements;\n /**\n * Optional drift bound; see {@link ExpectedChanges}. Absent means the drift\n * report is informational only.\n */\n expectedChanges?: ExpectedChanges;\n /** Free-form notes for reviewers / the coding agent. */\n acceptanceNotes?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Validation result types\n// ---------------------------------------------------------------------------\n\nexport type ValidationCategory =\n | \"trace_shape\"\n | \"capability_missing\"\n | \"capability_mismatch\"\n | \"schema_missing\"\n | \"schema_column_missing\"\n | \"schema_rls_mismatch\"\n /** A policy grants access the manifest declared must not exist. */\n | \"schema_policy_permits\"\n | \"trace_missing\"\n /**\n * A passing assertion matched the requirement but was recorded directly by\n * spec code (`recordAssertion`) where the requirement demands an SDK-helper\n * origin (`evidence: \"helper\"`, the default for `tenant_isolation`).\n */\n | \"trace_unverified\"\n | \"manifest_shape\"\n /** The Playwright run itself exited non-zero. Reported by the CLI, not the validator. */\n | \"proof_run\"\n /** The run produced no trace artifacts at all, so there is nothing to validate. */\n | \"no_traces\"\n /**\n * A trace in the directory was recorded against a different commit than the\n * current run — leftover evidence that cannot speak for the code under review.\n * Reported by the CLI, which knows the run's commit; the validator sees only\n * the bundle it is handed.\n */\n | \"stale_trace\";\n\n/**\n * Which proof supplied the evidence for a satisfied `trace_must_prove` entry.\n *\n * Recorded for every requirement — satisfied or not — so a reviewer can audit\n * the mapping instead of trusting that \"0 issues\" meant the right proof made\n * the claim. Requirements are matched against all assertions in the run, so\n * without this the attribution would be invisible.\n */\nexport interface RequirementEvidence {\n kind: ProofKind;\n target: string;\n role: AssertionRole;\n satisfied: boolean;\n /** proofId of the trace whose assertion satisfied it, when satisfied. */\n proofId?: string;\n /** Spec file that produced that trace, when known. */\n specFile?: string;\n /** The satisfying assertion's `detail`, verbatim. */\n detail?: string;\n /**\n * The satisfying assertion's helper provenance, when it has one. Absent\n * means the evidence was recorded directly by spec code.\n */\n emittedBy?: string;\n}\n\nexport interface ValidationIssue {\n category: ValidationCategory;\n /** Human-readable summary including expected vs found. */\n message: string;\n /** Structured context for machine consumers (optional). */\n context?: Record<string, unknown>;\n}\n\nexport interface ValidationResult {\n missionId: string;\n ok: boolean;\n issues: ValidationIssue[];\n /** Attribution for every `trace_must_prove` entry, in manifest order. */\n evidence?: RequirementEvidence[];\n}\n","import {\n ASSERTION_HELPERS,\n assertionPassed,\n traceShapeIssues,\n} from \"./evidence\";\n// ---------------------------------------------------------------------------\n// Mission manifest validator.\n//\n// Three-way cross-reference:\n// 1. Manifest is shape-correct (schemaVersion + required fields).\n// 2. Every `capabilities_must_exist[i]` is found in `.proof/capabilities.json`.\n// 3. Every `schema_must_contain[i]` is found in `.proof/schema.json` with\n// matching required_columns and rls_classification.\n// 4. No policy in `.proof/schema.json` grants what a\n// `policies_must_not_allow[i]` entry forbids. This is the per-command view\n// that `rls_classification`'s single label per table cannot express.\n// 5. Every `trace_must_prove[i]` has at least one passing assertion in the\n// aggregated trace with matching `kind` + `target` + `role` (default\n// `primary`), optionally pinned to a specific `proofId`. Which proof\n// supplied each piece of evidence is reported back in `evidence` so the\n// attribution is auditable rather than implicit.\n//\n// Produces a structured ValidationResult; the CLI driver is responsible for\n// formatting to `[PROOF_FAIL]` lines and exit codes.\n//\n// Intentionally zero runtime dependencies — this file runs under Node during\n// `pnpm proof:verify` and inside CI, and must stay fast-starting.\n// ---------------------------------------------------------------------------\n\nimport {\n ACTION_CHANGE_KINDS,\n MISSION_MANIFEST_SCHEMA_VERSION,\n requiredEvidenceOrigin,\n TABLE_CHANGE_KINDS,\n type MissionManifest,\n type RequirementEvidence,\n type ValidationIssue,\n type ValidationResult,\n} from \"../shared/mission-types\";\nimport {\n isPolicyCommand,\n isProofKind,\n isProofVerb,\n isRlsClassification,\n} from \"../shared/vocabulary\";\nimport {\n normalizeAssertionRole,\n TENANT_ISOLATION_ACTION_HELPERS,\n TENANT_ISOLATION_TABLE_HELPERS,\n type TraceArtifact,\n type TraceAssertion,\n} from \"../shared/trace-types\";\n\n// ---------------------------------------------------------------------------\n// External artifact shapes (only the fields we need)\n// ---------------------------------------------------------------------------\n\nexport interface CapabilitiesArtifact {\n schemaVersion: number;\n capabilities: Array<{\n name: string;\n module: string;\n /** Literal exported wrapper function used by the generated invoke registry. */\n exportName?: string | null;\n verb: string | null;\n object: string | null;\n invariants: readonly string[];\n /** Derived scanner signal; absent on older additive v1 artifacts. */\n acceptsWorkspaceId?: boolean;\n /** Internal BOT/server-only plumbing is not a user-invoked boundary. */\n internalOnly?: boolean;\n /** Direct next/cache updateTag is not safe through the proof route. */\n usesDirectUpdateTag?: boolean;\n /** Concrete writes issued through createSupabaseServiceClient(). */\n serviceRoleMutations?: ReadonlyArray<{\n table: string;\n operation: \"insert\" | \"upsert\" | \"update\" | \"delete\";\n }>;\n /** Direct supported privileged Auth API calls, separate from SQL writes. */\n serviceRoleAuthOperations?: readonly \"deleteUser\"[];\n /** Relevant guards observed in the action pipeline. */\n middleware?: {\n auth: boolean;\n tenantIsolation: boolean;\n rbac: boolean;\n };\n file: string;\n }>;\n unclassified?: readonly unknown[];\n}\n\nexport interface SchemaPolicy {\n name: string;\n /** One of POLICY_COMMANDS as the parser reports it. */\n command: string;\n /** Roles from the policy's `TO` clause. Empty means no clause, i.e. PUBLIC. */\n roles: readonly string[];\n}\n\nexport interface SchemaArtifact {\n assessed?: boolean;\n schemaVersion: number;\n tables: Array<{\n name: string;\n columns: readonly string[];\n rls_classification: string;\n policies: readonly SchemaPolicy[];\n sourceFiles: readonly string[];\n }>;\n unclassified?: readonly unknown[];\n}\n\n/**\n * A trace bundle for validation is the aggregated list of every trace the\n * proof run emitted. Kept as `TraceArtifact[]` so v0.6's per-proof JSON\n * files can be concatenated without transformation.\n */\nexport type TraceBundle = readonly TraceArtifact[];\n\n// ---------------------------------------------------------------------------\n// Shape validation\n// ---------------------------------------------------------------------------\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/**\n * Validate the optional `expectedChanges` block. Kept separate because the\n * shape has real structure (string shorthands vs narrowing objects) and a\n * malformed budget must fail loudly rather than half-enforce.\n */\nfunction expectedChangesShapeIssues(block: unknown): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n if (block === undefined) return issues;\n if (!block || typeof block !== \"object\" || Array.isArray(block)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"expectedChanges must be an object when present\",\n });\n return issues;\n }\n const changes = block as Record<string, unknown>;\n\n for (const key of [\"modules\", \"dependencies\"] as const) {\n const list = changes[key];\n if (list === undefined) continue;\n if (!Array.isArray(list) || !list.every(isNonEmptyString)) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key} must be an array of non-empty strings when present`,\n });\n }\n }\n\n const validateEntryList = (\n key: \"actions\" | \"tables\",\n nameField: \"ref\" | \"name\",\n kinds: readonly string[],\n ) => {\n const list = changes[key];\n if (list === undefined) return;\n if (!Array.isArray(list)) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key} must be an array when present`,\n });\n return;\n }\n for (const entry of list) {\n if (isNonEmptyString(entry)) continue;\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key} entries must be non-empty strings or { ${nameField}, changes? } objects`,\n });\n continue;\n }\n const record = entry as Record<string, unknown>;\n if (!isNonEmptyString(record[nameField])) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key}[].${nameField} is required (non-empty string)`,\n });\n }\n if (record.changes !== undefined) {\n if (\n !Array.isArray(record.changes) ||\n record.changes.length === 0 ||\n !record.changes.every((c) => kinds.includes(c as string))\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key}[].changes must be a non-empty array drawn from: ${kinds.join(\", \")}`,\n });\n }\n }\n }\n };\n validateEntryList(\"actions\", \"ref\", ACTION_CHANGE_KINDS);\n validateEntryList(\"tables\", \"name\", TABLE_CHANGE_KINDS);\n\n if (changes.lockfile !== undefined && typeof changes.lockfile !== \"boolean\") {\n issues.push({\n category: \"manifest_shape\",\n message: \"expectedChanges.lockfile must be a boolean when present\",\n });\n }\n\n return issues;\n}\n\nfunction shapeIssues(manifest: unknown): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n if (!manifest || typeof manifest !== \"object\") {\n issues.push({\n category: \"manifest_shape\",\n message: \"manifest is not a JSON object\",\n });\n return issues;\n }\n const m = manifest as Partial<MissionManifest>;\n\n if (m.schemaVersion !== MISSION_MANIFEST_SCHEMA_VERSION) {\n issues.push({\n category: \"manifest_shape\",\n message: `expected schemaVersion=${MISSION_MANIFEST_SCHEMA_VERSION}, found ${String(m.schemaVersion)}`,\n });\n }\n if (typeof m.missionId !== \"string\" || m.missionId.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message: \"missionId is required (non-empty string)\",\n });\n }\n if (typeof m.missionTitle !== \"string\" || m.missionTitle.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message: \"missionTitle is required (non-empty string)\",\n });\n }\n if (typeof m.createdAt !== \"string\" || m.createdAt.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message: \"createdAt is required (ISO-8601 string)\",\n });\n }\n\n // Optional drift bound: absent is fine, present-but-wrong is not — a typo'd\n // scope list would otherwise silently disable the enforcement it names.\n issues.push(...expectedChangesShapeIssues(m.expectedChanges));\n\n const req = m.requirements;\n if (!req || typeof req !== \"object\") {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements block is required\",\n });\n return issues;\n }\n if (!Array.isArray(req.capabilities_must_exist)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements.capabilities_must_exist must be an array\",\n });\n }\n if (!Array.isArray(req.schema_must_contain)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements.schema_must_contain must be an array\",\n });\n }\n if (!Array.isArray(req.trace_must_prove)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements.trace_must_prove must be an array\",\n });\n }\n // Optional block: absent is fine, present-but-wrong is not.\n if (\n req.policies_must_not_allow !== undefined &&\n !Array.isArray(req.policies_must_not_allow)\n ) {\n issues.push({\n category: \"manifest_shape\",\n message:\n \"requirements.policies_must_not_allow must be an array when present\",\n });\n }\n\n const fields = {\n capabilities_must_exist: [\"name\", \"module\", \"object\"],\n schema_must_contain: [\"table\"],\n trace_must_prove: [\"target\", \"description\"],\n policies_must_not_allow: [\"table\", \"role\", \"description\"],\n } as const;\n for (const [key, names] of Object.entries(fields)) {\n const entries = (req as unknown as Record<string, unknown>)[key];\n if (!Array.isArray(entries)) continue;\n for (const entry of entries) {\n if (\n !entry ||\n typeof entry !== \"object\" ||\n names.some((name) => !isNonEmptyString(entry[name]))\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: `${key} contains an invalid entry`,\n });\n } else if (\n key === \"schema_must_contain\" &&\n (!Array.isArray(entry.required_columns) ||\n !entry.required_columns.every(isNonEmptyString))\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: \"required_columns must be an array of column names\",\n });\n }\n }\n }\n if (issues.length) return issues;\n\n // Vocabulary checks (best-effort — don't fail if fields are missing since\n // shape errors above will already have been reported).\n for (const cap of Array.isArray(req.capabilities_must_exist)\n ? req.capabilities_must_exist\n : []) {\n if (!isProofVerb((cap as { verb?: unknown }).verb)) {\n issues.push({\n category: \"manifest_shape\",\n message: `capabilities_must_exist[].verb \"${String((cap as { verb?: unknown }).verb)}\" is not in PROOF_VERBS`,\n });\n }\n }\n for (const s of Array.isArray(req.schema_must_contain)\n ? req.schema_must_contain\n : []) {\n if (\n !isRlsClassification(\n (s as { rls_classification?: unknown }).rls_classification,\n )\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: `schema_must_contain[].rls_classification \"${String((s as { rls_classification?: unknown }).rls_classification)}\" is not in RLS_CLASSIFICATIONS`,\n });\n }\n }\n for (const t of Array.isArray(req.trace_must_prove)\n ? req.trace_must_prove\n : []) {\n if (!isProofKind((t as { kind?: unknown }).kind)) {\n issues.push({\n category: \"manifest_shape\",\n message: `trace_must_prove[].kind \"${String((t as { kind?: unknown }).kind)}\" is not in PROOF_KINDS`,\n });\n }\n const evidence = (t as { evidence?: unknown }).evidence;\n if (evidence !== undefined && evidence !== \"helper\" && evidence !== \"any\") {\n issues.push({\n category: \"manifest_shape\",\n message: `trace_must_prove[].evidence \"${String(evidence)}\" is not a valid evidence origin; expected \"helper\" or \"any\"`,\n });\n }\n // Tenant isolation admits no opt-out: strict coverage rejects raw\n // isolation assertions unconditionally, and the mission gate must never\n // accept what coverage refuses. Bespoke escapes live in mutation policy,\n // not here.\n if (\n evidence === \"any\" &&\n (t as { kind?: unknown }).kind === \"tenant_isolation\"\n ) {\n issues.push({\n category: \"manifest_shape\",\n message:\n 'trace_must_prove[].evidence \"any\" is not allowed for kind \"tenant_isolation\"; isolation evidence must come from an SDK helper (assert.tenantIsolation, or assert.authorization for action targets)',\n });\n }\n }\n for (const p of Array.isArray(req.policies_must_not_allow)\n ? req.policies_must_not_allow\n : []) {\n const prohibition = p as { role?: unknown; commands?: unknown };\n if (typeof prohibition.role !== \"string\" || prohibition.role.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message:\n \"policies_must_not_allow[].role is required (non-empty database role name)\",\n });\n }\n // A typo'd command would otherwise match nothing and pass, turning a\n // security claim into a comment.\n if (prohibition.commands !== undefined) {\n if (!Array.isArray(prohibition.commands)) {\n issues.push({\n category: \"manifest_shape\",\n message:\n \"policies_must_not_allow[].commands must be an array when present\",\n });\n } else {\n for (const command of prohibition.commands) {\n if (!isPolicyCommand(command)) {\n issues.push({\n category: \"manifest_shape\",\n message: `policies_must_not_allow[].commands entry \"${String(command)}\" is not in POLICY_COMMANDS`,\n });\n }\n }\n }\n }\n }\n\n return issues;\n}\n\n// ---------------------------------------------------------------------------\n// Cross-reference checks\n// ---------------------------------------------------------------------------\n\nfunction checkCapabilities(\n manifest: MissionManifest,\n capabilities: CapabilitiesArtifact,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n for (const req of manifest.requirements.capabilities_must_exist) {\n const match = capabilities.capabilities.find(\n (c) => c.name === req.name && c.module === req.module,\n );\n if (!match) {\n issues.push({\n category: \"capability_missing\",\n message: `expected capability ${req.module}:${req.name} (verb=${req.verb}, object=${req.object}); not found in capabilities.json`,\n context: { requirement: req },\n });\n continue;\n }\n if (match.verb !== req.verb) {\n issues.push({\n category: \"capability_mismatch\",\n message: `capability ${req.module}:${req.name} has verb=\"${match.verb ?? \"<missing>\"}\" but manifest requires \"${req.verb}\"; add or update withProof({ verb }) on the action`,\n context: { requirement: req, found: match },\n });\n }\n if (req.object && match.object !== req.object) {\n issues.push({\n category: \"capability_mismatch\",\n message: `capability ${req.module}:${req.name} has object=\"${match.object ?? \"<missing>\"}\" but manifest requires \"${req.object}\"; add or update withProof({ object }) on the action`,\n context: { requirement: req, found: match },\n });\n }\n if (req.invariants && req.invariants.length > 0) {\n const missing = req.invariants.filter(\n (inv) => !match.invariants.includes(inv),\n );\n if (missing.length > 0) {\n issues.push({\n category: \"capability_mismatch\",\n message: `capability ${req.module}:${req.name} is missing invariants: ${missing.join(\", \")}; add them to withProof({ invariants })`,\n context: { requirement: req, found: match, missing },\n });\n }\n }\n }\n return issues;\n}\n\nfunction checkSchema(\n manifest: MissionManifest,\n schema: SchemaArtifact,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n for (const req of manifest.requirements.schema_must_contain) {\n const table = schema.tables.find((t) => t.name === req.table);\n if (!table) {\n issues.push({\n category: \"schema_missing\",\n message: `expected table \"${req.table}\" not found in schema.json; add a CREATE TABLE migration`,\n context: { requirement: req },\n });\n continue;\n }\n const missingCols = req.required_columns.filter(\n (c) => !table.columns.includes(c),\n );\n if (missingCols.length > 0) {\n issues.push({\n category: \"schema_column_missing\",\n message: `table \"${req.table}\" is missing columns: ${missingCols.join(\", \")}; found columns: ${table.columns.join(\", \")}`,\n context: {\n requirement: req,\n missing: missingCols,\n found: table.columns,\n },\n });\n }\n if (table.rls_classification !== req.rls_classification) {\n issues.push({\n category: \"schema_rls_mismatch\",\n message: `table \"${req.table}\" has rls_classification=\"${table.rls_classification}\" but manifest requires \"${req.rls_classification}\"; review RLS policies on the table`,\n context: { requirement: req, found: table.rls_classification },\n });\n }\n }\n return issues;\n}\n\n/**\n * Whether a policy hands `role` any authority.\n *\n * An empty `roles` list is Postgres' `PUBLIC` default — a policy written without\n * a `TO` clause applies to everyone, so treating \"no roles listed\" as \"no roles\n * granted\" would read the most permissive policy as the most restrictive one.\n */\nfunction policyGrantsRole(policy: SchemaPolicy, role: string): boolean {\n if (policy.roles.length === 0) return true;\n return policy.roles.some(\n (r) => r === role || r === \"public\" || r === \"PUBLIC\",\n );\n}\n\n/** Whether a policy's command is covered by a prohibition's command list. */\nfunction policyMatchesCommands(\n policy: SchemaPolicy,\n commands: readonly string[] | undefined,\n): boolean {\n if (!commands || commands.length === 0) return true;\n // `FOR ALL` covers every command, so it matches whatever was asked about.\n if (policy.command === \"ALL\") return true;\n return commands.includes(policy.command);\n}\n\nfunction checkPolicies(\n manifest: MissionManifest,\n schema: SchemaArtifact,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n for (const req of manifest.requirements.policies_must_not_allow ?? []) {\n const table = schema.tables.find((t) => t.name === req.table);\n if (!table) {\n issues.push({\n category: \"schema_missing\",\n message: `expected table \"${req.table}\" not found in schema.json; add a CREATE TABLE migration`,\n context: { requirement: req },\n });\n continue;\n }\n const offending = table.policies.filter(\n (p) =>\n policyGrantsRole(p, req.role) && policyMatchesCommands(p, req.commands),\n );\n if (offending.length > 0) {\n const scope = req.commands?.length\n ? req.commands.join(\", \")\n : \"any command\";\n issues.push({\n category: \"schema_policy_permits\",\n message:\n `table \"${req.table}\" must not allow role \"${req.role}\" (${scope}), ` +\n `but ${offending.length} policy/policies do: ` +\n offending\n .map(\n (p) =>\n `\"${p.name}\" FOR ${p.command} TO ${p.roles.length > 0 ? p.roles.join(\"/\") : \"PUBLIC (no TO clause)\"}`,\n )\n .join(\", \") +\n `; requirement: \"${req.description}\"`,\n context: { requirement: req, offending },\n });\n }\n }\n return issues;\n}\n\n/**\n * An assertion plus the proof it came from. Attribution has to survive the\n * flattening, otherwise a satisfied requirement can't be traced back to the\n * spec that satisfied it.\n */\nexport interface AttributedAssertion extends TraceAssertion {\n eligible?: boolean;\n proofId: string;\n specFile?: string;\n}\n\n/** Flatten every assertion across every trace / step, keeping attribution. */\nexport function collectAssertions(bundle: TraceBundle): AttributedAssertion[] {\n const out: AttributedAssertion[] = [];\n for (const trace of bundle) {\n for (const step of trace.steps ?? []) {\n for (const a of step.assertions ?? []) {\n out.push({\n ...a,\n eligible: trace.passed && step.passed && !trace.mutation,\n proofId: trace.proofId,\n ...(trace.specFile ? { specFile: trace.specFile } : {}),\n });\n }\n }\n }\n return out;\n}\n\n/**\n * Which helper stamps may satisfy a requirement, or `null` for \"any stamp\".\n *\n * Only `tenant_isolation` is identity-bound: `assert.httpResponse` records\n * whatever `kind` its caller passes, so a mere presence check would let an\n * HTTP probe labelled `tenant_isolation` count as isolation evidence. Table\n * targets accept only `assert.tenantIsolation` (the dual-tenant probe);\n * action targets also accept `assert.authorization`, whose action probes\n * record the cross-tenant invariant they establish.\n */\nfunction allowedHelpersFor(\n kind: string,\n target: string,\n tableNames: ReadonlySet<string>,\n): readonly string[] | null {\n if (kind !== \"tenant_isolation\") return null;\n const head = String(target).split(\".\")[0];\n return tableNames.has(head)\n ? TENANT_ISOLATION_TABLE_HELPERS\n : TENANT_ISOLATION_ACTION_HELPERS;\n}\n\nfunction checkTraces(\n manifest: MissionManifest,\n bundle: TraceBundle,\n schema: SchemaArtifact,\n): { issues: ValidationIssue[]; evidence: RequirementEvidence[] } {\n const issues: ValidationIssue[] = [];\n const evidence: RequirementEvidence[] = [];\n const assertions = collectAssertions(bundle);\n const tableNames = new Set((schema.tables ?? []).map((t) => t.name));\n\n for (const req of manifest.requirements.trace_must_prove) {\n const requiredRole = normalizeAssertionRole(req.role);\n if (requiredRole === null) {\n issues.push({\n category: \"manifest_shape\",\n message: `trace_must_prove[].role \"${String(req.role)}\" is not a valid assertion role; expected \"primary\" or \"control\"`,\n context: { requirement: req },\n });\n continue;\n }\n\n // Candidates share kind + target (and proofId when the requirement pins\n // one). Everything after this is about explaining *why* a candidate does\n // or does not count as evidence.\n const candidates = assertions.filter(\n (a) =>\n a.kind === req.kind &&\n a.target === req.target &&\n (req.proofId === undefined || a.proofId === req.proofId),\n );\n const requiredOrigin = requiredEvidenceOrigin(req.kind, req.evidence);\n const allowedHelpers = allowedHelpersFor(req.kind, req.target, tableNames);\n const originSatisfied = (a: TraceAssertion) =>\n requiredOrigin === \"any\" ||\n (typeof a.emittedBy === \"string\" &&\n (allowedHelpers ?? ASSERTION_HELPERS).includes(a.emittedBy));\n const match = candidates.find(\n (a) =>\n a.eligible !== false &&\n assertionPassed(a) &&\n normalizeAssertionRole(a.role) === requiredRole &&\n originSatisfied(a),\n );\n\n if (match) {\n evidence.push({\n kind: req.kind,\n target: req.target,\n role: requiredRole,\n satisfied: true,\n proofId: match.proofId,\n ...(match.specFile ? { specFile: match.specFile } : {}),\n ...(match.detail ? { detail: match.detail } : {}),\n ...(match.emittedBy ? { emittedBy: match.emittedBy } : {}),\n });\n continue;\n }\n\n evidence.push({\n kind: req.kind,\n target: req.target,\n role: requiredRole,\n satisfied: false,\n });\n\n // Explain the near miss. \"No passing assertion found\" sends people hunting\n // for a missing proof when the real cause is usually one of these three.\n const failed = candidates.filter((a) => a.passed === false);\n const wrongRole = candidates.filter(\n (a) =>\n a.passed === true && normalizeAssertionRole(a.role) !== requiredRole,\n );\n const unverified = candidates.filter(\n (a) =>\n a.passed === true &&\n normalizeAssertionRole(a.role) === requiredRole &&\n !originSatisfied(a),\n );\n\n // A passing right-role assertion exists but its origin disqualifies it —\n // spec-recorded where a helper is demanded, or stamped by a helper that\n // cannot vouch for this kind (assert.httpResponse records the caller's\n // kind verbatim). Its own category: the fix is different and the failure\n // must not read as \"no proof was written\".\n if (unverified.length > 0) {\n const found = unverified[0].emittedBy\n ? `was emitted by ${unverified[0].emittedBy}, which cannot vouch for this claim`\n : \"was recorded directly by spec code (no emittedBy)\";\n const wanted = allowedHelpers\n ? allowedHelpers.join(\" or \")\n : \"an SDK assert.* helper\";\n issues.push({\n category: \"trace_unverified\",\n message:\n `a passing role=\"${requiredRole}\" assertion with kind=\"${req.kind}\" and target=\"${req.target}\" exists but ${found}, ` +\n `and this requirement demands evidence=\"helper\"${req.evidence === undefined ? ` (mandatory for ${req.kind})` : \"\"}. ` +\n `Emit the claim through ${wanted}` +\n `${allowedHelpers === null ? `, or set evidence: \"any\" on the requirement if a bespoke probe is intended` : \"\"}; description: \"${req.description}\"`,\n context: { requirement: req, unverifiedCount: unverified.length },\n });\n continue;\n }\n\n let reason: string;\n if (failed.length > 0) {\n reason =\n `found ${failed.length} matching assertion(s) but they FAILED: ` +\n failed\n .map((a) => `[${a.proofId}] ${a.detail ?? \"(no detail)\"}`)\n .join(\" | \");\n } else if (wrongRole.length > 0) {\n reason =\n `found ${wrongRole.length} passing assertion(s) with role=\"${normalizeAssertionRole(wrongRole[0].role)}\" but this requirement needs role=\"${requiredRole}\". ` +\n `A \"control\" assertion passes by design and only shows the probe could have failed — it cannot stand in for the claim itself. ` +\n `Add the missing ${requiredRole} probe`;\n } else if (req.proofId !== undefined) {\n reason = `no assertion with this kind+target was emitted by proof \"${req.proofId}\" (other proofs may have emitted one; this requirement is pinned)`;\n } else {\n reason = \"no assertion with this kind+target was emitted by any proof\";\n }\n\n issues.push({\n category: \"trace_missing\",\n message: `no passing role=\"${requiredRole}\" assertion found with kind=\"${req.kind}\" and target=\"${req.target}\"; ${reason}; description: \"${req.description}\"`,\n context: { requirement: req, candidateCount: candidates.length },\n });\n }\n\n return { issues, evidence };\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\nexport interface ValidateMissionInput {\n manifest: unknown;\n capabilities: CapabilitiesArtifact;\n schema: SchemaArtifact;\n traces: TraceBundle;\n}\n\nexport function validateMission(input: ValidateMissionInput): ValidationResult {\n const shape = shapeIssues(input.manifest);\n if (shape.length > 0) {\n return {\n missionId:\n (input.manifest as { missionId?: string } | null)?.missionId ??\n \"<invalid>\",\n ok: false,\n issues: shape,\n };\n }\n\n const evidenceIssues = Array.isArray(input.traces)\n ? input.traces.flatMap((t) => traceShapeIssues(t))\n : [\"trace_shape: trace bundle must be an array\"];\n if (\n !evidenceIssues.length &&\n new Set(input.traces.map((t) => t.proofId)).size !== input.traces.length\n )\n evidenceIssues.push(\"duplicate_proof_id\");\n if (evidenceIssues.length)\n return {\n missionId: (input.manifest as MissionManifest).missionId,\n ok: false,\n issues: evidenceIssues.map((message) => ({\n category: \"trace_shape\" as const,\n message,\n })),\n };\n if (\n input.schema.assessed === false ||\n input.capabilities.unclassified?.length\n )\n return {\n missionId: (input.manifest as MissionManifest).missionId,\n ok: false,\n issues: [\n {\n category: \"manifest_shape\",\n message:\n \"discovery_unassessed: regenerate complete schema and capability artifacts\",\n },\n ],\n };\n const manifest = input.manifest as MissionManifest;\n const traceResult = checkTraces(manifest, input.traces, input.schema);\n const issues: ValidationIssue[] = [\n ...checkCapabilities(manifest, input.capabilities),\n ...checkSchema(manifest, input.schema),\n ...checkPolicies(manifest, input.schema),\n ...traceResult.issues,\n ];\n\n return {\n missionId: manifest.missionId,\n ok: issues.length === 0,\n issues,\n evidence: traceResult.evidence,\n };\n}\n","import { Client, type ClientConfig } from \"pg\";\n\n/** Direct database access is restricted to explicitly configured local test stacks. */\nexport async function withLocalPostgres<T>(\n connectionString: string,\n fn: (client: Client) => Promise<T>,\n): Promise<T> {\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch {\n throw new Error(\n \"[PROOF_FAIL] unsafe_database: invalid PostgreSQL connection URL\",\n );\n }\n if (\n ![\"postgres:\", \"postgresql:\"].includes(url.protocol) ||\n ![\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)\n )\n throw new Error(\n \"[PROOF_FAIL] unsafe_database: direct proof connections require a loopback PostgreSQL host\",\n );\n // Reject connection-string query options that could redirect the actual host.\n if ([...url.searchParams.keys()].some((key) => ![\"sslmode\"].includes(key)))\n throw new Error(\n \"[PROOF_FAIL] unsafe_database: unsupported PostgreSQL URL options\",\n );\n const config: ClientConfig = {\n connectionString,\n connectionTimeoutMillis: 5000,\n statement_timeout: 30000,\n query_timeout: 35000,\n application_name: \"proof-harness\",\n };\n const client = new Client(config);\n try {\n await client.connect();\n return await fn(client);\n } finally {\n await client.end();\n }\n}\n\nexport function quotePostgresIdentifier(value: string): string {\n if (typeof value !== \"string\" || !value || value.includes(\"\\0\"))\n throw new Error(\"invalid PostgreSQL identifier\");\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n}\n\nexport interface PostgresCatalogPolicy {\n name: string;\n command: \"SELECT\" | \"INSERT\" | \"UPDATE\" | \"DELETE\" | \"ALL\";\n roles: string[];\n mode: \"PERMISSIVE\" | \"RESTRICTIVE\";\n using: string;\n check: string;\n}\n\nexport interface PostgresCatalogTable {\n schema: string;\n name: string;\n columns: string[];\n rls_enabled: boolean;\n rls_forced: boolean;\n policies: PostgresCatalogPolicy[];\n}\n\n/** One statement takes a consistent snapshot of columns, RLS and complete policies. */\nexport async function readPostgresCatalog(\n client: Client,\n schemas: readonly string[] = [\"public\"],\n) {\n if (!schemas.length || schemas.some((s) => typeof s !== \"string\" || !s))\n throw new Error(\"catalog schemas must be nonempty names\");\n const result = await client.query<PostgresCatalogTable>(\n `\n SELECT n.nspname AS schema, c.relname AS name, c.relrowsecurity AS rls_enabled,\n c.relforcerowsecurity AS rls_forced,\n ARRAY(SELECT a.attname::text FROM pg_attribute a WHERE a.attrelid=c.oid AND a.attnum>0 AND NOT a.attisdropped ORDER BY a.attname) AS columns,\n coalesce((SELECT json_agg(json_build_object(\n 'name',p.polname,'command',CASE p.polcmd WHEN '*' THEN 'ALL' WHEN 'r' THEN 'SELECT' WHEN 'a' THEN 'INSERT' WHEN 'w' THEN 'UPDATE' WHEN 'd' THEN 'DELETE' END,\n 'roles',ARRAY(SELECT CASE WHEN r=0 THEN 'public' ELSE pg_get_userbyid(r)::text END FROM unnest(p.polroles) r ORDER BY 1),\n 'mode',CASE WHEN p.polpermissive THEN 'PERMISSIVE' ELSE 'RESTRICTIVE' END,\n 'using',coalesce(pg_get_expr(p.polqual,p.polrelid),''),\n 'check',coalesce(pg_get_expr(p.polwithcheck,p.polrelid),'')\n ) ORDER BY p.polname) FROM pg_policy p WHERE p.polrelid=c.oid),'[]'::json) AS policies\n FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace\n WHERE c.relkind IN ('r','p') AND n.nspname = ANY($1::text[]) ORDER BY n.nspname,c.relname`,\n [schemas],\n );\n return result.rows;\n}\n\n/** Exercise constraints as the intended role, then roll back on the same connection. */\nexport async function rollbackInsertControl(\n client: Client,\n options: {\n schema?: string;\n table: string;\n role: string;\n payload: Record<string, unknown>;\n },\n) {\n const { table, role, payload, schema = \"public\" } = options;\n const columns = Object.keys(payload);\n if (!columns.length || columns.some((c) => payload[c] === undefined))\n throw new Error(\n \"[PROOF_FAIL] authorization_incomplete: INSERT control needs a JSON payload with defined columns\",\n );\n const target = `${quotePostgresIdentifier(schema)}.${quotePostgresIdentifier(table)}`;\n const selected = columns.map(quotePostgresIdentifier).join(\", \");\n let began = false;\n let failure: unknown;\n let failed = false;\n try {\n await client.query(\"BEGIN\");\n began = true;\n await client.query(`SET LOCAL ROLE ${quotePostgresIdentifier(role)}`);\n const current = await client.query(\"SELECT current_user AS role\");\n if (current.rows[0]?.role !== role)\n throw new Error(\"control did not assume its configured role\");\n // JSON conversion follows PostgreSQL column types; omitted columns retain defaults.\n const inserted = await client.query(\n `INSERT INTO ${target} (${selected}) SELECT ${selected} FROM jsonb_populate_record(NULL::${target}, $1::jsonb)`,\n [JSON.stringify(payload)],\n );\n if (inserted.rowCount !== 1)\n throw new Error(\"control INSERT did not insert exactly one row\");\n // A payload rejected by a deferred FK or constraint trigger is not valid evidence.\n await client.query(\"SET CONSTRAINTS ALL IMMEDIATE\");\n } catch (error) {\n failed = true;\n failure = error;\n }\n if (began) {\n try {\n const rollback = await client.query(\"ROLLBACK\");\n if (rollback.command !== \"ROLLBACK\")\n throw new Error(\"rollback was not acknowledged\");\n } catch (error) {\n throw new AggregateError(\n failed ? [failure, error] : [error],\n \"[PROOF_FAIL] authorization_incomplete: INSERT control rollback failed\",\n );\n }\n }\n if (failed) throw failure;\n}\n"],"mappings":"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","names":["fs","path","crypto","spawnSync","crypto","fs","path","spawnSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/node/source-hash.ts","../src/node/evidence.ts","../src/shared/vocabulary.ts","../src/shared/portable-vocabulary.mjs","../src/shared/trace-types.ts","../src/shared/mission-types.ts","../src/node/validate-mission.ts","../src/node/postgres.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { spawnSync } from \"node:child_process\";\n/** Content identity for a git worktree, including untracked nonignored inputs. */\nexport function sourceHash(\n rootDir = process.cwd(),\n excludedPaths: readonly string[] = [],\n): string | undefined {\n const listed = spawnSync(\n \"git\",\n [\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\"],\n { cwd: rootDir, encoding: \"utf8\" },\n );\n if (listed.status !== 0) return undefined;\n const exclusions = [\n \".proof/traces\",\n \".proof/mutations\",\n \".proof/schema.json\",\n \".proof/capabilities.json\",\n \".proof/drift.json\",\n \".proof/mutation-recovery.json\",\n ...excludedPaths,\n ].map((p) => path.resolve(rootDir, p));\n const digest = crypto.createHash(\"sha256\");\n for (const file of [\n ...new Set(listed.stdout.split(\"\\0\").filter(Boolean)),\n ].sort()) {\n const full = path.join(rootDir, file);\n if (exclusions.some((p) => full === p || full.startsWith(p + path.sep)))\n continue;\n digest.update(file).update(\"\\0\");\n if (!fs.existsSync(full)) {\n digest.update(\"<deleted>\");\n continue;\n }\n const stat = fs.lstatSync(full);\n if (stat.isSymbolicLink()) digest.update(fs.readlinkSync(full));\n else if (stat.isFile())\n digest.update(String(stat.mode & 0o777)).update(fs.readFileSync(full));\n digest.update(\"\\0\");\n }\n return digest.digest(\"hex\");\n}\n","import { sourceHash } from \"./source-hash\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { spawnSync } from \"node:child_process\";\nimport { isProofKind } from \"../shared/vocabulary\";\nimport {\n normalizeAssertionRole,\n normalizeAssertionStatus,\n type TraceArtifact,\n type TraceAssertion,\n} from \"../shared/trace-types\";\n\nexport const ASSERTION_HELPERS = [\n \"assert.tenantIsolation\",\n \"assert.authorization\",\n \"assert.actionSucceeds\",\n \"assert.httpResponse\",\n] as const;\nconst object = (v: unknown): v is Record<string, unknown> =>\n !!v && typeof v === \"object\" && !Array.isArray(v);\nconst text = (v: unknown): v is string =>\n typeof v === \"string\" && v.trim().length > 0;\nconst duration = (v: unknown) =>\n typeof v === \"number\" && Number.isFinite(v) && v >= 0;\nexport function isArtifactId(v: unknown): v is string {\n return (\n typeof v === \"string\" &&\n /^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(v) &&\n v !== \".\" &&\n v !== \"..\"\n );\n}\nexport function assertionPassed(a: TraceAssertion): boolean {\n return (\n a.passed === true &&\n normalizeAssertionStatus(a.status, a.passed) === \"passed\" &&\n normalizeAssertionRole(a.role) !== null\n );\n}\nexport function traceShapeIssues(value: unknown): string[] {\n const issues: string[] = [];\n const bad = (where: string) => issues.push(`trace_shape: invalid ${where}`);\n if (!object(value)) return [\"trace_shape: expected an object\"];\n // Explicit legacy support: v1 and unversioned traces retain the original required fields.\n if (\n value.schemaVersion !== undefined &&\n value.schemaVersion !== 1 &&\n value.schemaVersion !== 2\n )\n bad(\"schemaVersion\");\n if (!isArtifactId(value.proofId)) bad(\"proofId\");\n if (value.missionId !== undefined && !isArtifactId(value.missionId))\n bad(\"missionId\");\n if (!text(value.timestamp) || !Number.isFinite(Date.parse(value.timestamp)))\n bad(\"timestamp\");\n if (!duration(value.durationMs)) bad(\"durationMs\");\n if (typeof value.passed !== \"boolean\") bad(\"passed\");\n if (\n value.mutation !== undefined &&\n (!object(value.mutation) ||\n !isArtifactId(value.mutation.id) ||\n value.mutation.planted !== true)\n )\n bad(\"mutation\");\n for (const key of [\n \"specFile\",\n \"specHash\",\n \"commit\",\n \"sourceHash\",\n \"executionId\",\n ]) {\n if (value[key] !== undefined && !text(value[key])) bad(key);\n }\n if (value.dirty !== undefined && typeof value.dirty !== \"boolean\")\n bad(\"dirty\");\n if (\n value.retry !== undefined &&\n (!Number.isInteger(value.retry) || Number(value.retry) < 0)\n )\n bad(\"retry\");\n if (!Array.isArray(value.steps))\n return [...issues, \"trace_shape: steps must be an array\"];\n for (const [i, step] of value.steps.entries()) {\n if (!object(step)) {\n bad(`steps[${i}]`);\n continue;\n }\n if (\n !isProofKind(step.kind) ||\n !text(step.target) ||\n !text(step.intent) ||\n typeof step.observation !== \"string\" ||\n typeof step.passed !== \"boolean\" ||\n !duration(step.durationMs)\n )\n bad(`steps[${i}] fields`);\n if (value.passed === true && step.passed !== true)\n bad(`steps[${i}] contradicts proof verdict`);\n if (step.assertions === undefined) continue;\n if (!Array.isArray(step.assertions)) {\n bad(`steps[${i}].assertions`);\n continue;\n }\n for (const [j, a] of step.assertions.entries()) {\n const where = `steps[${i}].assertions[${j}]`;\n if (!object(a)) {\n bad(where);\n continue;\n }\n if (\n !isProofKind(a.kind) ||\n !text(a.target) ||\n typeof a.passed !== \"boolean\" ||\n normalizeAssertionRole(a.role) === null\n )\n bad(where);\n if (\n a.operation !== undefined &&\n ![\"select\", \"insert\", \"update\", \"delete\", \"invoke\", \"request\"].includes(\n String(a.operation),\n )\n )\n bad(`${where}.operation`);\n if (\n a.emittedBy !== undefined &&\n !ASSERTION_HELPERS.includes(\n a.emittedBy as (typeof ASSERTION_HELPERS)[number],\n )\n )\n bad(`${where}.emittedBy`);\n if (\n a.status !== undefined &&\n (![\"passed\", \"failed\", \"skipped\", \"incomplete\"].includes(\n String(a.status),\n ) ||\n (a.status === \"passed\") !== a.passed)\n )\n bad(`${where}.status`);\n if (\n step.passed === true &&\n a.passed === false &&\n (a.status === undefined ||\n a.status === \"failed\" ||\n a.status === \"incomplete\")\n )\n bad(`${where} contradicts step verdict`);\n }\n }\n return issues;\n}\nexport function decodeTrace(value: unknown): TraceArtifact {\n const issues = traceShapeIssues(value);\n if (issues.length) throw new Error(`[PROOF_FAIL] ${issues.join(\"; \")}`);\n return value as TraceArtifact;\n}\nexport function decodeTraceBundle(values: readonly unknown[]): TraceArtifact[] {\n const seen = new Set<string>();\n return values.map((value) => {\n const t = decodeTrace(value);\n if (seen.has(t.proofId))\n throw new Error(`[PROOF_FAIL] duplicate_proof_id: ${t.proofId}`);\n seen.add(t.proofId);\n return t;\n });\n}\nexport function fileHash(file: string): string {\n return crypto\n .createHash(\"sha256\")\n .update(fs.readFileSync(file))\n .digest(\"hex\")\n .slice(0, 12);\n}\nexport function readTraceDirectory(\n directory: string,\n {\n rootDir = process.cwd(),\n checkFreshness = true,\n excludedPaths = [] as readonly string[],\n } = {},\n): TraceArtifact[] {\n if (!fs.existsSync(directory)) return [];\n const values: unknown[] = [];\n for (const entry of fs\n .readdirSync(directory, { withFileTypes: true })\n .sort((a, b) => a.name.localeCompare(b.name))) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n let value: unknown;\n try {\n value = JSON.parse(\n fs.readFileSync(path.join(directory, entry.name), \"utf8\"),\n );\n } catch {\n throw new Error(`[PROOF_FAIL] invalid_json: ${entry.name}`);\n }\n if (\n object(value) &&\n value.schemaVersion === 1 &&\n isArtifactId(value.missionId) &&\n typeof value.passed === \"boolean\" &&\n Array.isArray(value.proofs) &&\n Array.isArray(value.traces) &&\n Array.isArray(value.issues) &&\n Array.isArray(value.requirementEvidence) &&\n value.steps === undefined\n )\n continue;\n values.push(value);\n }\n const traces = decodeTraceBundle(values);\n const git = spawnSync(\"git\", [\"rev-parse\", \"HEAD\"], {\n cwd: rootDir,\n encoding: \"utf8\",\n });\n const commit = git.status === 0 ? git.stdout.trim() : undefined;\n const hash = checkFreshness\n ? sourceHash(rootDir, [...excludedPaths, directory])\n : undefined;\n for (const t of traces) {\n if (!checkFreshness || t.mutation) continue;\n if (!text(t.specFile) || !text(t.specHash))\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} lacks spec provenance; rerun proofs`,\n );\n const file = path.resolve(rootDir, t.specFile);\n const relative = path.relative(rootDir, file);\n if (\n relative.startsWith(\"..\") ||\n path.isAbsolute(relative) ||\n !fs.existsSync(file) ||\n fileHash(file) !== t.specHash\n )\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} spec changed or disappeared`,\n );\n if (hash && t.sourceHash !== hash)\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} source contents changed or lack content identity`,\n );\n if (commit && t.commit !== commit)\n throw new Error(\n `[PROOF_FAIL] stale_trace: ${t.proofId} does not describe current commit`,\n );\n }\n return traces;\n}\nexport function baselineAssertions(traces: readonly TraceArtifact[]) {\n return traces\n .filter((t) => t.passed && !t.mutation)\n .flatMap((t) =>\n t.steps\n .filter((s) => s.passed)\n .flatMap((s) =>\n (s.assertions ?? [])\n .filter(assertionPassed)\n .map((a) => ({ ...a, proofId: t.proofId, specFile: t.specFile })),\n ),\n );\n}\n","// Import External Packages\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Closed Vocabularies\n//\n// These are the ONLY allowed values for verb, kind, and rls_classification\n// across the Proof system. Keeping them closed lets the scanner, manifest\n// validator, and trace consumers all speak the same language.\n// ---------------------------------------------------------------------------\n\n/**\n * Verb — the action a capability performs.\n * Used as metadata in `withProof({ verb: ... })`.\n */\nexport const PROOF_VERBS = [\n \"create\",\n \"read\",\n \"update\",\n \"delete\",\n \"invoke\",\n \"transfer\",\n] as const;\n\nexport type ProofVerb = (typeof PROOF_VERBS)[number];\n\n/**\n * Kind — the category of a trace step / assertion.\n * Used in `trace_must_prove` manifest entries and emitted by `assert.*`\n * helpers and `trace.step({ kind })` calls.\n */\nexport const PROOF_KINDS = [\n \"happy_path\",\n \"tenant_isolation\",\n \"authorization\",\n \"validation\",\n \"error_handling\",\n \"idempotency\",\n] as const;\n\nexport type ProofKind = (typeof PROOF_KINDS)[number];\n\n/**\n * RLS classification — how a table is scoped under Row Level Security.\n * Emitted by the migration parser (v1). Defined here so v0.5 helpers and\n * future tooling share the same vocabulary.\n */\nexport const RLS_CLASSIFICATIONS = [\n \"workspace_scoped\",\n \"user_scoped\",\n \"admin_only\",\n \"public_read\",\n \"service_only\",\n \"unclassified\",\n] as const;\n\nexport type RlsClassification = (typeof RLS_CLASSIFICATIONS)[number];\n\n/**\n * The commands a Postgres RLS policy can cover, as the migration parser reports\n * them.\n *\n * `rls_classification` gives a table one label, which is a useful summary and a\n * poor description: real policies are per-command, so a table can be readable\n * only by admins while being writable only by the service role. `ALL` is Postgres'\n * own wildcard and matches every command, which is why a prohibition on any single\n * command has to consider it.\n */\nexport const POLICY_COMMANDS = [\n \"SELECT\",\n \"INSERT\",\n \"UPDATE\",\n \"DELETE\",\n \"ALL\",\n] as const;\n\nexport type PolicyCommand = (typeof POLICY_COMMANDS)[number];\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\nexport function isProofVerb(value: unknown): value is ProofVerb {\n return (\n typeof value === \"string\" &&\n (PROOF_VERBS as readonly string[]).includes(value)\n );\n}\n\nexport function isProofKind(value: unknown): value is ProofKind {\n return (\n typeof value === \"string\" &&\n (PROOF_KINDS as readonly string[]).includes(value)\n );\n}\n\nexport function isRlsClassification(\n value: unknown,\n): value is RlsClassification {\n return (\n typeof value === \"string\" &&\n (RLS_CLASSIFICATIONS as readonly string[]).includes(value)\n );\n}\n\nexport function isPolicyCommand(value: unknown): value is PolicyCommand {\n return (\n typeof value === \"string\" &&\n (POLICY_COMMANDS as readonly string[]).includes(value)\n );\n}\n","// Import External Packages\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Portable proof vocabulary — the single source of truth for closed value\n// lists that BOTH the TypeScript SDK and the dependency-free Node scripts\n// (`proof_verify.mjs`, `proof_drift.mjs`, `proof_coverage.mjs`) must agree on.\n//\n// Why a .mjs file inside a TypeScript package: those scripts run under plain\n// `node` with no compile step and must stay fast-starting, so they cannot\n// import `vocabulary.ts`. Everything here is therefore plain JS, and the\n// TypeScript side derives its union types from these arrays\n// (`(typeof ACTION_CHANGE_KINDS)[number]` in mission-types.ts), so adding or\n// renaming a value in exactly one place updates the validators, the drift\n// script, coverage, and the types together. The JSDoc `@type {const}` casts\n// are what preserve the literal element types for that derivation.\n//\n// Only vocabularies needed by the .mjs scripts live here; TS-only\n// vocabularies stay in `vocabulary.ts`.\n// ---------------------------------------------------------------------------\n\n/**\n * Change kinds an `expectedChanges.actions[]` declaration may allow, and the\n * facets a modified action decomposes into. Like tables, EVERY facet of a\n * change must be declared for the change to be covered — a mission expecting\n * an invariant declaration to change does not thereby authorize an RBAC/auth\n * middleware change on the same action.\n *\n * - `middleware_changed`: a scanner-observed guard (auth / tenantIsolation\n * / rbac) appeared, disappeared, or flipped.\n * - `invariants_changed`: the `withProof({ invariants })` list changed.\n * - `service_role_mutations_changed`: the set of tables the action writes\n * through the service-role client changed.\n * - `metadata_changed`: verb, object, acceptsWorkspaceId, internalOnly, or\n * usesDirectUpdateTag changed.\n */\nexport const ACTION_CHANGE_KINDS = /** @type {const} */ ([\n \"added\",\n \"removed\",\n \"middleware_changed\",\n \"invariants_changed\",\n \"service_role_mutations_changed\",\n \"metadata_changed\",\n]);\n\n/**\n * Change kinds an `expectedChanges.tables[]` declaration may allow, and the\n * facets a modified table decomposes into. Same coverage rule as actions:\n * declaring `columns_added` on a table does not authorize a policy change\n * that rode along on the same table.\n */\nexport const TABLE_CHANGE_KINDS = /** @type {const} */ ([\n \"added\",\n \"removed\",\n \"columns_added\",\n \"columns_removed\",\n \"rls_classification_changed\",\n \"policies_changed\",\n]);\n\n/**\n * The only helpers whose `emittedBy` stamp counts as tenant-isolation\n * evidence, split by target.\n *\n * Identity matters because helpers differ in who chooses the `kind`:\n * `assert.httpResponse` records the caller's kind verbatim, so its stamp on a\n * `tenant_isolation` assertion would prove only that an HTTP probe ran — not\n * that anything checked cross-tenant visibility. Isolation OF A TABLE is\n * exactly what `assert.tenantIsolation` proves, so table targets accept only\n * it; action-ref targets also accept `assert.authorization`, whose action\n * probes record the cross-tenant invariant they actually establish.\n */\nexport const TENANT_ISOLATION_TABLE_HELPERS = /** @type {const} */ ([\n \"assert.tenantIsolation\",\n]);\n\n/** Allowed stamps for tenant-isolation claims against non-table targets (action refs). */\nexport const TENANT_ISOLATION_ACTION_HELPERS = /** @type {const} */ ([\n \"assert.tenantIsolation\",\n \"assert.authorization\",\n]);\n","// Import External Packages\n// Import Local Imports\nimport type { ProofKind } from \"./vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Trace artifact types\n//\n// These are the durable shape written to `.proof/traces/<proofId>.json`.\n//\n// Two separate identifiers on purpose:\n// - `proofId` — per-file id; what _this_ proof verifies (e.g. \"auth-login-admin\",\n// \"workspace-invite-tenant-isolation\"). The filename is keyed on it,\n// so proofs never collide.\n// - `missionId` — optional external ticket reference (e.g. \"M-042\"). A single mission\n// typically has multiple proofs (happy_path + tenant_isolation +\n// authorization), each with its own file. The v1 mission-manifest\n// validator groups by `missionId` and checks that every\n// `trace_must_prove` entry has at least one matching passing step\n// across the group.\n// ---------------------------------------------------------------------------\n\n/**\n * The evidential role an assertion plays.\n *\n * - `primary` — the claim the proof exists to make. For an authorization or\n * tenant-isolation proof that is a refusal (\"the outsider could\n * NOT read the row\"); for a happy_path proof it is a success\n * (\"the user reached their workspace\"). This is what a mission's\n * `trace_must_prove` entry matches by default.\n * - `control` — a companion assertion proving the primary probe was capable of\n * failing at all: \"the operation that SHOULD be permitted still\n * succeeded\", or \"the row the primary probe looked for genuinely\n * exists and is readable by someone.\"\n *\n * The distinction is load-bearing, not cosmetic. A refusal probe alone cannot\n * tell \"RLS correctly hid this row\" apart from \"there was no row / the grant\n * denies everyone / the table name was a typo\" — every one of those yields zero\n * rows and would record a passing assertion. The control is what makes the zero\n * mean something.\n *\n * Because the validator matches `trace_must_prove` on `{ kind, target,\n * passed: true }`, controls MUST be labelled: otherwise a control (which by\n * design passes) could satisfy a requirement all on its own and the primary\n * probe would never be checked at all.\n *\n * Naming note: an earlier draft called these `negative` / `positive_control`.\n * That reads backwards for `happy_path`, whose primary claim is a success, so\n * the roles are named by evidential function instead. Both older names are\n * accepted as deprecated aliases wherever a role is parsed.\n */\nexport type AssertionRole = \"primary\" | \"control\";\n\n/**\n * Concrete operation exercised by an assertion. Mutation inventory keys claims\n * by kind + target + operation so a SELECT mutation cannot silently stand in\n * for an untested UPDATE/DELETE or action invocation.\n */\nexport type ProofOperation =\n | \"select\"\n | \"insert\"\n | \"update\"\n | \"delete\"\n | \"invoke\"\n | \"request\";\n\n/** Deprecated role spellings accepted on input and normalized to AssertionRole. */\nexport const ASSERTION_ROLE_ALIASES: Readonly<Record<string, AssertionRole>> = {\n negative: \"primary\",\n positive_control: \"control\",\n};\n\n/**\n * Normalize a role from an artifact or manifest. `undefined` means `primary`:\n * traces written before schemaVersion 2 carry no role, and their assertions\n * were all primary claims.\n */\nexport function normalizeAssertionRole(role: unknown): AssertionRole | null {\n if (role === undefined || role === null) return \"primary\";\n if (role === \"primary\" || role === \"control\") return role;\n if (typeof role === \"string\" && role in ASSERTION_ROLE_ALIASES) {\n return ASSERTION_ROLE_ALIASES[role];\n }\n return null;\n}\n\n/**\n * What an assertion concluded.\n *\n * `passed` / `failed` are the two verdicts a boolean can carry. The other two\n * exist because \"we did not find out\" is a real outcome that a boolean has to\n * round to one side or the other, and rounding it to `false` is indistinguishable\n * from a genuine failure while rounding it to `true` is how a suite quietly stops\n * proving anything:\n *\n * - `incomplete` — the probe ran but could not reach a verdict (a precondition\n * was missing, a dependency was unavailable).\n * - `skipped` — the probe was deliberately not run, e.g. a tenant-isolation\n * direction with no fixture rows on that side.\n *\n * Neither counts as evidence: both carry `passed: false`, so neither can satisfy\n * a mission requirement. The status is what lets a consumer tell them apart from\n * a real failure, and stops a skip from disappearing into \"one fewer assertion\".\n */\nexport type AssertionStatus = \"passed\" | \"failed\" | \"incomplete\" | \"skipped\";\n\n/** Derive the status of an assertion that predates the field. */\nexport function normalizeAssertionStatus(\n status: unknown,\n passed: boolean,\n): AssertionStatus {\n if (\n status === \"passed\" ||\n status === \"failed\" ||\n status === \"incomplete\" ||\n status === \"skipped\"\n ) {\n return status;\n }\n return passed ? \"passed\" : \"failed\";\n}\n\n/**\n * A single structured assertion recorded during a step. Produced by the\n * `assert.*` helpers via `recordAssertion(...)`. Hand-written `expect(...)`\n * calls inside a step do NOT appear here — only helper-driven assertions do,\n * because the v1 mission-manifest validator matches on\n * `{ kind, target, passed: true, role }` and needs a closed, machine-readable\n * surface.\n */\nexport interface TraceAssertion {\n /** Category from the closed `PROOF_KINDS` vocabulary. */\n kind: ProofKind;\n /** The thing asserted against: table, route, action, etc. */\n target: string;\n /** Concrete operation the probe exercised. */\n operation?: ProofOperation;\n /**\n * Whether this specific assertion held. Remains the authoritative field for\n * the validator and for consumers: only `true` is evidence.\n */\n passed: boolean;\n /**\n * Why `passed` has the value it has. Omitted is derived from `passed`, so\n * traces written before this field stay readable.\n */\n status?: AssertionStatus;\n /**\n * Evidential role. Omitted is treated as `\"primary\"` by the validator for\n * backward compatibility with traces written before schemaVersion 2.\n */\n role?: AssertionRole;\n /** Optional human-readable note for failure triage. */\n detail?: string;\n /**\n * Which SDK helper produced this assertion, e.g. `\"assert.tenantIsolation\"`.\n *\n * Stamped by the SDK itself through an internal channel that the public\n * `recordAssertion(...)` entry point cannot reach: a value supplied by spec\n * code is stripped before the assertion is recorded. Absent means the\n * assertion was recorded directly by a spec via `recordAssertion(...)`.\n *\n * The distinction is evidential. A helper-emitted assertion went through the\n * helper's vacuity controls, ground-truth re-reads, and role bookkeeping; a\n * spec-recorded assertion asserts whatever the spec chose to write into it.\n * The mission validator therefore requires helper provenance for\n * `tenant_isolation` requirements (a hand-rolled isolation probe has none of\n * `assert.tenantIsolation`'s protections), and a mission can demand it for\n * any requirement via `trace_must_prove[].evidence: \"helper\"`.\n *\n * For `tenant_isolation` the stamp is also identity-checked, not merely\n * present-checked — see TENANT_ISOLATION_TABLE_HELPERS. `assert.httpResponse`\n * records whatever `kind` its caller passes, so \"some helper emitted this\"\n * is not the same claim as \"the isolation probe ran\".\n */\n emittedBy?: string;\n}\n\n/**\n * The only helpers whose stamp counts as tenant-isolation evidence, split by\n * target (tables are stricter than action refs). The single runtime source is\n * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the\n * dependency-free Node scripts (`proof_verify.mjs`, `proof_coverage.mjs`)\n * import directly; see there for the identity rationale.\n */\nexport {\n TENANT_ISOLATION_ACTION_HELPERS,\n TENANT_ISOLATION_TABLE_HELPERS,\n} from \"./portable-vocabulary.mjs\";\n\n/**\n * A single step inside a proof.\n * Produced by `t.step({ intent, kind, target }, async () => { ... })`.\n */\nexport interface TraceStep {\n /** Human-readable description of what the step attempted. */\n intent: string;\n /** Category of the step, from closed vocabulary. */\n kind: ProofKind;\n /** The thing the step targets: action name, table, route, etc. */\n target: string;\n /** What the step observed (returned by the step callback). */\n observation: string;\n /** Whether the step's assertions held. */\n passed: boolean;\n /** Optional: who the step acted as (email, role, seed identifier). */\n actor?: string;\n /** Optional: which workspace the step operated in. */\n workspaceId?: string;\n /** Wall-clock duration of the step callback. */\n durationMs: number;\n /** On failure, the error message captured. */\n error?: string;\n /**\n * Structured assertions recorded by `assert.*` helpers during this step.\n * Empty or omitted for steps that only use `expect(...)` directly.\n * The v1 mission-manifest validator matches `trace_must_prove` entries\n * against this array.\n */\n assertions?: TraceAssertion[];\n}\n\n/**\n * Current per-proof trace artifact schema version.\n *\n * 2 — adds `role` and `status` to assertions and the `schemaVersion` /\n * `specFile` / `specHash` / `commit` / `dirty` provenance fields below.\n * Consumers written against v1 keep working: every v1 field is still present\n * with the same meaning, and both new assertion fields are derivable when\n * absent.\n *\n * `commit`, `dirty`, and `mutation` arrived after v2 shipped and did not bump\n * it: they are optional additions, and the contract's rule is that additive\n * fields do not move a version. A consumer that needs them must treat absence\n * as \"unknown\" anyway, since normal runs have no mutation and a non-git\n * checkout cannot supply code provenance.\n */\nexport const TRACE_ARTIFACT_SCHEMA_VERSION = 2;\n\n/**\n * Provenance attached only when the mutation harness deliberately plants a\n * defect. A red mutation trace means the check detected the named defect; it\n * must never be mistaken for a real application regression.\n */\nexport interface TraceMutation {\n /** Stable mutation identifier from the repository-owned mutation catalog. */\n id: string;\n /** Always true; makes the intentional nature explicit to copied consumers. */\n planted: true;\n}\n\n/**\n * A trace artifact — one proof's worth of evidence.\n * Written atomically to `.proof/traces/<proofId>.json` when the proof\n * completes (pass or fail).\n */\nexport interface TraceArtifact {\n /**\n * Pinned to TRACE_ARTIFACT_SCHEMA_VERSION. Absent on traces written by\n * SDK versions before provenance was added.\n */\n schemaVersion?: number;\n /**\n * Repo-relative path of the spec file that produced this trace, e.g.\n * `e2e/proofs/workspace-invite.proof.ts`. Lets a reviewer go from a claim\n * straight to the code that made it.\n */\n specFile?: string;\n /**\n * First 12 hex chars of the SHA-256 of the spec file's contents. Two runs\n * claiming the same proof with different hashes were produced by different\n * code — which is the whole question when a previously-red proof turns\n * green.\n */\n specHash?: string;\n /**\n * Full SHA of the commit that was checked out while the proof ran. `specHash`\n * pins the spec that made the claim; this pins the application it was made\n * against, which is what lets an external ledger say \"red at X, green at Y\".\n *\n * Absent outside a git checkout. On a CI `pull_request` run this is the\n * ephemeral merge commit the checkout created — accurate about what was\n * tested, but not a commit you can return to later.\n */\n commit?: string;\n /**\n * Whether the working tree had uncommitted changes when the proof ran. A pass\n * from a dirty tree is evidence about code that exists nowhere but one laptop,\n * so a consumer gating on traces should be able to refuse it. Absent when it\n * could not be determined, which is not the same as `false`.\n */\n dirty?: boolean;\n /** Hash of git-tracked and untracked nonignored source inputs. */\n sourceHash?: string;\n /** Writer identity for distinguishing retries from duplicate proof IDs. */\n executionId?: string;\n retry?: number;\n /**\n * Present only for intentional red runs produced by `proof:mutate`.\n * Stored inside the trace so provenance survives artifact renames/copies.\n */\n mutation?: TraceMutation;\n /**\n * Per-file identifier for this proof. Used as the filename stem and as the\n * join key when the v1 mission-manifest validator looks for a matching\n * passing step for a `trace_must_prove` entry.\n */\n proofId: string;\n /**\n * Optional reference to the external mission (ticket) this proof belongs\n * to. Multiple proofs share one `missionId`. Absent in v0.5 local runs\n * that are not tied to a mission.\n */\n missionId?: string;\n /** ISO 8601 timestamp of proof start. */\n timestamp: string;\n /** Total wall-clock duration across all steps. */\n durationMs: number;\n /** True only if every step passed. */\n passed: boolean;\n /** Ordered list of steps as they were executed. */\n steps: TraceStep[];\n}\n\n/**\n * Options accepted by `trace.proof(...)`. A bare string is treated as\n * `{ proofId: string }` — the common v0.5 case where no mission is attached.\n */\nexport interface ProofOptions {\n proofId: string;\n missionId?: string;\n}\n\n/**\n * The handle passed to a proof callback. Provides the step recorder.\n */\nexport interface TraceRecorder {\n step<T extends StepResult | void>(\n opts: StepOptions,\n fn: () => Promise<T>,\n ): Promise<T>;\n}\n\nexport interface StepOptions {\n intent: string;\n kind: ProofKind;\n target: string;\n actor?: string;\n workspaceId?: string;\n}\n\n/**\n * Optional payload returned by a step callback. If the callback returns\n * `void` the step is still recorded with `observation: \"\"`.\n */\nexport interface StepResult {\n observation?: string;\n}\n","// ---------------------------------------------------------------------------\n// Mission manifest types.\n//\n// A *mission manifest* is the JSON artifact an external orchestrator or a\n// developer in manual mode writes to `.proof/current-mission.json` to\n// tell `pnpm proof:verify` what the PR must prove. The validator at\n// `src/node/validate-mission.ts` cross-references each\n// requirement against the fresh `.proof/capabilities.json`, `.proof/schema.json`,\n// and the aggregated trace.\n//\n// Shape is pinned by contract — additive changes bump nothing, breaking\n// changes bump `schemaVersion` at the top of the manifest. Consumers should\n// assert `schemaVersion === 1` and fail loudly otherwise.\n// ---------------------------------------------------------------------------\n\nimport type { AssertionRole } from \"./trace-types\";\nimport {\n ACTION_CHANGE_KINDS,\n TABLE_CHANGE_KINDS,\n} from \"./portable-vocabulary.mjs\";\nimport type {\n PolicyCommand,\n ProofKind,\n ProofVerb,\n RlsClassification,\n} from \"./vocabulary\";\n\n/**\n * Manifest schema version. Deliberately still 1 after the July 2026 validator\n * work: `trace_must_prove[].role` and `.proofId` are OPTIONAL additions, so\n * every v1 manifest is still shape-valid and consumers need not regenerate\n * anything. Note that validation did get stricter — a `control` assertion can\n * no longer satisfy a requirement — so a manifest that passed on a vacuous\n * probe before may now (correctly) fail. That is a behaviour tightening, not a\n * shape change, and bumping the version would have forced a rewrite of every\n * existing manifest for no benefit.\n */\nexport const MISSION_MANIFEST_SCHEMA_VERSION = 1;\n\nexport interface CapabilityRequirement {\n /** Matches `createAction({ functionName: <name> })` exactly. */\n name: string;\n /** Module folder name (e.g. `\"issues\"`, not `\"__business-logic/issues\"`). */\n module: string;\n /** One of PROOF_VERBS. */\n verb: ProofVerb;\n /** Free-form noun, lowercased by convention (e.g. `\"issue\"`, `\"workspace\"`). */\n object: string;\n /** Optional: invariants that the action's `withProof({ invariants })` must advertise. */\n invariants?: readonly string[];\n}\n\nexport interface SchemaRequirement {\n /** Unqualified public-schema table name. */\n table: string;\n /** Columns that must exist on the table. Order-insensitive. */\n required_columns: readonly string[];\n /** One of RLS_CLASSIFICATIONS. */\n rls_classification: RlsClassification;\n}\n\n/**\n * \"No policy on this table targets this role for these commands.\"\n *\n * Exists because `rls_classification` is one label per table and Postgres RLS is\n * per-command: `audit_logs` being \"admin readable, service writable\" cannot be\n * said with a single label, and the label a manifest picks would have to be a\n * lie in one direction or the other.\n *\n * Checks reachability, not the predicate — `USING` and `WITH CHECK` are not read.\n * A policy `TO authenticated USING (is_super_admin())` counts as targeting\n * `authenticated`, so \"this role may act only under condition X\" is not\n * expressible here and belongs in `trace_must_prove`, where something exercises\n * it. Write prohibitions (\"members never INSERT into the audit log\") are the\n * natural fit.\n *\n * Deliberately only the prohibition, never \"a policy must exist\". A policy's\n * existence is weak evidence — it says a rule was written, not that it works —\n * and the thing that makes a permission real is a passing proof, which belongs in\n * `trace_must_prove`. A prohibition is different: it is a claim that can fail\n * dangerously and silently, and it is checkable from the migrations alone.\n */\nexport interface PolicyProhibition {\n /** Unqualified public-schema table name. */\n table: string;\n /**\n * Database role that must not be granted access, e.g. `\"authenticated\"` or\n * `\"anon\"`. A policy with no `TO` clause defaults to `PUBLIC` in Postgres and\n * therefore counts as granting every role.\n */\n role: string;\n /**\n * Commands the prohibition covers. Omit to mean \"any command\". A policy\n * declared `FOR ALL` matches whatever is listed here, because that is what it\n * does in the database.\n */\n commands?: readonly PolicyCommand[];\n /** Human-readable statement of the rule, echoed in the failure message. */\n description: string;\n}\n\nexport interface TraceRequirement {\n /** One of PROOF_KINDS. */\n kind: ProofKind;\n /**\n * Free-form identifier the assertion emits. Must match an assertion's\n * `target` exactly (e.g. table name for `tenant_isolation`, capability\n * name for `authorization`).\n */\n target: string;\n /** Human-readable statement of what should be proved. */\n description: string;\n /**\n * Which evidential role must satisfy this requirement. Defaults to\n * `\"primary\"`, which is almost always what you want: a `\"control\"`\n * assertion passes by design and proves only that the probe was capable of\n * failing, so it must never stand in for the claim itself.\n *\n * Accepts the deprecated spellings `\"negative\"` / `\"positive_control\"`.\n */\n role?: AssertionRole;\n /**\n * Optional: restrict matching to assertions emitted by this proof.\n *\n * By default a requirement may be satisfied by a matching assertion from\n * ANY proof in the run. That is convenient but means an unrelated spec can\n * unknowingly satisfy a mission's requirement. Set this when the mission\n * cares that a SPECIFIC proof made the claim. Either way, the aggregated\n * trace records which proof actually supplied the evidence.\n */\n proofId?: string;\n /**\n * What origin of assertion may satisfy this requirement.\n *\n * - `\"helper\"` — only assertions stamped `emittedBy` by an SDK `assert.*`\n * helper count. Helper assertions went through the SDK's vacuity\n * controls and ground-truth re-reads; a spec-recorded assertion asserts\n * whatever the spec wrote into it.\n * - `\"any\"` — spec-recorded assertions (raw `recordAssertion`) count too.\n * Those claims remain backstopped by the mutation inventory, which is\n * why bespoke probes (RPC grants, column guards) stay expressible.\n *\n * `tenant_isolation` is always `\"helper\"`, and the stamp is additionally\n * identity-checked (see TENANT_ISOLATION_TABLE_HELPERS in trace-types):\n * declaring `evidence: \"any\"` for it is a `manifest_shape` error, so the\n * mission gate and strict coverage can never disagree about whether a raw\n * isolation assertion is admissible. Every other kind defaults to `\"any\"`.\n */\n evidence?: EvidenceOrigin;\n}\n\n/** Origin of assertion allowed to satisfy a `trace_must_prove` entry. */\nexport type EvidenceOrigin = \"helper\" | \"any\";\n\n/**\n * The evidence origin a requirement demands, applying the kind-based default.\n * Consumed directly by the compiled validator used by `proof-harness verify`.\n */\nexport function requiredEvidenceOrigin(\n kind: ProofKind,\n evidence: EvidenceOrigin | undefined,\n): EvidenceOrigin {\n // Tenant isolation is helper-only regardless of what the manifest says: the\n // shape validator rejects `evidence: \"any\"` for it, and this belt-and-braces\n // keeps the matching honest even for a manifest that skipped shape checks.\n if (kind === \"tenant_isolation\") return \"helper\";\n return evidence ?? \"any\";\n}\n\nexport interface MissionRequirements {\n capabilities_must_exist: readonly CapabilityRequirement[];\n schema_must_contain: readonly SchemaRequirement[];\n trace_must_prove: readonly TraceRequirement[];\n /**\n * Optional, so every existing v1 manifest stays shape-valid. Omitting it means\n * \"no policy-level claims\", not \"policies are fine\".\n */\n policies_must_not_allow?: readonly PolicyProhibition[];\n}\n\n/**\n * Change kinds an action declaration may allow. Like tables, a modified\n * action is decomposed into facets and EVERY facet must be covered — a\n * mission expecting an invariant declaration to change does not thereby\n * authorize an RBAC/auth middleware change on the same action.\n *\n * Derived from the single runtime source in\n * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the\n * dependency-free Node scripts import directly; see there for the per-facet\n * meanings.\n */\nexport type ActionChangeKind = (typeof ACTION_CHANGE_KINDS)[number];\n\n/**\n * Change kinds a table declaration may allow. A \"modified\" table entry is\n * decomposed into facets, and EVERY facet must be covered by the declaration —\n * declaring `columns_added` on a table does not authorize a policy change on\n * the same table. Derived from `portable-vocabulary.mjs`.\n */\nexport type TableChangeKind = (typeof TABLE_CHANGE_KINDS)[number];\n\n// Re-exported so TS consumers get the runtime lists from the same import\n// site as the types they parameterize.\nexport { ACTION_CHANGE_KINDS, TABLE_CHANGE_KINDS };\n\n/** Exact-ref action declaration. A bare string ref means \"any change\". */\nexport interface ExpectedActionChange {\n /** `<module>:<name>`, the same ref capabilities.json uses. */\n ref: string;\n /** Omit for \"any change to this action\". */\n changes?: readonly ActionChangeKind[];\n}\n\n/** Table declaration. A bare string name means \"any tracked change\". */\nexport interface ExpectedTableChange {\n name: string;\n /** Omit for \"any tracked change to this table\". */\n changes?: readonly TableChangeKind[];\n}\n\n/**\n * The proof-surface change budget a mission grants its PR.\n *\n * Everything else in the manifest states what MUST exist; this block bounds\n * what else MAY change on the surfaces drift tracks. Read by\n * the package drift engine, which diffs the regenerated\n * `capabilities.json` / `schema.json` / `package.json` / lockfile against the\n * PR's base and fails (`drift_undeclared`) on a high-severity delta outside\n * the declared budget.\n *\n * Scope is deliberately narrow-by-default:\n *\n * - `modules` covers only ADDED actions in the named module (the\n * new-module convenience). Modifying or removing an existing action is\n * never authorized by a module declaration — a mission that expects a\n * guard or signature change on an existing action must name the exact\n * ref in `actions`, so \"add a small feature\" can never quietly license\n * weakening an unrelated action in the same module.\n * - String shorthands (\"issues\" in `tables`, \"issues:createIssue\" in\n * `actions`) mean \"any change\" and are the coarse form; the object forms\n * narrow to specific change kinds.\n *\n * Omitting the block keeps drift report-only, so every existing v1 manifest\n * behaves exactly as before.\n */\nexport interface ExpectedChanges {\n /**\n * Module leaf names (e.g. `\"issues\"`) in which NEW actions may appear.\n * Does not authorize modification or removal of existing actions.\n */\n modules?: readonly string[];\n /** Exact action refs that may change; see {@link ExpectedActionChange}. */\n actions?: readonly (string | ExpectedActionChange)[];\n /** Tables that may change; see {@link ExpectedTableChange}. */\n tables?: readonly (string | ExpectedTableChange)[];\n /**\n * Runtime dependencies (`package.json` `dependencies`) that may be added,\n * removed, or version-changed. Every runtime dependency delta is\n * high-severity — a swap or downgrade changes shipped behavior as surely as\n * an addition.\n */\n dependencies?: readonly string[];\n /**\n * Expect a lockfile-only resolution change (pnpm-lock.yaml differs while\n * the package.json dependency surface does not). Rare and suspicious by\n * default, hence explicitly declared rather than inferred.\n */\n lockfile?: boolean;\n}\n\nexport interface MissionManifest {\n /** Pinned to MISSION_MANIFEST_SCHEMA_VERSION. */\n schemaVersion: number;\n /** Ticket id from the consumer's kanban (e.g. `\"M-042\"`). */\n missionId: string;\n /** Short human-readable title. */\n missionTitle: string;\n /** Product repo slug (informational). */\n productRepo?: string;\n /** PR branch name (informational). */\n prBranch?: string;\n /** ISO-8601 timestamp the manifest was generated. */\n createdAt: string;\n requirements: MissionRequirements;\n /**\n * Optional drift bound; see {@link ExpectedChanges}. Absent means the drift\n * report is informational only.\n */\n expectedChanges?: ExpectedChanges;\n /** Free-form notes for reviewers / the coding agent. */\n acceptanceNotes?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Validation result types\n// ---------------------------------------------------------------------------\n\nexport type ValidationCategory =\n | \"trace_shape\"\n | \"capability_missing\"\n | \"capability_mismatch\"\n | \"schema_missing\"\n | \"schema_column_missing\"\n | \"schema_rls_mismatch\"\n /** A policy grants access the manifest declared must not exist. */\n | \"schema_policy_permits\"\n | \"trace_missing\"\n /**\n * A passing assertion matched the requirement but was recorded directly by\n * spec code (`recordAssertion`) where the requirement demands an SDK-helper\n * origin (`evidence: \"helper\"`, the default for `tenant_isolation`).\n */\n | \"trace_unverified\"\n | \"manifest_shape\"\n /** The Playwright run itself exited non-zero. Reported by the CLI, not the validator. */\n | \"proof_run\"\n /** The run produced no trace artifacts at all, so there is nothing to validate. */\n | \"no_traces\"\n /**\n * A trace in the directory was recorded against a different commit than the\n * current run — leftover evidence that cannot speak for the code under review.\n * Reported by the CLI, which knows the run's commit; the validator sees only\n * the bundle it is handed.\n */\n | \"stale_trace\";\n\n/**\n * Which proof supplied the evidence for a satisfied `trace_must_prove` entry.\n *\n * Recorded for every requirement — satisfied or not — so a reviewer can audit\n * the mapping instead of trusting that \"0 issues\" meant the right proof made\n * the claim. Requirements are matched against all assertions in the run, so\n * without this the attribution would be invisible.\n */\nexport interface RequirementEvidence {\n kind: ProofKind;\n target: string;\n role: AssertionRole;\n satisfied: boolean;\n /** proofId of the trace whose assertion satisfied it, when satisfied. */\n proofId?: string;\n /** Spec file that produced that trace, when known. */\n specFile?: string;\n /** The satisfying assertion's `detail`, verbatim. */\n detail?: string;\n /**\n * The satisfying assertion's helper provenance, when it has one. Absent\n * means the evidence was recorded directly by spec code.\n */\n emittedBy?: string;\n}\n\nexport interface ValidationIssue {\n category: ValidationCategory;\n /** Human-readable summary including expected vs found. */\n message: string;\n /** Structured context for machine consumers (optional). */\n context?: Record<string, unknown>;\n}\n\nexport interface ValidationResult {\n missionId: string;\n ok: boolean;\n issues: ValidationIssue[];\n /** Attribution for every `trace_must_prove` entry, in manifest order. */\n evidence?: RequirementEvidence[];\n}\n","import {\n ASSERTION_HELPERS,\n assertionPassed,\n traceShapeIssues,\n} from \"./evidence\";\n// ---------------------------------------------------------------------------\n// Mission manifest validator.\n//\n// Three-way cross-reference:\n// 1. Manifest is shape-correct (schemaVersion + required fields).\n// 2. Every `capabilities_must_exist[i]` is found in `.proof/capabilities.json`.\n// 3. Every `schema_must_contain[i]` is found in `.proof/schema.json` with\n// matching required_columns and rls_classification.\n// 4. No policy in `.proof/schema.json` grants what a\n// `policies_must_not_allow[i]` entry forbids. This is the per-command view\n// that `rls_classification`'s single label per table cannot express.\n// 5. Every `trace_must_prove[i]` has at least one passing assertion in the\n// aggregated trace with matching `kind` + `target` + `role` (default\n// `primary`), optionally pinned to a specific `proofId`. Which proof\n// supplied each piece of evidence is reported back in `evidence` so the\n// attribution is auditable rather than implicit.\n//\n// Produces a structured ValidationResult; the CLI driver is responsible for\n// formatting to `[PROOF_FAIL]` lines and exit codes.\n//\n// Intentionally zero runtime dependencies — this file runs under Node during\n// `pnpm proof:verify` and inside CI, and must stay fast-starting.\n// ---------------------------------------------------------------------------\n\nimport {\n ACTION_CHANGE_KINDS,\n MISSION_MANIFEST_SCHEMA_VERSION,\n requiredEvidenceOrigin,\n TABLE_CHANGE_KINDS,\n type MissionManifest,\n type RequirementEvidence,\n type ValidationIssue,\n type ValidationResult,\n} from \"../shared/mission-types\";\nimport {\n isPolicyCommand,\n isProofKind,\n isProofVerb,\n isRlsClassification,\n} from \"../shared/vocabulary\";\nimport {\n normalizeAssertionRole,\n TENANT_ISOLATION_ACTION_HELPERS,\n TENANT_ISOLATION_TABLE_HELPERS,\n type TraceArtifact,\n type TraceAssertion,\n} from \"../shared/trace-types\";\n\n// ---------------------------------------------------------------------------\n// External artifact shapes (only the fields we need)\n// ---------------------------------------------------------------------------\n\nexport interface CapabilitiesArtifact {\n schemaVersion: number;\n capabilities: Array<{\n name: string;\n module: string;\n /** Literal exported wrapper function used by the generated invoke registry. */\n exportName?: string | null;\n verb: string | null;\n object: string | null;\n invariants: readonly string[];\n /** Derived scanner signal; absent on older additive v1 artifacts. */\n acceptsWorkspaceId?: boolean;\n /** Internal BOT/server-only plumbing is not a user-invoked boundary. */\n internalOnly?: boolean;\n /** Direct next/cache updateTag is not safe through the proof route. */\n usesDirectUpdateTag?: boolean;\n /** Concrete writes issued through createSupabaseServiceClient(). */\n serviceRoleMutations?: ReadonlyArray<{\n table: string;\n operation: \"insert\" | \"upsert\" | \"update\" | \"delete\";\n }>;\n /** Direct supported privileged Auth API calls, separate from SQL writes. */\n serviceRoleAuthOperations?: readonly \"deleteUser\"[];\n /** Conservative RPC assessment; does not infer SQL tables, grants or read-only behavior. */\n serviceRoleRpcCalls?: ReadonlyArray<{\n function: string;\n assessment: \"potential_privileged_write\";\n }>;\n /** Relevant guards observed in the action pipeline. */\n middleware?: {\n auth: boolean;\n tenantIsolation: boolean;\n rbac: boolean;\n };\n file: string;\n }>;\n unclassified?: readonly unknown[];\n}\n\nexport interface SchemaPolicy {\n name: string;\n /** One of POLICY_COMMANDS as the parser reports it. */\n command: string;\n /** Roles from the policy's `TO` clause. Empty means no clause, i.e. PUBLIC. */\n roles: readonly string[];\n}\n\nexport interface SchemaArtifact {\n assessed?: boolean;\n schemaVersion: number;\n tables: Array<{\n name: string;\n columns: readonly string[];\n rls_classification: string;\n policies: readonly SchemaPolicy[];\n sourceFiles: readonly string[];\n }>;\n unclassified?: readonly unknown[];\n}\n\n/**\n * A trace bundle for validation is the aggregated list of every trace the\n * proof run emitted. Kept as `TraceArtifact[]` so v0.6's per-proof JSON\n * files can be concatenated without transformation.\n */\nexport type TraceBundle = readonly TraceArtifact[];\n\n// ---------------------------------------------------------------------------\n// Shape validation\n// ---------------------------------------------------------------------------\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/**\n * Validate the optional `expectedChanges` block. Kept separate because the\n * shape has real structure (string shorthands vs narrowing objects) and a\n * malformed budget must fail loudly rather than half-enforce.\n */\nfunction expectedChangesShapeIssues(block: unknown): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n if (block === undefined) return issues;\n if (!block || typeof block !== \"object\" || Array.isArray(block)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"expectedChanges must be an object when present\",\n });\n return issues;\n }\n const changes = block as Record<string, unknown>;\n\n for (const key of [\"modules\", \"dependencies\"] as const) {\n const list = changes[key];\n if (list === undefined) continue;\n if (!Array.isArray(list) || !list.every(isNonEmptyString)) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key} must be an array of non-empty strings when present`,\n });\n }\n }\n\n const validateEntryList = (\n key: \"actions\" | \"tables\",\n nameField: \"ref\" | \"name\",\n kinds: readonly string[],\n ) => {\n const list = changes[key];\n if (list === undefined) return;\n if (!Array.isArray(list)) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key} must be an array when present`,\n });\n return;\n }\n for (const entry of list) {\n if (isNonEmptyString(entry)) continue;\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key} entries must be non-empty strings or { ${nameField}, changes? } objects`,\n });\n continue;\n }\n const record = entry as Record<string, unknown>;\n if (!isNonEmptyString(record[nameField])) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key}[].${nameField} is required (non-empty string)`,\n });\n }\n if (record.changes !== undefined) {\n if (\n !Array.isArray(record.changes) ||\n record.changes.length === 0 ||\n !record.changes.every((c) => kinds.includes(c as string))\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: `expectedChanges.${key}[].changes must be a non-empty array drawn from: ${kinds.join(\", \")}`,\n });\n }\n }\n }\n };\n validateEntryList(\"actions\", \"ref\", ACTION_CHANGE_KINDS);\n validateEntryList(\"tables\", \"name\", TABLE_CHANGE_KINDS);\n\n if (changes.lockfile !== undefined && typeof changes.lockfile !== \"boolean\") {\n issues.push({\n category: \"manifest_shape\",\n message: \"expectedChanges.lockfile must be a boolean when present\",\n });\n }\n\n return issues;\n}\n\nfunction shapeIssues(manifest: unknown): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n if (!manifest || typeof manifest !== \"object\") {\n issues.push({\n category: \"manifest_shape\",\n message: \"manifest is not a JSON object\",\n });\n return issues;\n }\n const m = manifest as Partial<MissionManifest>;\n\n if (m.schemaVersion !== MISSION_MANIFEST_SCHEMA_VERSION) {\n issues.push({\n category: \"manifest_shape\",\n message: `expected schemaVersion=${MISSION_MANIFEST_SCHEMA_VERSION}, found ${String(m.schemaVersion)}`,\n });\n }\n if (typeof m.missionId !== \"string\" || m.missionId.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message: \"missionId is required (non-empty string)\",\n });\n }\n if (typeof m.missionTitle !== \"string\" || m.missionTitle.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message: \"missionTitle is required (non-empty string)\",\n });\n }\n if (typeof m.createdAt !== \"string\" || m.createdAt.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message: \"createdAt is required (ISO-8601 string)\",\n });\n }\n\n // Optional drift bound: absent is fine, present-but-wrong is not — a typo'd\n // scope list would otherwise silently disable the enforcement it names.\n issues.push(...expectedChangesShapeIssues(m.expectedChanges));\n\n const req = m.requirements;\n if (!req || typeof req !== \"object\") {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements block is required\",\n });\n return issues;\n }\n if (!Array.isArray(req.capabilities_must_exist)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements.capabilities_must_exist must be an array\",\n });\n }\n if (!Array.isArray(req.schema_must_contain)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements.schema_must_contain must be an array\",\n });\n }\n if (!Array.isArray(req.trace_must_prove)) {\n issues.push({\n category: \"manifest_shape\",\n message: \"requirements.trace_must_prove must be an array\",\n });\n }\n // Optional block: absent is fine, present-but-wrong is not.\n if (\n req.policies_must_not_allow !== undefined &&\n !Array.isArray(req.policies_must_not_allow)\n ) {\n issues.push({\n category: \"manifest_shape\",\n message:\n \"requirements.policies_must_not_allow must be an array when present\",\n });\n }\n\n const fields = {\n capabilities_must_exist: [\"name\", \"module\", \"object\"],\n schema_must_contain: [\"table\"],\n trace_must_prove: [\"target\", \"description\"],\n policies_must_not_allow: [\"table\", \"role\", \"description\"],\n } as const;\n for (const [key, names] of Object.entries(fields)) {\n const entries = (req as unknown as Record<string, unknown>)[key];\n if (!Array.isArray(entries)) continue;\n for (const entry of entries) {\n if (\n !entry ||\n typeof entry !== \"object\" ||\n names.some((name) => !isNonEmptyString(entry[name]))\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: `${key} contains an invalid entry`,\n });\n } else if (\n key === \"schema_must_contain\" &&\n (!Array.isArray(entry.required_columns) ||\n !entry.required_columns.every(isNonEmptyString))\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: \"required_columns must be an array of column names\",\n });\n }\n }\n }\n if (issues.length) return issues;\n\n // Vocabulary checks (best-effort — don't fail if fields are missing since\n // shape errors above will already have been reported).\n for (const cap of Array.isArray(req.capabilities_must_exist)\n ? req.capabilities_must_exist\n : []) {\n if (!isProofVerb((cap as { verb?: unknown }).verb)) {\n issues.push({\n category: \"manifest_shape\",\n message: `capabilities_must_exist[].verb \"${String((cap as { verb?: unknown }).verb)}\" is not in PROOF_VERBS`,\n });\n }\n }\n for (const s of Array.isArray(req.schema_must_contain)\n ? req.schema_must_contain\n : []) {\n if (\n !isRlsClassification(\n (s as { rls_classification?: unknown }).rls_classification,\n )\n ) {\n issues.push({\n category: \"manifest_shape\",\n message: `schema_must_contain[].rls_classification \"${String((s as { rls_classification?: unknown }).rls_classification)}\" is not in RLS_CLASSIFICATIONS`,\n });\n }\n }\n for (const t of Array.isArray(req.trace_must_prove)\n ? req.trace_must_prove\n : []) {\n if (!isProofKind((t as { kind?: unknown }).kind)) {\n issues.push({\n category: \"manifest_shape\",\n message: `trace_must_prove[].kind \"${String((t as { kind?: unknown }).kind)}\" is not in PROOF_KINDS`,\n });\n }\n const evidence = (t as { evidence?: unknown }).evidence;\n if (evidence !== undefined && evidence !== \"helper\" && evidence !== \"any\") {\n issues.push({\n category: \"manifest_shape\",\n message: `trace_must_prove[].evidence \"${String(evidence)}\" is not a valid evidence origin; expected \"helper\" or \"any\"`,\n });\n }\n // Tenant isolation admits no opt-out: strict coverage rejects raw\n // isolation assertions unconditionally, and the mission gate must never\n // accept what coverage refuses. Bespoke escapes live in mutation policy,\n // not here.\n if (\n evidence === \"any\" &&\n (t as { kind?: unknown }).kind === \"tenant_isolation\"\n ) {\n issues.push({\n category: \"manifest_shape\",\n message:\n 'trace_must_prove[].evidence \"any\" is not allowed for kind \"tenant_isolation\"; isolation evidence must come from an SDK helper (assert.tenantIsolation, or assert.authorization for action targets)',\n });\n }\n }\n for (const p of Array.isArray(req.policies_must_not_allow)\n ? req.policies_must_not_allow\n : []) {\n const prohibition = p as { role?: unknown; commands?: unknown };\n if (typeof prohibition.role !== \"string\" || prohibition.role.length === 0) {\n issues.push({\n category: \"manifest_shape\",\n message:\n \"policies_must_not_allow[].role is required (non-empty database role name)\",\n });\n }\n // A typo'd command would otherwise match nothing and pass, turning a\n // security claim into a comment.\n if (prohibition.commands !== undefined) {\n if (!Array.isArray(prohibition.commands)) {\n issues.push({\n category: \"manifest_shape\",\n message:\n \"policies_must_not_allow[].commands must be an array when present\",\n });\n } else {\n for (const command of prohibition.commands) {\n if (!isPolicyCommand(command)) {\n issues.push({\n category: \"manifest_shape\",\n message: `policies_must_not_allow[].commands entry \"${String(command)}\" is not in POLICY_COMMANDS`,\n });\n }\n }\n }\n }\n }\n\n return issues;\n}\n\n// ---------------------------------------------------------------------------\n// Cross-reference checks\n// ---------------------------------------------------------------------------\n\nfunction checkCapabilities(\n manifest: MissionManifest,\n capabilities: CapabilitiesArtifact,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n for (const req of manifest.requirements.capabilities_must_exist) {\n const match = capabilities.capabilities.find(\n (c) => c.name === req.name && c.module === req.module,\n );\n if (!match) {\n issues.push({\n category: \"capability_missing\",\n message: `expected capability ${req.module}:${req.name} (verb=${req.verb}, object=${req.object}); not found in capabilities.json`,\n context: { requirement: req },\n });\n continue;\n }\n if (match.verb !== req.verb) {\n issues.push({\n category: \"capability_mismatch\",\n message: `capability ${req.module}:${req.name} has verb=\"${match.verb ?? \"<missing>\"}\" but manifest requires \"${req.verb}\"; add or update withProof({ verb }) on the action`,\n context: { requirement: req, found: match },\n });\n }\n if (req.object && match.object !== req.object) {\n issues.push({\n category: \"capability_mismatch\",\n message: `capability ${req.module}:${req.name} has object=\"${match.object ?? \"<missing>\"}\" but manifest requires \"${req.object}\"; add or update withProof({ object }) on the action`,\n context: { requirement: req, found: match },\n });\n }\n if (req.invariants && req.invariants.length > 0) {\n const missing = req.invariants.filter(\n (inv) => !match.invariants.includes(inv),\n );\n if (missing.length > 0) {\n issues.push({\n category: \"capability_mismatch\",\n message: `capability ${req.module}:${req.name} is missing invariants: ${missing.join(\", \")}; add them to withProof({ invariants })`,\n context: { requirement: req, found: match, missing },\n });\n }\n }\n }\n return issues;\n}\n\nfunction checkSchema(\n manifest: MissionManifest,\n schema: SchemaArtifact,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n for (const req of manifest.requirements.schema_must_contain) {\n const table = schema.tables.find((t) => t.name === req.table);\n if (!table) {\n issues.push({\n category: \"schema_missing\",\n message: `expected table \"${req.table}\" not found in schema.json; add a CREATE TABLE migration`,\n context: { requirement: req },\n });\n continue;\n }\n const missingCols = req.required_columns.filter(\n (c) => !table.columns.includes(c),\n );\n if (missingCols.length > 0) {\n issues.push({\n category: \"schema_column_missing\",\n message: `table \"${req.table}\" is missing columns: ${missingCols.join(\", \")}; found columns: ${table.columns.join(\", \")}`,\n context: {\n requirement: req,\n missing: missingCols,\n found: table.columns,\n },\n });\n }\n if (table.rls_classification !== req.rls_classification) {\n issues.push({\n category: \"schema_rls_mismatch\",\n message: `table \"${req.table}\" has rls_classification=\"${table.rls_classification}\" but manifest requires \"${req.rls_classification}\"; review RLS policies on the table`,\n context: { requirement: req, found: table.rls_classification },\n });\n }\n }\n return issues;\n}\n\n/**\n * Whether a policy hands `role` any authority.\n *\n * An empty `roles` list is Postgres' `PUBLIC` default — a policy written without\n * a `TO` clause applies to everyone, so treating \"no roles listed\" as \"no roles\n * granted\" would read the most permissive policy as the most restrictive one.\n */\nfunction policyGrantsRole(policy: SchemaPolicy, role: string): boolean {\n if (policy.roles.length === 0) return true;\n return policy.roles.some(\n (r) => r === role || r === \"public\" || r === \"PUBLIC\",\n );\n}\n\n/** Whether a policy's command is covered by a prohibition's command list. */\nfunction policyMatchesCommands(\n policy: SchemaPolicy,\n commands: readonly string[] | undefined,\n): boolean {\n if (!commands || commands.length === 0) return true;\n // `FOR ALL` covers every command, so it matches whatever was asked about.\n if (policy.command === \"ALL\") return true;\n return commands.includes(policy.command);\n}\n\nfunction checkPolicies(\n manifest: MissionManifest,\n schema: SchemaArtifact,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n for (const req of manifest.requirements.policies_must_not_allow ?? []) {\n const table = schema.tables.find((t) => t.name === req.table);\n if (!table) {\n issues.push({\n category: \"schema_missing\",\n message: `expected table \"${req.table}\" not found in schema.json; add a CREATE TABLE migration`,\n context: { requirement: req },\n });\n continue;\n }\n const offending = table.policies.filter(\n (p) =>\n policyGrantsRole(p, req.role) && policyMatchesCommands(p, req.commands),\n );\n if (offending.length > 0) {\n const scope = req.commands?.length\n ? req.commands.join(\", \")\n : \"any command\";\n issues.push({\n category: \"schema_policy_permits\",\n message:\n `table \"${req.table}\" must not allow role \"${req.role}\" (${scope}), ` +\n `but ${offending.length} policy/policies do: ` +\n offending\n .map(\n (p) =>\n `\"${p.name}\" FOR ${p.command} TO ${p.roles.length > 0 ? p.roles.join(\"/\") : \"PUBLIC (no TO clause)\"}`,\n )\n .join(\", \") +\n `; requirement: \"${req.description}\"`,\n context: { requirement: req, offending },\n });\n }\n }\n return issues;\n}\n\n/**\n * An assertion plus the proof it came from. Attribution has to survive the\n * flattening, otherwise a satisfied requirement can't be traced back to the\n * spec that satisfied it.\n */\nexport interface AttributedAssertion extends TraceAssertion {\n eligible?: boolean;\n proofId: string;\n specFile?: string;\n}\n\n/** Flatten every assertion across every trace / step, keeping attribution. */\nexport function collectAssertions(bundle: TraceBundle): AttributedAssertion[] {\n const out: AttributedAssertion[] = [];\n for (const trace of bundle) {\n for (const step of trace.steps ?? []) {\n for (const a of step.assertions ?? []) {\n out.push({\n ...a,\n eligible: trace.passed && step.passed && !trace.mutation,\n proofId: trace.proofId,\n ...(trace.specFile ? { specFile: trace.specFile } : {}),\n });\n }\n }\n }\n return out;\n}\n\n/**\n * Which helper stamps may satisfy a requirement, or `null` for \"any stamp\".\n *\n * Only `tenant_isolation` is identity-bound: `assert.httpResponse` records\n * whatever `kind` its caller passes, so a mere presence check would let an\n * HTTP probe labelled `tenant_isolation` count as isolation evidence. Table\n * targets accept only `assert.tenantIsolation` (the dual-tenant probe);\n * action targets also accept `assert.authorization`, whose action probes\n * record the cross-tenant invariant they establish.\n */\nfunction allowedHelpersFor(\n kind: string,\n target: string,\n tableNames: ReadonlySet<string>,\n): readonly string[] | null {\n if (kind !== \"tenant_isolation\") return null;\n const head = String(target).split(\".\")[0];\n return tableNames.has(head)\n ? TENANT_ISOLATION_TABLE_HELPERS\n : TENANT_ISOLATION_ACTION_HELPERS;\n}\n\nfunction checkTraces(\n manifest: MissionManifest,\n bundle: TraceBundle,\n schema: SchemaArtifact,\n): { issues: ValidationIssue[]; evidence: RequirementEvidence[] } {\n const issues: ValidationIssue[] = [];\n const evidence: RequirementEvidence[] = [];\n const assertions = collectAssertions(bundle);\n const tableNames = new Set((schema.tables ?? []).map((t) => t.name));\n\n for (const req of manifest.requirements.trace_must_prove) {\n const requiredRole = normalizeAssertionRole(req.role);\n if (requiredRole === null) {\n issues.push({\n category: \"manifest_shape\",\n message: `trace_must_prove[].role \"${String(req.role)}\" is not a valid assertion role; expected \"primary\" or \"control\"`,\n context: { requirement: req },\n });\n continue;\n }\n\n // Candidates share kind + target (and proofId when the requirement pins\n // one). Everything after this is about explaining *why* a candidate does\n // or does not count as evidence.\n const candidates = assertions.filter(\n (a) =>\n a.kind === req.kind &&\n a.target === req.target &&\n (req.proofId === undefined || a.proofId === req.proofId),\n );\n const requiredOrigin = requiredEvidenceOrigin(req.kind, req.evidence);\n const allowedHelpers = allowedHelpersFor(req.kind, req.target, tableNames);\n const originSatisfied = (a: TraceAssertion) =>\n requiredOrigin === \"any\" ||\n (typeof a.emittedBy === \"string\" &&\n (allowedHelpers ?? ASSERTION_HELPERS).includes(a.emittedBy));\n const match = candidates.find(\n (a) =>\n a.eligible !== false &&\n assertionPassed(a) &&\n normalizeAssertionRole(a.role) === requiredRole &&\n originSatisfied(a),\n );\n\n if (match) {\n evidence.push({\n kind: req.kind,\n target: req.target,\n role: requiredRole,\n satisfied: true,\n proofId: match.proofId,\n ...(match.specFile ? { specFile: match.specFile } : {}),\n ...(match.detail ? { detail: match.detail } : {}),\n ...(match.emittedBy ? { emittedBy: match.emittedBy } : {}),\n });\n continue;\n }\n\n evidence.push({\n kind: req.kind,\n target: req.target,\n role: requiredRole,\n satisfied: false,\n });\n\n // Explain the near miss. \"No passing assertion found\" sends people hunting\n // for a missing proof when the real cause is usually one of these three.\n const failed = candidates.filter((a) => a.passed === false);\n const wrongRole = candidates.filter(\n (a) =>\n a.passed === true && normalizeAssertionRole(a.role) !== requiredRole,\n );\n const unverified = candidates.filter(\n (a) =>\n a.passed === true &&\n normalizeAssertionRole(a.role) === requiredRole &&\n !originSatisfied(a),\n );\n\n // A passing right-role assertion exists but its origin disqualifies it —\n // spec-recorded where a helper is demanded, or stamped by a helper that\n // cannot vouch for this kind (assert.httpResponse records the caller's\n // kind verbatim). Its own category: the fix is different and the failure\n // must not read as \"no proof was written\".\n if (unverified.length > 0) {\n const found = unverified[0].emittedBy\n ? `was emitted by ${unverified[0].emittedBy}, which cannot vouch for this claim`\n : \"was recorded directly by spec code (no emittedBy)\";\n const wanted = allowedHelpers\n ? allowedHelpers.join(\" or \")\n : \"an SDK assert.* helper\";\n issues.push({\n category: \"trace_unverified\",\n message:\n `a passing role=\"${requiredRole}\" assertion with kind=\"${req.kind}\" and target=\"${req.target}\" exists but ${found}, ` +\n `and this requirement demands evidence=\"helper\"${req.evidence === undefined ? ` (mandatory for ${req.kind})` : \"\"}. ` +\n `Emit the claim through ${wanted}` +\n `${allowedHelpers === null ? `, or set evidence: \"any\" on the requirement if a bespoke probe is intended` : \"\"}; description: \"${req.description}\"`,\n context: { requirement: req, unverifiedCount: unverified.length },\n });\n continue;\n }\n\n let reason: string;\n if (failed.length > 0) {\n reason =\n `found ${failed.length} matching assertion(s) but they FAILED: ` +\n failed\n .map((a) => `[${a.proofId}] ${a.detail ?? \"(no detail)\"}`)\n .join(\" | \");\n } else if (wrongRole.length > 0) {\n reason =\n `found ${wrongRole.length} passing assertion(s) with role=\"${normalizeAssertionRole(wrongRole[0].role)}\" but this requirement needs role=\"${requiredRole}\". ` +\n `A \"control\" assertion passes by design and only shows the probe could have failed — it cannot stand in for the claim itself. ` +\n `Add the missing ${requiredRole} probe`;\n } else if (req.proofId !== undefined) {\n reason = `no assertion with this kind+target was emitted by proof \"${req.proofId}\" (other proofs may have emitted one; this requirement is pinned)`;\n } else {\n reason = \"no assertion with this kind+target was emitted by any proof\";\n }\n\n issues.push({\n category: \"trace_missing\",\n message: `no passing role=\"${requiredRole}\" assertion found with kind=\"${req.kind}\" and target=\"${req.target}\"; ${reason}; description: \"${req.description}\"`,\n context: { requirement: req, candidateCount: candidates.length },\n });\n }\n\n return { issues, evidence };\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\nexport interface ValidateMissionInput {\n manifest: unknown;\n capabilities: CapabilitiesArtifact;\n schema: SchemaArtifact;\n traces: TraceBundle;\n}\n\nexport function validateMission(input: ValidateMissionInput): ValidationResult {\n const shape = shapeIssues(input.manifest);\n if (shape.length > 0) {\n return {\n missionId:\n (input.manifest as { missionId?: string } | null)?.missionId ??\n \"<invalid>\",\n ok: false,\n issues: shape,\n };\n }\n\n const evidenceIssues = Array.isArray(input.traces)\n ? input.traces.flatMap((t) => traceShapeIssues(t))\n : [\"trace_shape: trace bundle must be an array\"];\n if (\n !evidenceIssues.length &&\n new Set(input.traces.map((t) => t.proofId)).size !== input.traces.length\n )\n evidenceIssues.push(\"duplicate_proof_id\");\n if (evidenceIssues.length)\n return {\n missionId: (input.manifest as MissionManifest).missionId,\n ok: false,\n issues: evidenceIssues.map((message) => ({\n category: \"trace_shape\" as const,\n message,\n })),\n };\n if (\n input.schema.assessed === false ||\n input.capabilities.unclassified?.length\n )\n return {\n missionId: (input.manifest as MissionManifest).missionId,\n ok: false,\n issues: [\n {\n category: \"manifest_shape\",\n message:\n \"discovery_unassessed: regenerate complete schema and capability artifacts\",\n },\n ],\n };\n const manifest = input.manifest as MissionManifest;\n const traceResult = checkTraces(manifest, input.traces, input.schema);\n const issues: ValidationIssue[] = [\n ...checkCapabilities(manifest, input.capabilities),\n ...checkSchema(manifest, input.schema),\n ...checkPolicies(manifest, input.schema),\n ...traceResult.issues,\n ];\n\n return {\n missionId: manifest.missionId,\n ok: issues.length === 0,\n issues,\n evidence: traceResult.evidence,\n };\n}\n","import { Client, type ClientConfig } from \"pg\";\n\n/** Direct database access is restricted to explicitly configured local test stacks. */\nexport async function withLocalPostgres<T>(\n connectionString: string,\n fn: (client: Client) => Promise<T>,\n): Promise<T> {\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch {\n throw new Error(\n \"[PROOF_FAIL] unsafe_database: invalid PostgreSQL connection URL\",\n );\n }\n if (\n ![\"postgres:\", \"postgresql:\"].includes(url.protocol) ||\n ![\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)\n )\n throw new Error(\n \"[PROOF_FAIL] unsafe_database: direct proof connections require a loopback PostgreSQL host\",\n );\n // Reject connection-string query options that could redirect the actual host.\n if ([...url.searchParams.keys()].some((key) => ![\"sslmode\"].includes(key)))\n throw new Error(\n \"[PROOF_FAIL] unsafe_database: unsupported PostgreSQL URL options\",\n );\n const config: ClientConfig = {\n connectionString,\n connectionTimeoutMillis: 5000,\n statement_timeout: 30000,\n query_timeout: 35000,\n application_name: \"proof-harness\",\n };\n const client = new Client(config);\n try {\n await client.connect();\n return await fn(client);\n } finally {\n await client.end();\n }\n}\n\nexport function quotePostgresIdentifier(value: string): string {\n if (typeof value !== \"string\" || !value || value.includes(\"\\0\"))\n throw new Error(\"invalid PostgreSQL identifier\");\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n}\n\nexport interface PostgresCatalogPolicy {\n name: string;\n command: \"SELECT\" | \"INSERT\" | \"UPDATE\" | \"DELETE\" | \"ALL\";\n roles: string[];\n mode: \"PERMISSIVE\" | \"RESTRICTIVE\";\n using: string;\n check: string;\n}\n\nexport interface PostgresCatalogTable {\n schema: string;\n name: string;\n columns: string[];\n rls_enabled: boolean;\n rls_forced: boolean;\n policies: PostgresCatalogPolicy[];\n}\n\n/** One statement takes a consistent snapshot of columns, RLS and complete policies. */\nexport async function readPostgresCatalog(\n client: Client,\n schemas: readonly string[] = [\"public\"],\n) {\n if (!schemas.length || schemas.some((s) => typeof s !== \"string\" || !s))\n throw new Error(\"catalog schemas must be nonempty names\");\n const result = await client.query<PostgresCatalogTable>(\n `\n SELECT n.nspname AS schema, c.relname AS name, c.relrowsecurity AS rls_enabled,\n c.relforcerowsecurity AS rls_forced,\n ARRAY(SELECT a.attname::text FROM pg_attribute a WHERE a.attrelid=c.oid AND a.attnum>0 AND NOT a.attisdropped ORDER BY a.attname) AS columns,\n coalesce((SELECT json_agg(json_build_object(\n 'name',p.polname,'command',CASE p.polcmd WHEN '*' THEN 'ALL' WHEN 'r' THEN 'SELECT' WHEN 'a' THEN 'INSERT' WHEN 'w' THEN 'UPDATE' WHEN 'd' THEN 'DELETE' END,\n 'roles',ARRAY(SELECT CASE WHEN r=0 THEN 'public' ELSE pg_get_userbyid(r)::text END FROM unnest(p.polroles) r ORDER BY 1),\n 'mode',CASE WHEN p.polpermissive THEN 'PERMISSIVE' ELSE 'RESTRICTIVE' END,\n 'using',coalesce(pg_get_expr(p.polqual,p.polrelid),''),\n 'check',coalesce(pg_get_expr(p.polwithcheck,p.polrelid),'')\n ) ORDER BY p.polname) FROM pg_policy p WHERE p.polrelid=c.oid),'[]'::json) AS policies\n FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace\n WHERE c.relkind IN ('r','p') AND n.nspname = ANY($1::text[]) ORDER BY n.nspname,c.relname`,\n [schemas],\n );\n return result.rows;\n}\n\n/** Exercise constraints as the intended role, then roll back on the same connection. */\nexport async function rollbackInsertControl(\n client: Client,\n options: {\n schema?: string;\n table: string;\n role: string;\n payload: Record<string, unknown>;\n },\n) {\n const { table, role, payload, schema = \"public\" } = options;\n const columns = Object.keys(payload);\n if (!columns.length || columns.some((c) => payload[c] === undefined))\n throw new Error(\n \"[PROOF_FAIL] authorization_incomplete: INSERT control needs a JSON payload with defined columns\",\n );\n const target = `${quotePostgresIdentifier(schema)}.${quotePostgresIdentifier(table)}`;\n const selected = columns.map(quotePostgresIdentifier).join(\", \");\n let began = false;\n let failure: unknown;\n let failed = false;\n try {\n await client.query(\"BEGIN\");\n began = true;\n await client.query(`SET LOCAL ROLE ${quotePostgresIdentifier(role)}`);\n const current = await client.query(\"SELECT current_user AS role\");\n if (current.rows[0]?.role !== role)\n throw new Error(\"control did not assume its configured role\");\n // JSON conversion follows PostgreSQL column types; omitted columns retain defaults.\n const inserted = await client.query(\n `INSERT INTO ${target} (${selected}) SELECT ${selected} FROM jsonb_populate_record(NULL::${target}, $1::jsonb)`,\n [JSON.stringify(payload)],\n );\n if (inserted.rowCount !== 1)\n throw new Error(\"control INSERT did not insert exactly one row\");\n // A payload rejected by a deferred FK or constraint trigger is not valid evidence.\n await client.query(\"SET CONSTRAINTS ALL IMMEDIATE\");\n } catch (error) {\n failed = true;\n failure = error;\n }\n if (began) {\n try {\n const rollback = await client.query(\"ROLLBACK\");\n if (rollback.command !== \"ROLLBACK\")\n throw new Error(\"rollback was not acknowledged\");\n } catch (error) {\n throw new AggregateError(\n failed ? [failure, error] : [error],\n \"[PROOF_FAIL] authorization_incomplete: INSERT control rollback failed\",\n );\n }\n }\n if (failed) throw failure;\n}\n"],"mappings":"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","names":["fs","path","crypto","spawnSync","crypto","fs","path","spawnSync"]}
|
package/package.json
CHANGED