proof-harness 0.1.0-next.1
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 +26 -0
- package/LICENSE +201 -0
- package/PROOF_SDK_CONTRACT.md +1255 -0
- package/README.md +60 -0
- package/cli/config.mjs +116 -0
- package/cli/engines/generate_proof_action_registry.mjs +271 -0
- package/cli/engines/parse_proof_schema.mjs +457 -0
- package/cli/engines/proof_coverage.mjs +871 -0
- package/cli/engines/proof_drift.mjs +980 -0
- package/cli/engines/proof_module_check.mjs +498 -0
- package/cli/engines/proof_mutation_check.mjs +1084 -0
- package/cli/engines/proof_mutation_inventory.mjs +508 -0
- package/cli/engines/proof_source_mutation.mjs +35 -0
- package/cli/engines/proof_verify.mjs +620 -0
- package/cli/engines/scan_module_meta.mjs +352 -0
- package/cli/engines/scan_proof_capabilities.mjs +359 -0
- package/cli/proof-harness.mjs +107 -0
- package/dist/action-types-C3OmHRyH.d.ts +26 -0
- package/dist/fixture-B-dmnG5G.d.ts +105 -0
- package/dist/mission-types-Dr5rBHYO.d.ts +299 -0
- package/dist/node.d.ts +79 -0
- package/dist/node.js +553 -0
- package/dist/node.js.map +1 -0
- package/dist/playwright.d.ts +433 -0
- package/dist/playwright.js +1824 -0
- package/dist/playwright.js.map +1 -0
- package/dist/portable-vocabulary.d.ts +86 -0
- package/dist/portable-vocabulary.js +43 -0
- package/dist/portable-vocabulary.js.map +1 -0
- package/dist/server.d.ts +55 -0
- package/dist/server.js +376 -0
- package/dist/server.js.map +1 -0
- package/dist/shared.d.ts +169 -0
- package/dist/shared.js +121 -0
- package/dist/shared.js.map +1 -0
- package/dist/trace-types-DkIzq-l9.d.ts +333 -0
- package/package.json +109 -0
- package/proof-consumer-fixtures/README.md +60 -0
- package/proof-consumer-fixtures/aggregates/M-CONFORMANCE-FAIL.json +112 -0
- package/proof-consumer-fixtures/aggregates/M-CONFORMANCE-PASS.json +101 -0
- package/proof-consumer-fixtures/baseline/failing/widgets-tenant-isolation.json +46 -0
- package/proof-consumer-fixtures/baseline/passing/widgets-tenant-isolation.json +45 -0
- package/proof-consumer-fixtures/corpus.json +102 -0
- package/proof-consumer-fixtures/duplicates/first/widgets-tenant-isolation.json +12 -0
- package/proof-consumer-fixtures/duplicates/second/widgets-tenant-isolation.json +12 -0
- package/proof-consumer-fixtures/inputs/capabilities.json +4 -0
- package/proof-consumer-fixtures/inputs/mission.json +20 -0
- package/proof-consumer-fixtures/inputs/schema.json +4 -0
- package/proof-consumer-fixtures/malformed/invalid-trace.json.invalid +4 -0
- package/proof-consumer-fixtures/malformed/wrong-manifest-version.json +11 -0
- package/proof-consumer-fixtures/malformed/wrong-trace-shape.json +8 -0
- package/proof-consumer-fixtures/mutations/MUT-WIDGETS-LEAK/mutation.json +17 -0
- package/proof-consumer-fixtures/mutations/MUT-WIDGETS-LEAK/widgets-tenant-isolation.json +50 -0
package/dist/shared.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// src/shared/vocabulary.ts
|
|
2
|
+
var PROOF_VERBS = [
|
|
3
|
+
"create",
|
|
4
|
+
"read",
|
|
5
|
+
"update",
|
|
6
|
+
"delete",
|
|
7
|
+
"invoke",
|
|
8
|
+
"transfer"
|
|
9
|
+
];
|
|
10
|
+
var PROOF_KINDS = [
|
|
11
|
+
"happy_path",
|
|
12
|
+
"tenant_isolation",
|
|
13
|
+
"authorization",
|
|
14
|
+
"validation",
|
|
15
|
+
"error_handling",
|
|
16
|
+
"idempotency"
|
|
17
|
+
];
|
|
18
|
+
var RLS_CLASSIFICATIONS = [
|
|
19
|
+
"workspace_scoped",
|
|
20
|
+
"user_scoped",
|
|
21
|
+
"admin_only",
|
|
22
|
+
"public_read",
|
|
23
|
+
"service_only",
|
|
24
|
+
"unclassified"
|
|
25
|
+
];
|
|
26
|
+
var MODULE_KINDS = [
|
|
27
|
+
/** The template ships it. Always present. */
|
|
28
|
+
"core",
|
|
29
|
+
/** Installable and already proven. Configured by answering its decisions. */
|
|
30
|
+
"extension",
|
|
31
|
+
/** Domain-specific code built for one product, under `__business-logic/`. */
|
|
32
|
+
"business",
|
|
33
|
+
/** Does not exist yet. An agent must build it against checks authored first. */
|
|
34
|
+
"generated"
|
|
35
|
+
];
|
|
36
|
+
var APPLIED_BY = [
|
|
37
|
+
/** Set a value in a named constant. */
|
|
38
|
+
"config",
|
|
39
|
+
/** Rewrite prose — legal text, marketing copy. */
|
|
40
|
+
"content",
|
|
41
|
+
/** Add or remove files, components, routes, migrations. */
|
|
42
|
+
"code"
|
|
43
|
+
];
|
|
44
|
+
function isProofVerb(value) {
|
|
45
|
+
return typeof value === "string" && PROOF_VERBS.includes(value);
|
|
46
|
+
}
|
|
47
|
+
function isProofKind(value) {
|
|
48
|
+
return typeof value === "string" && PROOF_KINDS.includes(value);
|
|
49
|
+
}
|
|
50
|
+
function isRlsClassification(value) {
|
|
51
|
+
return typeof value === "string" && RLS_CLASSIFICATIONS.includes(value);
|
|
52
|
+
}
|
|
53
|
+
function isModuleKind(value) {
|
|
54
|
+
return typeof value === "string" && MODULE_KINDS.includes(value);
|
|
55
|
+
}
|
|
56
|
+
function isAppliedBy(value) {
|
|
57
|
+
return typeof value === "string" && APPLIED_BY.includes(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/shared/portable-vocabulary.mjs
|
|
61
|
+
var TENANT_ISOLATION_TABLE_HELPERS = (
|
|
62
|
+
/** @type {const} */
|
|
63
|
+
[
|
|
64
|
+
"assert.tenantIsolation"
|
|
65
|
+
]
|
|
66
|
+
);
|
|
67
|
+
var TENANT_ISOLATION_ACTION_HELPERS = (
|
|
68
|
+
/** @type {const} */
|
|
69
|
+
[
|
|
70
|
+
"assert.tenantIsolation",
|
|
71
|
+
"assert.authorization"
|
|
72
|
+
]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// src/shared/trace-types.ts
|
|
76
|
+
var ASSERTION_ROLE_ALIASES = {
|
|
77
|
+
negative: "primary",
|
|
78
|
+
positive_control: "control"
|
|
79
|
+
};
|
|
80
|
+
function normalizeAssertionRole(role) {
|
|
81
|
+
if (role === void 0 || role === null) return "primary";
|
|
82
|
+
if (role === "primary" || role === "control") return role;
|
|
83
|
+
if (typeof role === "string" && role in ASSERTION_ROLE_ALIASES) {
|
|
84
|
+
return ASSERTION_ROLE_ALIASES[role];
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function normalizeAssertionStatus(status, passed) {
|
|
89
|
+
if (status === "passed" || status === "failed" || status === "incomplete" || status === "skipped") {
|
|
90
|
+
return status;
|
|
91
|
+
}
|
|
92
|
+
return passed ? "passed" : "failed";
|
|
93
|
+
}
|
|
94
|
+
var TRACE_ARTIFACT_SCHEMA_VERSION = 2;
|
|
95
|
+
|
|
96
|
+
// src/shared/mission-types.ts
|
|
97
|
+
var MISSION_MANIFEST_SCHEMA_VERSION = 1;
|
|
98
|
+
|
|
99
|
+
// src/shared/module-types.ts
|
|
100
|
+
var MODULE_DESCRIPTOR_SCHEMA_VERSION = 1;
|
|
101
|
+
export {
|
|
102
|
+
APPLIED_BY,
|
|
103
|
+
ASSERTION_ROLE_ALIASES,
|
|
104
|
+
MISSION_MANIFEST_SCHEMA_VERSION,
|
|
105
|
+
MODULE_DESCRIPTOR_SCHEMA_VERSION,
|
|
106
|
+
MODULE_KINDS,
|
|
107
|
+
PROOF_KINDS,
|
|
108
|
+
PROOF_VERBS,
|
|
109
|
+
RLS_CLASSIFICATIONS,
|
|
110
|
+
TENANT_ISOLATION_ACTION_HELPERS,
|
|
111
|
+
TENANT_ISOLATION_TABLE_HELPERS,
|
|
112
|
+
TRACE_ARTIFACT_SCHEMA_VERSION,
|
|
113
|
+
isAppliedBy,
|
|
114
|
+
isModuleKind,
|
|
115
|
+
isProofKind,
|
|
116
|
+
isProofVerb,
|
|
117
|
+
isRlsClassification,
|
|
118
|
+
normalizeAssertionRole,
|
|
119
|
+
normalizeAssertionStatus
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/vocabulary.ts","../src/shared/portable-vocabulary.mjs","../src/shared/trace-types.ts","../src/shared/mission-types.ts","../src/shared/module-types.ts"],"sourcesContent":["// 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 * Module kind — which of four very different things a module is.\n *\n * The field a planner reads first, because it decides cost and risk at once.\n * A module moving from `generated` to `extension` is work that will never be\n * paid for twice, and it should be visible in a diff.\n */\nexport const MODULE_KINDS = [\n /** The template ships it. Always present. */\n \"core\",\n /** Installable and already proven. Configured by answering its decisions. */\n \"extension\",\n /** Domain-specific code built for one product, under `__business-logic/`. */\n \"business\",\n /** Does not exist yet. An agent must build it against checks authored first. */\n \"generated\",\n] as const;\n\nexport type ModuleKind = (typeof MODULE_KINDS)[number];\n\n/**\n * How a buyer's answer reaches the code.\n *\n * The only technical field a planner needs, because it is what separates a\n * cheap dispatch from an expensive one. Most of this template is `code`: the\n * survey behind this vocabulary found exactly one real feature flag and about\n * twenty-five decisions that mean deleting routes and components.\n */\nexport const APPLIED_BY = [\n /** Set a value in a named constant. */\n \"config\",\n /** Rewrite prose — legal text, marketing copy. */\n \"content\",\n /** Add or remove files, components, routes, migrations. */\n \"code\",\n] as const;\n\nexport type AppliedBy = (typeof APPLIED_BY)[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\nexport function isModuleKind(value: unknown): value is ModuleKind {\n return (\n typeof value === \"string\" &&\n (MODULE_KINDS as readonly string[]).includes(value)\n );\n}\n\nexport function isAppliedBy(value: unknown): value is AppliedBy {\n return (\n typeof value === \"string\" &&\n (APPLIED_BY 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 /**\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 | \"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 External Packages\n// Import Local Imports\nimport type { AppliedBy, ModuleKind } from \"./vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// What a module declares about itself.\n//\n// Written to be read by a model rather than a person, and the reason is cost:\n// without it a planner needs the codebase in its context, which grows with\n// every extension we write. With it, a planner retrieves one small file.\n//\n// **Declare only what the code cannot say.** Tables, dependencies, actions and\n// proof coverage are all derivable from source, and a written copy of a\n// derivable fact is a fact that will be wrong later and say nothing about it.\n// What is left — what this is FOR, and what must be settled before it can be\n// used — is the part no compiler knows.\n//\n// Everything declared here is checked against the code by\n// `pnpm proof:modules:check`. A descriptor that can be wrong silently is a\n// liability with a nice format.\n//\n// Note on the name: \"manifest\" in this repo means the *mission* manifest\n// (`.proof/current-mission.json`), which gates CI. This is a module\n// *descriptor*, and the two are unrelated.\n// ---------------------------------------------------------------------------\n\nexport const MODULE_DESCRIPTOR_SCHEMA_VERSION = 1;\n\n/**\n * Where a buyer's answer has to land, so the code agent that applies it does\n * not have to rediscover the seams — and so a rename breaks CI instead of\n * quietly misleading the next agent.\n *\n * Two forms, both checkable:\n *\n * \"app/__core/legal-notices/src/pages/imprint.tsx\" — the file must exist\n * \"app/__core/user-auth/src/config.ts#MFA_POLICY\" — ...and contain that identifier\n *\n * Seams routinely sit **outside** the declaring module. Answering \"we do not\n * need an Impressum\" touches the page, its route, the shared footer and\n * `llm.txt`; an agent that only knows about `legal-notices` deletes one of the\n * four and leaves three links pointing at a 404. That is the whole reason this\n * field exists.\n */\nexport type ModuleSeam = string;\n\n/**\n * Something the buyer chooses between before this module can be used.\n *\n * Declared by the module rather than known by the planner, so a planner's\n * context stays flat as the library grows from one extension to thirty.\n *\n * **A question the buyer cannot answer does not belong here.** Anything needing\n * technical judgement is a `ModuleDefault` and we own it; asking a dentist\n * about webhook retry semantics is not consultation, it is abdication.\n */\nexport interface ModuleDecision {\n /** snake_case, unique across every module. A planner keys answers by it. */\n id: string;\n /** In the buyer's language. They must be able to answer it unaided. */\n ask: string;\n /** Why it is being asked, in terms of what changes for them. */\n because: string;\n options: {\n value: string;\n /** What choosing this gets them, again in their language. */\n means: string;\n }[];\n /** How the answer reaches the code. The planner's cost signal. */\n applied_by: AppliedBy;\n /** Every place the answer lands. Checked to still exist. */\n applies: ModuleSeam[];\n}\n\n/**\n * A value the buyer supplies rather than picks — a legal name, a domain, a\n * support address.\n *\n * Separate from `decisions` because these are not choices and forcing them into\n * one would mean options that are not options. Separate from `defaults` because\n * nothing can be built until they exist: this is the planner's answer to \"what\n * must I collect before I dispatch anything?\".\n */\nexport interface ModuleInput {\n /** snake_case, unique across every module. */\n id: string;\n /** In the buyer's language. */\n ask: string;\n /** What breaks or looks wrong if it is left as shipped. */\n because: string;\n /** A concrete illustration, so nobody has to guess the format. */\n example: string;\n /** False when the template's placeholder is survivable for a while. */\n required: boolean;\n applies: ModuleSeam[];\n}\n\n/**\n * Something we decided on the buyer's behalf.\n *\n * Recorded rather than asked, so a planner can answer \"what else could change?\"\n * without putting a question in front of someone who has no opinion on invite\n * expiry windows. The code agent gets the seam either way.\n */\nexport interface ModuleDefault {\n /** snake_case, unique across every module. */\n id: string;\n /** What the template does today, in one plain sentence. */\n is: string;\n /** Where it is set. Checked to still exist. */\n where: ModuleSeam;\n}\n\n/**\n * The declared half — everything a person writes. The derived half is added by\n * `proof-harness modules`.\n */\nexport interface ModuleMeta {\n /** Directory name. Checked against where the file sits. */\n id: string;\n kind: ModuleKind;\n /**\n * What this is for, in a sentence a buyer would recognise.\n *\n * The field a planner leans on hardest and the only one nothing can verify —\n * so it is short, and reviewed whenever the module changes.\n */\n purpose: string;\n /**\n * Environment variables this module needs.\n *\n * Declared rather than derived because need is transitive: the notification\n * system reaches Resend through `@shared/email`, so no grep of its own source\n * finds `RESEND_API_KEY`. Checked against `.env.local.example`, and anything\n * the module reads directly must appear here.\n */\n env: string[];\n decisions: ModuleDecision[];\n inputs: ModuleInput[];\n defaults: ModuleDefault[];\n}\n\n/**\n * A descriptor plus everything the scanner read from the source.\n *\n * This is what lands in `.proof/modules.json` and what a planner actually\n * reads. Nothing below `purpose` is hand-written.\n */\nexport interface ScannedModule extends ModuleMeta {\n /** Repo-relative, `/` separators. */\n path: string;\n /** Tables this module's own migrations create. */\n entities: string[];\n /** Core / extension / business modules it imports. Never declared. */\n requires: string[];\n /** Shared utilities it imports. Not installable, so not a dependency to order. */\n uses_shared: string[];\n /** From `capabilities.json`: what this module can be asked to do. */\n actions: { name: string; verb: string | null; object: string | null }[];\n /**\n * How battle-tested it is. Derived, and eventually the reason an installed\n * module costs a buyer less than a built one: somebody has already shown\n * these checks can fail.\n */\n proven: { specs: number; mutations: number };\n}\n\n/**\n * The generated artifact. Deterministic and unordered-by-nothing: every list is\n * sorted and there is no timestamp, so two runs on one tree are byte-identical.\n */\nexport interface ModulesArtifact {\n schemaVersion: number;\n modules: ScannedModule[];\n /**\n * Tables no descriptor claims — today the ones owned by `__shared` modules,\n * which are utilities nobody installs and so carry no descriptor.\n *\n * Listed rather than omitted. A planner that cannot see `audit_logs` will\n * eventually promise to build it.\n */\n unowned_tables: { table: string; module: string; path: string }[];\n}\n"],"mappings":";AAkBO,IAAM,cAAc;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,IAAM,cAAc;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+BO,IAAM,eAAe;AAAA;AAAA,EAE1B;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;AAYO,IAAM,aAAa;AAAA;AAAA,EAExB;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;AAQO,SAAS,YAAY,OAAoC;AAC9D,SACE,OAAO,UAAU,YAChB,YAAkC,SAAS,KAAK;AAErD;AAEO,SAAS,YAAY,OAAoC;AAC9D,SACE,OAAO,UAAU,YAChB,YAAkC,SAAS,KAAK;AAErD;AAEO,SAAS,oBACd,OAC4B;AAC5B,SACE,OAAO,UAAU,YAChB,oBAA0C,SAAS,KAAK;AAE7D;AASO,SAAS,aAAa,OAAqC;AAChE,SACE,OAAO,UAAU,YAChB,aAAmC,SAAS,KAAK;AAEtD;AAEO,SAAS,YAAY,OAAoC;AAC9D,SACE,OAAO,UAAU,YAChB,WAAiC,SAAS,KAAK;AAEpD;;;AC3FO,IAAM;AAAA;AAAA,EAAuD;AAAA,IAClE;AAAA,EACF;AAAA;AAGO,IAAM;AAAA;AAAA,EAAwD;AAAA,IACnE;AAAA,IACA;AAAA,EACF;AAAA;;;ACdO,IAAM,yBAAkE;AAAA,EAC7E,UAAU;AAAA,EACV,kBAAkB;AACpB;AAOO,SAAS,uBAAuB,MAAqC;AAC1E,MAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,MAAI,SAAS,aAAa,SAAS,UAAW,QAAO;AACrD,MAAI,OAAO,SAAS,YAAY,QAAQ,wBAAwB;AAC9D,WAAO,uBAAuB,IAAI;AAAA,EACpC;AACA,SAAO;AACT;AAuBO,SAAS,yBACd,QACA,QACiB;AACjB,MACE,WAAW,YACX,WAAW,YACX,WAAW,gBACX,WAAW,WACX;AACA,WAAO;AAAA,EACT;AACA,SAAO,SAAS,WAAW;AAC7B;AAqHO,IAAM,gCAAgC;;;AC1MtC,IAAM,kCAAkC;;;ACRxC,IAAM,mCAAmC;","names":[]}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verb — the action a capability performs.
|
|
3
|
+
* Used as metadata in `withProof({ verb: ... })`.
|
|
4
|
+
*/
|
|
5
|
+
declare const PROOF_VERBS: readonly ["create", "read", "update", "delete", "invoke", "transfer"];
|
|
6
|
+
type ProofVerb = (typeof PROOF_VERBS)[number];
|
|
7
|
+
/**
|
|
8
|
+
* Kind — the category of a trace step / assertion.
|
|
9
|
+
* Used in `trace_must_prove` manifest entries and emitted by `assert.*`
|
|
10
|
+
* helpers and `trace.step({ kind })` calls.
|
|
11
|
+
*/
|
|
12
|
+
declare const PROOF_KINDS: readonly ["happy_path", "tenant_isolation", "authorization", "validation", "error_handling", "idempotency"];
|
|
13
|
+
type ProofKind = (typeof PROOF_KINDS)[number];
|
|
14
|
+
/**
|
|
15
|
+
* RLS classification — how a table is scoped under Row Level Security.
|
|
16
|
+
* Emitted by the migration parser (v1). Defined here so v0.5 helpers and
|
|
17
|
+
* future tooling share the same vocabulary.
|
|
18
|
+
*/
|
|
19
|
+
declare const RLS_CLASSIFICATIONS: readonly ["workspace_scoped", "user_scoped", "admin_only", "public_read", "service_only", "unclassified"];
|
|
20
|
+
type RlsClassification = (typeof RLS_CLASSIFICATIONS)[number];
|
|
21
|
+
/**
|
|
22
|
+
* The commands a Postgres RLS policy can cover, as the migration parser reports
|
|
23
|
+
* them.
|
|
24
|
+
*
|
|
25
|
+
* `rls_classification` gives a table one label, which is a useful summary and a
|
|
26
|
+
* poor description: real policies are per-command, so a table can be readable
|
|
27
|
+
* only by admins while being writable only by the service role. `ALL` is Postgres'
|
|
28
|
+
* own wildcard and matches every command, which is why a prohibition on any single
|
|
29
|
+
* command has to consider it.
|
|
30
|
+
*/
|
|
31
|
+
declare const POLICY_COMMANDS: readonly ["SELECT", "INSERT", "UPDATE", "DELETE", "ALL"];
|
|
32
|
+
type PolicyCommand = (typeof POLICY_COMMANDS)[number];
|
|
33
|
+
/**
|
|
34
|
+
* Module kind — which of four very different things a module is.
|
|
35
|
+
*
|
|
36
|
+
* The field a planner reads first, because it decides cost and risk at once.
|
|
37
|
+
* A module moving from `generated` to `extension` is work that will never be
|
|
38
|
+
* paid for twice, and it should be visible in a diff.
|
|
39
|
+
*/
|
|
40
|
+
declare const MODULE_KINDS: readonly ["core", "extension", "business", "generated"];
|
|
41
|
+
type ModuleKind = (typeof MODULE_KINDS)[number];
|
|
42
|
+
/**
|
|
43
|
+
* How a buyer's answer reaches the code.
|
|
44
|
+
*
|
|
45
|
+
* The only technical field a planner needs, because it is what separates a
|
|
46
|
+
* cheap dispatch from an expensive one. Most of this template is `code`: the
|
|
47
|
+
* survey behind this vocabulary found exactly one real feature flag and about
|
|
48
|
+
* twenty-five decisions that mean deleting routes and components.
|
|
49
|
+
*/
|
|
50
|
+
declare const APPLIED_BY: readonly ["config", "content", "code"];
|
|
51
|
+
type AppliedBy = (typeof APPLIED_BY)[number];
|
|
52
|
+
declare function isProofVerb(value: unknown): value is ProofVerb;
|
|
53
|
+
declare function isProofKind(value: unknown): value is ProofKind;
|
|
54
|
+
declare function isRlsClassification(value: unknown): value is RlsClassification;
|
|
55
|
+
declare function isModuleKind(value: unknown): value is ModuleKind;
|
|
56
|
+
declare function isAppliedBy(value: unknown): value is AppliedBy;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The evidential role an assertion plays.
|
|
60
|
+
*
|
|
61
|
+
* - `primary` — the claim the proof exists to make. For an authorization or
|
|
62
|
+
* tenant-isolation proof that is a refusal ("the outsider could
|
|
63
|
+
* NOT read the row"); for a happy_path proof it is a success
|
|
64
|
+
* ("the user reached their workspace"). This is what a mission's
|
|
65
|
+
* `trace_must_prove` entry matches by default.
|
|
66
|
+
* - `control` — a companion assertion proving the primary probe was capable of
|
|
67
|
+
* failing at all: "the operation that SHOULD be permitted still
|
|
68
|
+
* succeeded", or "the row the primary probe looked for genuinely
|
|
69
|
+
* exists and is readable by someone."
|
|
70
|
+
*
|
|
71
|
+
* The distinction is load-bearing, not cosmetic. A refusal probe alone cannot
|
|
72
|
+
* tell "RLS correctly hid this row" apart from "there was no row / the grant
|
|
73
|
+
* denies everyone / the table name was a typo" — every one of those yields zero
|
|
74
|
+
* rows and would record a passing assertion. The control is what makes the zero
|
|
75
|
+
* mean something.
|
|
76
|
+
*
|
|
77
|
+
* Because the validator matches `trace_must_prove` on `{ kind, target,
|
|
78
|
+
* passed: true }`, controls MUST be labelled: otherwise a control (which by
|
|
79
|
+
* design passes) could satisfy a requirement all on its own and the primary
|
|
80
|
+
* probe would never be checked at all.
|
|
81
|
+
*
|
|
82
|
+
* Naming note: an earlier draft called these `negative` / `positive_control`.
|
|
83
|
+
* That reads backwards for `happy_path`, whose primary claim is a success, so
|
|
84
|
+
* the roles are named by evidential function instead. Both older names are
|
|
85
|
+
* accepted as deprecated aliases wherever a role is parsed.
|
|
86
|
+
*/
|
|
87
|
+
type AssertionRole = "primary" | "control";
|
|
88
|
+
/**
|
|
89
|
+
* Concrete operation exercised by an assertion. Mutation inventory keys claims
|
|
90
|
+
* by kind + target + operation so a SELECT mutation cannot silently stand in
|
|
91
|
+
* for an untested UPDATE/DELETE or action invocation.
|
|
92
|
+
*/
|
|
93
|
+
type ProofOperation = "select" | "insert" | "update" | "delete" | "invoke" | "request";
|
|
94
|
+
/** Deprecated role spellings accepted on input and normalized to AssertionRole. */
|
|
95
|
+
declare const ASSERTION_ROLE_ALIASES: Readonly<Record<string, AssertionRole>>;
|
|
96
|
+
/**
|
|
97
|
+
* Normalize a role from an artifact or manifest. `undefined` means `primary`:
|
|
98
|
+
* traces written before schemaVersion 2 carry no role, and their assertions
|
|
99
|
+
* were all primary claims.
|
|
100
|
+
*/
|
|
101
|
+
declare function normalizeAssertionRole(role: unknown): AssertionRole | null;
|
|
102
|
+
/**
|
|
103
|
+
* What an assertion concluded.
|
|
104
|
+
*
|
|
105
|
+
* `passed` / `failed` are the two verdicts a boolean can carry. The other two
|
|
106
|
+
* exist because "we did not find out" is a real outcome that a boolean has to
|
|
107
|
+
* round to one side or the other, and rounding it to `false` is indistinguishable
|
|
108
|
+
* from a genuine failure while rounding it to `true` is how a suite quietly stops
|
|
109
|
+
* proving anything:
|
|
110
|
+
*
|
|
111
|
+
* - `incomplete` — the probe ran but could not reach a verdict (a precondition
|
|
112
|
+
* was missing, a dependency was unavailable).
|
|
113
|
+
* - `skipped` — the probe was deliberately not run, e.g. a tenant-isolation
|
|
114
|
+
* direction with no fixture rows on that side.
|
|
115
|
+
*
|
|
116
|
+
* Neither counts as evidence: both carry `passed: false`, so neither can satisfy
|
|
117
|
+
* a mission requirement. The status is what lets a consumer tell them apart from
|
|
118
|
+
* a real failure, and stops a skip from disappearing into "one fewer assertion".
|
|
119
|
+
*/
|
|
120
|
+
type AssertionStatus = "passed" | "failed" | "incomplete" | "skipped";
|
|
121
|
+
/** Derive the status of an assertion that predates the field. */
|
|
122
|
+
declare function normalizeAssertionStatus(status: unknown, passed: boolean): AssertionStatus;
|
|
123
|
+
/**
|
|
124
|
+
* A single structured assertion recorded during a step. Produced by the
|
|
125
|
+
* `assert.*` helpers via `recordAssertion(...)`. Hand-written `expect(...)`
|
|
126
|
+
* calls inside a step do NOT appear here — only helper-driven assertions do,
|
|
127
|
+
* because the v1 mission-manifest validator matches on
|
|
128
|
+
* `{ kind, target, passed: true, role }` and needs a closed, machine-readable
|
|
129
|
+
* surface.
|
|
130
|
+
*/
|
|
131
|
+
interface TraceAssertion {
|
|
132
|
+
/** Category from the closed `PROOF_KINDS` vocabulary. */
|
|
133
|
+
kind: ProofKind;
|
|
134
|
+
/** The thing asserted against: table, route, action, etc. */
|
|
135
|
+
target: string;
|
|
136
|
+
/** Concrete operation the probe exercised. */
|
|
137
|
+
operation?: ProofOperation;
|
|
138
|
+
/**
|
|
139
|
+
* Whether this specific assertion held. Remains the authoritative field for
|
|
140
|
+
* the validator and for consumers: only `true` is evidence.
|
|
141
|
+
*/
|
|
142
|
+
passed: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Why `passed` has the value it has. Omitted is derived from `passed`, so
|
|
145
|
+
* traces written before this field stay readable.
|
|
146
|
+
*/
|
|
147
|
+
status?: AssertionStatus;
|
|
148
|
+
/**
|
|
149
|
+
* Evidential role. Omitted is treated as `"primary"` by the validator for
|
|
150
|
+
* backward compatibility with traces written before schemaVersion 2.
|
|
151
|
+
*/
|
|
152
|
+
role?: AssertionRole;
|
|
153
|
+
/** Optional human-readable note for failure triage. */
|
|
154
|
+
detail?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Which SDK helper produced this assertion, e.g. `"assert.tenantIsolation"`.
|
|
157
|
+
*
|
|
158
|
+
* Stamped by the SDK itself through an internal channel that the public
|
|
159
|
+
* `recordAssertion(...)` entry point cannot reach: a value supplied by spec
|
|
160
|
+
* code is stripped before the assertion is recorded. Absent means the
|
|
161
|
+
* assertion was recorded directly by a spec via `recordAssertion(...)`.
|
|
162
|
+
*
|
|
163
|
+
* The distinction is evidential. A helper-emitted assertion went through the
|
|
164
|
+
* helper's vacuity controls, ground-truth re-reads, and role bookkeeping; a
|
|
165
|
+
* spec-recorded assertion asserts whatever the spec chose to write into it.
|
|
166
|
+
* The mission validator therefore requires helper provenance for
|
|
167
|
+
* `tenant_isolation` requirements (a hand-rolled isolation probe has none of
|
|
168
|
+
* `assert.tenantIsolation`'s protections), and a mission can demand it for
|
|
169
|
+
* any requirement via `trace_must_prove[].evidence: "helper"`.
|
|
170
|
+
*
|
|
171
|
+
* For `tenant_isolation` the stamp is also identity-checked, not merely
|
|
172
|
+
* present-checked — see TENANT_ISOLATION_TABLE_HELPERS. `assert.httpResponse`
|
|
173
|
+
* records whatever `kind` its caller passes, so "some helper emitted this"
|
|
174
|
+
* is not the same claim as "the isolation probe ran".
|
|
175
|
+
*/
|
|
176
|
+
emittedBy?: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* A single step inside a proof.
|
|
181
|
+
* Produced by `t.step({ intent, kind, target }, async () => { ... })`.
|
|
182
|
+
*/
|
|
183
|
+
interface TraceStep {
|
|
184
|
+
/** Human-readable description of what the step attempted. */
|
|
185
|
+
intent: string;
|
|
186
|
+
/** Category of the step, from closed vocabulary. */
|
|
187
|
+
kind: ProofKind;
|
|
188
|
+
/** The thing the step targets: action name, table, route, etc. */
|
|
189
|
+
target: string;
|
|
190
|
+
/** What the step observed (returned by the step callback). */
|
|
191
|
+
observation: string;
|
|
192
|
+
/** Whether the step's assertions held. */
|
|
193
|
+
passed: boolean;
|
|
194
|
+
/** Optional: who the step acted as (email, role, seed identifier). */
|
|
195
|
+
actor?: string;
|
|
196
|
+
/** Optional: which workspace the step operated in. */
|
|
197
|
+
workspaceId?: string;
|
|
198
|
+
/** Wall-clock duration of the step callback. */
|
|
199
|
+
durationMs: number;
|
|
200
|
+
/** On failure, the error message captured. */
|
|
201
|
+
error?: string;
|
|
202
|
+
/**
|
|
203
|
+
* Structured assertions recorded by `assert.*` helpers during this step.
|
|
204
|
+
* Empty or omitted for steps that only use `expect(...)` directly.
|
|
205
|
+
* The v1 mission-manifest validator matches `trace_must_prove` entries
|
|
206
|
+
* against this array.
|
|
207
|
+
*/
|
|
208
|
+
assertions?: TraceAssertion[];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Current per-proof trace artifact schema version.
|
|
212
|
+
*
|
|
213
|
+
* 2 — adds `role` and `status` to assertions and the `schemaVersion` /
|
|
214
|
+
* `specFile` / `specHash` / `commit` / `dirty` provenance fields below.
|
|
215
|
+
* Consumers written against v1 keep working: every v1 field is still present
|
|
216
|
+
* with the same meaning, and both new assertion fields are derivable when
|
|
217
|
+
* absent.
|
|
218
|
+
*
|
|
219
|
+
* `commit`, `dirty`, and `mutation` arrived after v2 shipped and did not bump
|
|
220
|
+
* it: they are optional additions, and the contract's rule is that additive
|
|
221
|
+
* fields do not move a version. A consumer that needs them must treat absence
|
|
222
|
+
* as "unknown" anyway, since normal runs have no mutation and a non-git
|
|
223
|
+
* checkout cannot supply code provenance.
|
|
224
|
+
*/
|
|
225
|
+
declare const TRACE_ARTIFACT_SCHEMA_VERSION = 2;
|
|
226
|
+
/**
|
|
227
|
+
* Provenance attached only when the mutation harness deliberately plants a
|
|
228
|
+
* defect. A red mutation trace means the check detected the named defect; it
|
|
229
|
+
* must never be mistaken for a real application regression.
|
|
230
|
+
*/
|
|
231
|
+
interface TraceMutation {
|
|
232
|
+
/** Stable mutation identifier from the repository-owned mutation catalog. */
|
|
233
|
+
id: string;
|
|
234
|
+
/** Always true; makes the intentional nature explicit to copied consumers. */
|
|
235
|
+
planted: true;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* A trace artifact — one proof's worth of evidence.
|
|
239
|
+
* Written atomically to `.proof/traces/<proofId>.json` when the proof
|
|
240
|
+
* completes (pass or fail).
|
|
241
|
+
*/
|
|
242
|
+
interface TraceArtifact {
|
|
243
|
+
/**
|
|
244
|
+
* Pinned to TRACE_ARTIFACT_SCHEMA_VERSION. Absent on traces written by
|
|
245
|
+
* SDK versions before provenance was added.
|
|
246
|
+
*/
|
|
247
|
+
schemaVersion?: number;
|
|
248
|
+
/**
|
|
249
|
+
* Repo-relative path of the spec file that produced this trace, e.g.
|
|
250
|
+
* `e2e/proofs/workspace-invite.proof.ts`. Lets a reviewer go from a claim
|
|
251
|
+
* straight to the code that made it.
|
|
252
|
+
*/
|
|
253
|
+
specFile?: string;
|
|
254
|
+
/**
|
|
255
|
+
* First 12 hex chars of the SHA-256 of the spec file's contents. Two runs
|
|
256
|
+
* claiming the same proof with different hashes were produced by different
|
|
257
|
+
* code — which is the whole question when a previously-red proof turns
|
|
258
|
+
* green.
|
|
259
|
+
*/
|
|
260
|
+
specHash?: string;
|
|
261
|
+
/**
|
|
262
|
+
* Full SHA of the commit that was checked out while the proof ran. `specHash`
|
|
263
|
+
* pins the spec that made the claim; this pins the application it was made
|
|
264
|
+
* against, which is what lets an external ledger say "red at X, green at Y".
|
|
265
|
+
*
|
|
266
|
+
* Absent outside a git checkout. On a CI `pull_request` run this is the
|
|
267
|
+
* ephemeral merge commit the checkout created — accurate about what was
|
|
268
|
+
* tested, but not a commit you can return to later.
|
|
269
|
+
*/
|
|
270
|
+
commit?: string;
|
|
271
|
+
/**
|
|
272
|
+
* Whether the working tree had uncommitted changes when the proof ran. A pass
|
|
273
|
+
* from a dirty tree is evidence about code that exists nowhere but one laptop,
|
|
274
|
+
* so a consumer gating on traces should be able to refuse it. Absent when it
|
|
275
|
+
* could not be determined, which is not the same as `false`.
|
|
276
|
+
*/
|
|
277
|
+
dirty?: boolean;
|
|
278
|
+
/**
|
|
279
|
+
* Present only for intentional red runs produced by `proof:mutate`.
|
|
280
|
+
* Stored inside the trace so provenance survives artifact renames/copies.
|
|
281
|
+
*/
|
|
282
|
+
mutation?: TraceMutation;
|
|
283
|
+
/**
|
|
284
|
+
* Per-file identifier for this proof. Used as the filename stem and as the
|
|
285
|
+
* join key when the v1 mission-manifest validator looks for a matching
|
|
286
|
+
* passing step for a `trace_must_prove` entry.
|
|
287
|
+
*/
|
|
288
|
+
proofId: string;
|
|
289
|
+
/**
|
|
290
|
+
* Optional reference to the external mission (ticket) this proof belongs
|
|
291
|
+
* to. Multiple proofs share one `missionId`. Absent in v0.5 local runs
|
|
292
|
+
* that are not tied to a mission.
|
|
293
|
+
*/
|
|
294
|
+
missionId?: string;
|
|
295
|
+
/** ISO 8601 timestamp of proof start. */
|
|
296
|
+
timestamp: string;
|
|
297
|
+
/** Total wall-clock duration across all steps. */
|
|
298
|
+
durationMs: number;
|
|
299
|
+
/** True only if every step passed. */
|
|
300
|
+
passed: boolean;
|
|
301
|
+
/** Ordered list of steps as they were executed. */
|
|
302
|
+
steps: TraceStep[];
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Options accepted by `trace.proof(...)`. A bare string is treated as
|
|
306
|
+
* `{ proofId: string }` — the common v0.5 case where no mission is attached.
|
|
307
|
+
*/
|
|
308
|
+
interface ProofOptions {
|
|
309
|
+
proofId: string;
|
|
310
|
+
missionId?: string;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* The handle passed to a proof callback. Provides the step recorder.
|
|
314
|
+
*/
|
|
315
|
+
interface TraceRecorder {
|
|
316
|
+
step<T extends StepResult | void>(opts: StepOptions, fn: () => Promise<T>): Promise<T>;
|
|
317
|
+
}
|
|
318
|
+
interface StepOptions {
|
|
319
|
+
intent: string;
|
|
320
|
+
kind: ProofKind;
|
|
321
|
+
target: string;
|
|
322
|
+
actor?: string;
|
|
323
|
+
workspaceId?: string;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Optional payload returned by a step callback. If the callback returns
|
|
327
|
+
* `void` the step is still recorded with `observation: ""`.
|
|
328
|
+
*/
|
|
329
|
+
interface StepResult {
|
|
330
|
+
observation?: string;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export { type AppliedBy as A, type ModuleKind as M, PROOF_KINDS as P, RLS_CLASSIFICATIONS as R, type StepOptions as S, type TraceArtifact as T, type TraceAssertion as a, APPLIED_BY as b, ASSERTION_ROLE_ALIASES as c, type AssertionRole as d, type AssertionStatus as e, MODULE_KINDS as f, PROOF_VERBS as g, type ProofKind as h, type ProofOperation as i, type ProofOptions as j, type ProofVerb as k, type RlsClassification as l, type StepResult as m, TRACE_ARTIFACT_SCHEMA_VERSION as n, type TraceMutation as o, type TraceRecorder as p, type TraceStep as q, isAppliedBy as r, isModuleKind as s, isProofKind as t, isProofVerb as u, isRlsClassification as v, normalizeAssertionRole as w, normalizeAssertionStatus as x, type PolicyCommand as y };
|