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.
Files changed (53) hide show
  1. package/COMPATIBILITY.md +26 -0
  2. package/LICENSE +201 -0
  3. package/PROOF_SDK_CONTRACT.md +1255 -0
  4. package/README.md +60 -0
  5. package/cli/config.mjs +116 -0
  6. package/cli/engines/generate_proof_action_registry.mjs +271 -0
  7. package/cli/engines/parse_proof_schema.mjs +457 -0
  8. package/cli/engines/proof_coverage.mjs +871 -0
  9. package/cli/engines/proof_drift.mjs +980 -0
  10. package/cli/engines/proof_module_check.mjs +498 -0
  11. package/cli/engines/proof_mutation_check.mjs +1084 -0
  12. package/cli/engines/proof_mutation_inventory.mjs +508 -0
  13. package/cli/engines/proof_source_mutation.mjs +35 -0
  14. package/cli/engines/proof_verify.mjs +620 -0
  15. package/cli/engines/scan_module_meta.mjs +352 -0
  16. package/cli/engines/scan_proof_capabilities.mjs +359 -0
  17. package/cli/proof-harness.mjs +107 -0
  18. package/dist/action-types-C3OmHRyH.d.ts +26 -0
  19. package/dist/fixture-B-dmnG5G.d.ts +105 -0
  20. package/dist/mission-types-Dr5rBHYO.d.ts +299 -0
  21. package/dist/node.d.ts +79 -0
  22. package/dist/node.js +553 -0
  23. package/dist/node.js.map +1 -0
  24. package/dist/playwright.d.ts +433 -0
  25. package/dist/playwright.js +1824 -0
  26. package/dist/playwright.js.map +1 -0
  27. package/dist/portable-vocabulary.d.ts +86 -0
  28. package/dist/portable-vocabulary.js +43 -0
  29. package/dist/portable-vocabulary.js.map +1 -0
  30. package/dist/server.d.ts +55 -0
  31. package/dist/server.js +376 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/shared.d.ts +169 -0
  34. package/dist/shared.js +121 -0
  35. package/dist/shared.js.map +1 -0
  36. package/dist/trace-types-DkIzq-l9.d.ts +333 -0
  37. package/package.json +109 -0
  38. package/proof-consumer-fixtures/README.md +60 -0
  39. package/proof-consumer-fixtures/aggregates/M-CONFORMANCE-FAIL.json +112 -0
  40. package/proof-consumer-fixtures/aggregates/M-CONFORMANCE-PASS.json +101 -0
  41. package/proof-consumer-fixtures/baseline/failing/widgets-tenant-isolation.json +46 -0
  42. package/proof-consumer-fixtures/baseline/passing/widgets-tenant-isolation.json +45 -0
  43. package/proof-consumer-fixtures/corpus.json +102 -0
  44. package/proof-consumer-fixtures/duplicates/first/widgets-tenant-isolation.json +12 -0
  45. package/proof-consumer-fixtures/duplicates/second/widgets-tenant-isolation.json +12 -0
  46. package/proof-consumer-fixtures/inputs/capabilities.json +4 -0
  47. package/proof-consumer-fixtures/inputs/mission.json +20 -0
  48. package/proof-consumer-fixtures/inputs/schema.json +4 -0
  49. package/proof-consumer-fixtures/malformed/invalid-trace.json.invalid +4 -0
  50. package/proof-consumer-fixtures/malformed/wrong-manifest-version.json +11 -0
  51. package/proof-consumer-fixtures/malformed/wrong-trace-shape.json +8 -0
  52. package/proof-consumer-fixtures/mutations/MUT-WIDGETS-LEAK/mutation.json +17 -0
  53. package/proof-consumer-fixtures/mutations/MUT-WIDGETS-LEAK/widgets-tenant-isolation.json +50 -0
@@ -0,0 +1,299 @@
1
+ import { h as ProofKind, d as AssertionRole, k as ProofVerb, l as RlsClassification, y as PolicyCommand } from './trace-types-DkIzq-l9.js';
2
+ import { ACTION_CHANGE_KINDS, TABLE_CHANGE_KINDS } from './portable-vocabulary.js';
3
+
4
+ /**
5
+ * Manifest schema version. Deliberately still 1 after the July 2026 validator
6
+ * work: `trace_must_prove[].role` and `.proofId` are OPTIONAL additions, so
7
+ * every v1 manifest is still shape-valid and consumers need not regenerate
8
+ * anything. Note that validation did get stricter — a `control` assertion can
9
+ * no longer satisfy a requirement — so a manifest that passed on a vacuous
10
+ * probe before may now (correctly) fail. That is a behaviour tightening, not a
11
+ * shape change, and bumping the version would have forced a rewrite of every
12
+ * existing manifest for no benefit.
13
+ */
14
+ declare const MISSION_MANIFEST_SCHEMA_VERSION = 1;
15
+ interface CapabilityRequirement {
16
+ /** Matches `createAction({ functionName: <name> })` exactly. */
17
+ name: string;
18
+ /** Module folder name (e.g. `"issues"`, not `"__business-logic/issues"`). */
19
+ module: string;
20
+ /** One of PROOF_VERBS. */
21
+ verb: ProofVerb;
22
+ /** Free-form noun, lowercased by convention (e.g. `"issue"`, `"workspace"`). */
23
+ object: string;
24
+ /** Optional: invariants that the action's `withProof({ invariants })` must advertise. */
25
+ invariants?: readonly string[];
26
+ }
27
+ interface SchemaRequirement {
28
+ /** Unqualified public-schema table name. */
29
+ table: string;
30
+ /** Columns that must exist on the table. Order-insensitive. */
31
+ required_columns: readonly string[];
32
+ /** One of RLS_CLASSIFICATIONS. */
33
+ rls_classification: RlsClassification;
34
+ }
35
+ /**
36
+ * "No policy on this table targets this role for these commands."
37
+ *
38
+ * Exists because `rls_classification` is one label per table and Postgres RLS is
39
+ * per-command: `audit_logs` being "admin readable, service writable" cannot be
40
+ * said with a single label, and the label a manifest picks would have to be a
41
+ * lie in one direction or the other.
42
+ *
43
+ * Checks reachability, not the predicate — `USING` and `WITH CHECK` are not read.
44
+ * A policy `TO authenticated USING (is_super_admin())` counts as targeting
45
+ * `authenticated`, so "this role may act only under condition X" is not
46
+ * expressible here and belongs in `trace_must_prove`, where something exercises
47
+ * it. Write prohibitions ("members never INSERT into the audit log") are the
48
+ * natural fit.
49
+ *
50
+ * Deliberately only the prohibition, never "a policy must exist". A policy's
51
+ * existence is weak evidence — it says a rule was written, not that it works —
52
+ * and the thing that makes a permission real is a passing proof, which belongs in
53
+ * `trace_must_prove`. A prohibition is different: it is a claim that can fail
54
+ * dangerously and silently, and it is checkable from the migrations alone.
55
+ */
56
+ interface PolicyProhibition {
57
+ /** Unqualified public-schema table name. */
58
+ table: string;
59
+ /**
60
+ * Database role that must not be granted access, e.g. `"authenticated"` or
61
+ * `"anon"`. A policy with no `TO` clause defaults to `PUBLIC` in Postgres and
62
+ * therefore counts as granting every role.
63
+ */
64
+ role: string;
65
+ /**
66
+ * Commands the prohibition covers. Omit to mean "any command". A policy
67
+ * declared `FOR ALL` matches whatever is listed here, because that is what it
68
+ * does in the database.
69
+ */
70
+ commands?: readonly PolicyCommand[];
71
+ /** Human-readable statement of the rule, echoed in the failure message. */
72
+ description: string;
73
+ }
74
+ interface TraceRequirement {
75
+ /** One of PROOF_KINDS. */
76
+ kind: ProofKind;
77
+ /**
78
+ * Free-form identifier the assertion emits. Must match an assertion's
79
+ * `target` exactly (e.g. table name for `tenant_isolation`, capability
80
+ * name for `authorization`).
81
+ */
82
+ target: string;
83
+ /** Human-readable statement of what should be proved. */
84
+ description: string;
85
+ /**
86
+ * Which evidential role must satisfy this requirement. Defaults to
87
+ * `"primary"`, which is almost always what you want: a `"control"`
88
+ * assertion passes by design and proves only that the probe was capable of
89
+ * failing, so it must never stand in for the claim itself.
90
+ *
91
+ * Accepts the deprecated spellings `"negative"` / `"positive_control"`.
92
+ */
93
+ role?: AssertionRole;
94
+ /**
95
+ * Optional: restrict matching to assertions emitted by this proof.
96
+ *
97
+ * By default a requirement may be satisfied by a matching assertion from
98
+ * ANY proof in the run. That is convenient but means an unrelated spec can
99
+ * unknowingly satisfy a mission's requirement. Set this when the mission
100
+ * cares that a SPECIFIC proof made the claim. Either way, the aggregated
101
+ * trace records which proof actually supplied the evidence.
102
+ */
103
+ proofId?: string;
104
+ /**
105
+ * What origin of assertion may satisfy this requirement.
106
+ *
107
+ * - `"helper"` — only assertions stamped `emittedBy` by an SDK `assert.*`
108
+ * helper count. Helper assertions went through the SDK's vacuity
109
+ * controls and ground-truth re-reads; a spec-recorded assertion asserts
110
+ * whatever the spec wrote into it.
111
+ * - `"any"` — spec-recorded assertions (raw `recordAssertion`) count too.
112
+ * Those claims remain backstopped by the mutation inventory, which is
113
+ * why bespoke probes (RPC grants, column guards) stay expressible.
114
+ *
115
+ * `tenant_isolation` is always `"helper"`, and the stamp is additionally
116
+ * identity-checked (see TENANT_ISOLATION_TABLE_HELPERS in trace-types):
117
+ * declaring `evidence: "any"` for it is a `manifest_shape` error, so the
118
+ * mission gate and strict coverage can never disagree about whether a raw
119
+ * isolation assertion is admissible. Every other kind defaults to `"any"`.
120
+ */
121
+ evidence?: EvidenceOrigin;
122
+ }
123
+ /** Origin of assertion allowed to satisfy a `trace_must_prove` entry. */
124
+ type EvidenceOrigin = "helper" | "any";
125
+ interface MissionRequirements {
126
+ capabilities_must_exist: readonly CapabilityRequirement[];
127
+ schema_must_contain: readonly SchemaRequirement[];
128
+ trace_must_prove: readonly TraceRequirement[];
129
+ /**
130
+ * Optional, so every existing v1 manifest stays shape-valid. Omitting it means
131
+ * "no policy-level claims", not "policies are fine".
132
+ */
133
+ policies_must_not_allow?: readonly PolicyProhibition[];
134
+ }
135
+ /**
136
+ * Change kinds an action declaration may allow. Like tables, a modified
137
+ * action is decomposed into facets and EVERY facet must be covered — a
138
+ * mission expecting an invariant declaration to change does not thereby
139
+ * authorize an RBAC/auth middleware change on the same action.
140
+ *
141
+ * Derived from the single runtime source in
142
+ * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the
143
+ * dependency-free Node scripts import directly; see there for the per-facet
144
+ * meanings.
145
+ */
146
+ type ActionChangeKind = (typeof ACTION_CHANGE_KINDS)[number];
147
+ /**
148
+ * Change kinds a table declaration may allow. A "modified" table entry is
149
+ * decomposed into facets, and EVERY facet must be covered by the declaration —
150
+ * declaring `columns_added` on a table does not authorize a policy change on
151
+ * the same table. Derived from `portable-vocabulary.mjs`.
152
+ */
153
+ type TableChangeKind = (typeof TABLE_CHANGE_KINDS)[number];
154
+
155
+ /** Exact-ref action declaration. A bare string ref means "any change". */
156
+ interface ExpectedActionChange {
157
+ /** `<module>:<name>`, the same ref capabilities.json uses. */
158
+ ref: string;
159
+ /** Omit for "any change to this action". */
160
+ changes?: readonly ActionChangeKind[];
161
+ }
162
+ /** Table declaration. A bare string name means "any tracked change". */
163
+ interface ExpectedTableChange {
164
+ name: string;
165
+ /** Omit for "any tracked change to this table". */
166
+ changes?: readonly TableChangeKind[];
167
+ }
168
+ /**
169
+ * The proof-surface change budget a mission grants its PR.
170
+ *
171
+ * Everything else in the manifest states what MUST exist; this block bounds
172
+ * what else MAY change on the surfaces drift tracks. Read by
173
+ * the package drift engine, which diffs the regenerated
174
+ * `capabilities.json` / `schema.json` / `package.json` / lockfile against the
175
+ * PR's base and fails (`drift_undeclared`) on a high-severity delta outside
176
+ * the declared budget.
177
+ *
178
+ * Scope is deliberately narrow-by-default:
179
+ *
180
+ * - `modules` covers only ADDED actions in the named module (the
181
+ * new-module convenience). Modifying or removing an existing action is
182
+ * never authorized by a module declaration — a mission that expects a
183
+ * guard or signature change on an existing action must name the exact
184
+ * ref in `actions`, so "add a small feature" can never quietly license
185
+ * weakening an unrelated action in the same module.
186
+ * - String shorthands ("issues" in `tables`, "issues:createIssue" in
187
+ * `actions`) mean "any change" and are the coarse form; the object forms
188
+ * narrow to specific change kinds.
189
+ *
190
+ * Omitting the block keeps drift report-only, so every existing v1 manifest
191
+ * behaves exactly as before.
192
+ */
193
+ interface ExpectedChanges {
194
+ /**
195
+ * Module leaf names (e.g. `"issues"`) in which NEW actions may appear.
196
+ * Does not authorize modification or removal of existing actions.
197
+ */
198
+ modules?: readonly string[];
199
+ /** Exact action refs that may change; see {@link ExpectedActionChange}. */
200
+ actions?: readonly (string | ExpectedActionChange)[];
201
+ /** Tables that may change; see {@link ExpectedTableChange}. */
202
+ tables?: readonly (string | ExpectedTableChange)[];
203
+ /**
204
+ * Runtime dependencies (`package.json` `dependencies`) that may be added,
205
+ * removed, or version-changed. Every runtime dependency delta is
206
+ * high-severity — a swap or downgrade changes shipped behavior as surely as
207
+ * an addition.
208
+ */
209
+ dependencies?: readonly string[];
210
+ /**
211
+ * Expect a lockfile-only resolution change (pnpm-lock.yaml differs while
212
+ * the package.json dependency surface does not). Rare and suspicious by
213
+ * default, hence explicitly declared rather than inferred.
214
+ */
215
+ lockfile?: boolean;
216
+ }
217
+ interface MissionManifest {
218
+ /** Pinned to MISSION_MANIFEST_SCHEMA_VERSION. */
219
+ schemaVersion: number;
220
+ /** Ticket id from the consumer's kanban (e.g. `"M-042"`). */
221
+ missionId: string;
222
+ /** Short human-readable title. */
223
+ missionTitle: string;
224
+ /** Product repo slug (informational). */
225
+ productRepo?: string;
226
+ /** PR branch name (informational). */
227
+ prBranch?: string;
228
+ /** ISO-8601 timestamp the manifest was generated. */
229
+ createdAt: string;
230
+ requirements: MissionRequirements;
231
+ /**
232
+ * Optional drift bound; see {@link ExpectedChanges}. Absent means the drift
233
+ * report is informational only.
234
+ */
235
+ expectedChanges?: ExpectedChanges;
236
+ /** Free-form notes for reviewers / the coding agent. */
237
+ acceptanceNotes?: string;
238
+ }
239
+ type ValidationCategory = "capability_missing" | "capability_mismatch" | "schema_missing" | "schema_column_missing" | "schema_rls_mismatch"
240
+ /** A policy grants access the manifest declared must not exist. */
241
+ | "schema_policy_permits" | "trace_missing"
242
+ /**
243
+ * A passing assertion matched the requirement but was recorded directly by
244
+ * spec code (`recordAssertion`) where the requirement demands an SDK-helper
245
+ * origin (`evidence: "helper"`, the default for `tenant_isolation`).
246
+ */
247
+ | "trace_unverified" | "manifest_shape"
248
+ /** The Playwright run itself exited non-zero. Reported by the CLI, not the validator. */
249
+ | "proof_run"
250
+ /** The run produced no trace artifacts at all, so there is nothing to validate. */
251
+ | "no_traces"
252
+ /**
253
+ * A trace in the directory was recorded against a different commit than the
254
+ * current run — leftover evidence that cannot speak for the code under review.
255
+ * Reported by the CLI, which knows the run's commit; the validator sees only
256
+ * the bundle it is handed.
257
+ */
258
+ | "stale_trace";
259
+ /**
260
+ * Which proof supplied the evidence for a satisfied `trace_must_prove` entry.
261
+ *
262
+ * Recorded for every requirement — satisfied or not — so a reviewer can audit
263
+ * the mapping instead of trusting that "0 issues" meant the right proof made
264
+ * the claim. Requirements are matched against all assertions in the run, so
265
+ * without this the attribution would be invisible.
266
+ */
267
+ interface RequirementEvidence {
268
+ kind: ProofKind;
269
+ target: string;
270
+ role: AssertionRole;
271
+ satisfied: boolean;
272
+ /** proofId of the trace whose assertion satisfied it, when satisfied. */
273
+ proofId?: string;
274
+ /** Spec file that produced that trace, when known. */
275
+ specFile?: string;
276
+ /** The satisfying assertion's `detail`, verbatim. */
277
+ detail?: string;
278
+ /**
279
+ * The satisfying assertion's helper provenance, when it has one. Absent
280
+ * means the evidence was recorded directly by spec code.
281
+ */
282
+ emittedBy?: string;
283
+ }
284
+ interface ValidationIssue {
285
+ category: ValidationCategory;
286
+ /** Human-readable summary including expected vs found. */
287
+ message: string;
288
+ /** Structured context for machine consumers (optional). */
289
+ context?: Record<string, unknown>;
290
+ }
291
+ interface ValidationResult {
292
+ missionId: string;
293
+ ok: boolean;
294
+ issues: ValidationIssue[];
295
+ /** Attribution for every `trace_must_prove` entry, in manifest order. */
296
+ evidence?: RequirementEvidence[];
297
+ }
298
+
299
+ export { type CapabilityRequirement as C, MISSION_MANIFEST_SCHEMA_VERSION as M, type SchemaRequirement as S, type TraceRequirement as T, type ValidationResult as V, type MissionManifest as a, type MissionRequirements as b, type ValidationCategory as c, type ValidationIssue as d };
package/dist/node.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { V as ValidationResult } from './mission-types-Dr5rBHYO.js';
2
+ import { T as TraceArtifact, a as TraceAssertion } from './trace-types-DkIzq-l9.js';
3
+ import './portable-vocabulary.js';
4
+
5
+ interface CapabilitiesArtifact {
6
+ schemaVersion: number;
7
+ capabilities: Array<{
8
+ name: string;
9
+ module: string;
10
+ /** Literal exported wrapper function used by the generated invoke registry. */
11
+ exportName?: string | null;
12
+ verb: string | null;
13
+ object: string | null;
14
+ invariants: readonly string[];
15
+ /** Derived scanner signal; absent on older additive v1 artifacts. */
16
+ acceptsWorkspaceId?: boolean;
17
+ /** Internal BOT/server-only plumbing is not a user-invoked boundary. */
18
+ internalOnly?: boolean;
19
+ /** Direct next/cache updateTag is not safe through the proof route. */
20
+ usesDirectUpdateTag?: boolean;
21
+ /** Concrete writes issued through createSupabaseServiceClient(). */
22
+ serviceRoleMutations?: ReadonlyArray<{
23
+ table: string;
24
+ operation: "insert" | "upsert" | "update" | "delete";
25
+ }>;
26
+ /** Relevant guards observed in the action pipeline. */
27
+ middleware?: {
28
+ auth: boolean;
29
+ tenantIsolation: boolean;
30
+ rbac: boolean;
31
+ };
32
+ file: string;
33
+ }>;
34
+ unclassified?: readonly unknown[];
35
+ }
36
+ interface SchemaPolicy {
37
+ name: string;
38
+ /** One of POLICY_COMMANDS as the parser reports it. */
39
+ command: string;
40
+ /** Roles from the policy's `TO` clause. Empty means no clause, i.e. PUBLIC. */
41
+ roles: readonly string[];
42
+ }
43
+ interface SchemaArtifact {
44
+ schemaVersion: number;
45
+ tables: Array<{
46
+ name: string;
47
+ columns: readonly string[];
48
+ rls_classification: string;
49
+ policies: readonly SchemaPolicy[];
50
+ sourceFiles: readonly string[];
51
+ }>;
52
+ unclassified?: readonly unknown[];
53
+ }
54
+ /**
55
+ * A trace bundle for validation is the aggregated list of every trace the
56
+ * proof run emitted. Kept as `TraceArtifact[]` so v0.6's per-proof JSON
57
+ * files can be concatenated without transformation.
58
+ */
59
+ type TraceBundle = readonly TraceArtifact[];
60
+ /**
61
+ * An assertion plus the proof it came from. Attribution has to survive the
62
+ * flattening, otherwise a satisfied requirement can't be traced back to the
63
+ * spec that satisfied it.
64
+ */
65
+ interface AttributedAssertion extends TraceAssertion {
66
+ proofId: string;
67
+ specFile?: string;
68
+ }
69
+ /** Flatten every assertion across every trace / step, keeping attribution. */
70
+ declare function collectAssertions(bundle: TraceBundle): AttributedAssertion[];
71
+ interface ValidateMissionInput {
72
+ manifest: unknown;
73
+ capabilities: CapabilitiesArtifact;
74
+ schema: SchemaArtifact;
75
+ traces: TraceBundle;
76
+ }
77
+ declare function validateMission(input: ValidateMissionInput): ValidationResult;
78
+
79
+ export { type CapabilitiesArtifact, type SchemaArtifact, type TraceBundle, type ValidateMissionInput, collectAssertions, validateMission };