@gate-forge/core 0.11.1 → 0.13.0

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 (47) hide show
  1. package/dist/baselines/adoption.d.ts +70 -3
  2. package/dist/baselines/adoption.d.ts.map +1 -1
  3. package/dist/baselines/adoption.js +144 -5
  4. package/dist/baselines/adoption.js.map +1 -1
  5. package/dist/classifier/classify.d.ts +3 -0
  6. package/dist/classifier/classify.d.ts.map +1 -1
  7. package/dist/classifier/classify.js +39 -18
  8. package/dist/classifier/classify.js.map +1 -1
  9. package/dist/classifier/schema.d.ts +1 -0
  10. package/dist/classifier/schema.d.ts.map +1 -1
  11. package/dist/config/index.d.ts +46 -0
  12. package/dist/config/index.d.ts.map +1 -1
  13. package/dist/config/index.js +51 -0
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/graph/schema.d.ts +2 -0
  16. package/dist/graph/schema.d.ts.map +1 -1
  17. package/dist/index.d.ts +12 -4
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +10 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/mapping/resolve.d.ts +28 -0
  22. package/dist/mapping/resolve.d.ts.map +1 -1
  23. package/dist/mapping/resolve.js +66 -25
  24. package/dist/mapping/resolve.js.map +1 -1
  25. package/dist/policy/business-rules.d.ts +18 -0
  26. package/dist/policy/business-rules.d.ts.map +1 -1
  27. package/dist/policy/business-rules.js +23 -0
  28. package/dist/policy/business-rules.js.map +1 -1
  29. package/dist/policy/index.d.ts +1 -1
  30. package/dist/policy/index.d.ts.map +1 -1
  31. package/dist/policy/index.js +1 -1
  32. package/dist/policy/index.js.map +1 -1
  33. package/dist/schemas/adoption.d.ts +43 -0
  34. package/dist/schemas/adoption.d.ts.map +1 -1
  35. package/dist/schemas/adoption.js +102 -0
  36. package/dist/schemas/adoption.js.map +1 -1
  37. package/dist/schemas/classification.d.ts +7 -8
  38. package/dist/schemas/classification.d.ts.map +1 -1
  39. package/dist/schemas/classification.js +15 -20
  40. package/dist/schemas/classification.js.map +1 -1
  41. package/dist/verdict/cause.d.ts.map +1 -1
  42. package/dist/verdict/cause.js +30 -6
  43. package/dist/verdict/cause.js.map +1 -1
  44. package/dist/verdict/evaluate.d.ts.map +1 -1
  45. package/dist/verdict/evaluate.js +209 -44
  46. package/dist/verdict/evaluate.js.map +1 -1
  47. package/package.json +1 -1
@@ -8,14 +8,11 @@
8
8
  * `primaryKey` attribute;
9
9
  * - internal resources default to NO CRUD obligations and their claims
10
10
  * are invalid (enforced downstream by the verdict engine, not here);
11
- * - a user-facing resource cannot be proven without a trusted evidence
12
- * adapter, so `evidenceAdapter` is mandatory for user-facing entries —
13
- * on the BUSINESS-resource lane. `http.endpoint` resources are witnessed
14
- * through the claims/witness-proxy lane (`http:frontend-request-observed`
15
- * etc.), not through entity persistence adapters (whose contract — read
16
- * by id, normalize body, deletion kind — is about business entities), so
17
- * a user-facing classification may instead declare
18
- * `evidenceLane: 'claims'` and omit the adapter.
11
+ * - a user-facing resource cannot be proven without trusted evidence;
12
+ * business resources therefore require `evidenceAdapter`. `http.endpoint`
13
+ * resources use the claims/witness-proxy lane, while reader-resolved
14
+ * `ui.page` resources use browser observation. Those route lanes omit the
15
+ * persistence adapter, whose contract is about business entities.
19
16
  */
20
17
  import { z } from 'zod';
21
18
  import { ExposureSchema, PlaneSchema, SchemaVersionField } from './common.js';
@@ -101,9 +98,9 @@ export const ClassificationSchema = z
101
98
  /** Lifecycle operations the UI supports for this resource. */
102
99
  lifecycle: LifecycleSchema,
103
100
  /**
104
- * Mandatory ordered entity-identity columns (ADR 0001): a single
105
- * column for simple identity, ordered lowest-index-first for
106
- * composite identity.
101
+ * Ordered identity columns for persisted resources (ADR 0001). The
102
+ * classifier supplies `['path']` for `ui.page`: a route's structural
103
+ * key, not persistence identity evidence.
107
104
  */
108
105
  primaryKey: z.array(z.string().min(1)).min(1),
109
106
  /**
@@ -114,16 +111,13 @@ export const ClassificationSchema = z
114
111
  */
115
112
  evidenceAdapter: z.string().min(1).optional(),
116
113
  /**
117
- * Which evidence lane proves user-facing reachability. Omitted means
114
+ * Which evidence lane proves a user-facing resource. Omitted means
118
115
  * the default `'adapter'` lane: a reviewed EntityAdapter must collect
119
- * the evidence (the business-resource requirement — never weakened).
120
- * `'claims'` is the http.endpoint lane: routes are witnessed through
121
- * the claims/witness-proxy lane (`http:frontend-request-observed`),
122
- * not through entity persistence adapters, so the adapter is not
123
- * demanded. The classifier mints `'claims'` ONLY for `http.endpoint`
124
- * resources; business resources must keep the adapter demand.
116
+ * persistence evidence. `'claims'` is the HTTP endpoint lane;
117
+ * `'page-observation'` is the witness's observation-only UI page lane.
118
+ * Neither route lane uses an EntityAdapter.
125
119
  */
126
- evidenceLane: z.enum(['adapter', 'claims']).optional(),
120
+ evidenceLane: z.enum(['adapter', 'claims', 'page-observation']).optional(),
127
121
  /** Free-form classification note shown in reports. */
128
122
  notes: z.string().optional(),
129
123
  })
@@ -131,7 +125,8 @@ export const ClassificationSchema = z
131
125
  .superRefine((entry, ctx) => {
132
126
  if (entry.exposure === 'user-facing' &&
133
127
  entry.evidenceAdapter === undefined &&
134
- entry.evidenceLane !== 'claims') {
128
+ entry.evidenceLane !== 'claims' &&
129
+ entry.evidenceLane !== 'page-observation') {
135
130
  ctx.addIssue({
136
131
  code: 'custom',
137
132
  path: ['evidenceAdapter'],
@@ -1 +1 @@
1
- {"version":3,"file":"classification.js","sourceRoot":"","sources":["../../src/schemas/classification.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"classification.js","sourceRoot":"","sources":["../../src/schemas/classification.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cause.d.ts","sourceRoot":"","sources":["../../src/verdict/cause.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE3E,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA2CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GAAG,YAAY,CAsCf;AAOD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAClD,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAiDpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,SAAS;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB,EAAE,GAAG,KAAK,CAAC,aAAa,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAcpD"}
1
+ {"version":3,"file":"cause.d.ts","sourceRoot":"","sources":["../../src/verdict/cause.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE3E,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAwDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GAAG,YAAY,CAoDf;AAOD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAClD,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAiDpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,SAAS;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB,EAAE,GAAG,KAAK,CAAC,aAAa,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAcpD"}
@@ -7,12 +7,13 @@
7
7
  * a connected test whose required observations are absent
8
8
  * (EVIDENCE_NOT_COLLECTED — including the crud session-channel rules:
9
9
  * direct mutation outside the supervised channel, no visible result, no
10
- * session-bound anchor), an obligation no test is connected to
11
- * (TEST_MAPPING_MISSING), and the §3.6 exact-value echo violation
12
- * (EVIDENCE_VALUE_MISMATCH). Remaining causes are populated by the
13
- * phases that build their subsystems (catalog/mapping: Phase 2-3;
14
- * execution results: Phase 4); an unmapped blocking verdict carries a
15
- * null cause rather than a guess.
10
+ * session-bound anchor — and, since 0.13, a MISSING page promise, whose
11
+ * action names the witnessed page visit / post-suite sweep), an
12
+ * obligation no test is connected to (TEST_MAPPING_MISSING), and the
13
+ * §3.6 exact-value echo violation (EVIDENCE_VALUE_MISMATCH). Remaining
14
+ * causes are populated by the phases that build their subsystems
15
+ * (catalog/mapping: Phase 2-3; execution results: Phase 4); an unmapped
16
+ * blocking verdict carries a null cause rather than a guess.
16
17
  *
17
18
  * The mapping matches the verifier reasons verbatim — those strings are
18
19
  * engine-owned (single grading sites, kept in lockstep by tests), never
@@ -49,6 +50,17 @@ const EVIDENCE_NOT_COLLECTED_PATTERNS = [
49
50
  * session-channel verifier).
50
51
  */
51
52
  const EVIDENCE_VALUE_MISMATCH_PATTERN = 'exact-value echo violation (EVIDENCE_VALUE_MISMATCH)';
53
+ /**
54
+ * The page-promise evidence action (0.13): a missing `page:loads`/
55
+ * `page:data-ok` is a visit nobody witnessed — the actionable fix is a
56
+ * witnessed page visit through a browser test or the engine's own
57
+ * post-suite sweep, never "drop the pack" (the proof channel exists and
58
+ * is available). Kept beside the other verbatim grading-site strings;
59
+ * the page missing-reason text lives in exactly one grading site
60
+ * (`gradePageObligation`).
61
+ */
62
+ const PAGE_EVIDENCE_NEXT_ACTION = 'Run a witnessed page visit (a browser test through the Gateforge Playwright fixture) or let ' +
63
+ "the engine's post-suite sweep visit the route; mappings (`tests mark`) are intent, not proof.";
52
64
  /**
53
65
  * A crud session exchange that cannot be attributed because the host
54
66
  * supplied no route inventory (plan §9, D2): the proof channel the
@@ -117,6 +129,18 @@ export function causeForVerdict(params) {
117
129
  if (capability === null || capability.availability.status === 'unavailable' || !implemented) {
118
130
  return nextActionFor('VERIFIER_UNSUPPORTED');
119
131
  }
132
+ // The page namespace implements exactly `page:loads`/`page:data-ok`
133
+ // (any other `page:*` contract is unimplemented and already returned
134
+ // VERIFIER_UNSUPPORTED above). A MISSING page promise is absent
135
+ // evidence — a witnessed page visit or the engine's post-suite sweep
136
+ // has not produced a clean record — never an unsupported verifier, so
137
+ // it maps to EVIDENCE_NOT_COLLECTED with the page-specific action.
138
+ // Other page verdicts (a witness REFUSAL, for one) keep the generic
139
+ // mapping below: their reasons are precise and need no guessed cause.
140
+ if ((params.contract === 'page:loads' || params.contract === 'page:data-ok') &&
141
+ params.verdict === 'missing') {
142
+ return { cause: 'EVIDENCE_NOT_COLLECTED', nextAction: PAGE_EVIDENCE_NEXT_ACTION };
143
+ }
120
144
  const reason = params.reason ?? '';
121
145
  if (reason.includes(EVIDENCE_VALUE_MISMATCH_PATTERN)) {
122
146
  return nextActionFor('EVIDENCE_VALUE_MISMATCH');
@@ -1 +1 @@
1
- {"version":3,"file":"cause.js","sourceRoot":"","sources":["../../src/verdict/cause.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cause.js","sourceRoot":"","sources":["../../src/verdict/cause.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/verdict/evaluate.ts"],"names":[],"mappings":"AAuDA,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AAOvB,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AA6CjE,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,EAAE,SAAS,OAAO,EAM/C,CAAC;AAEF;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,8DAA8D;AAC9D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnE,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,4CAA4C;IAC5C,UAAU,EAAE,UAAU,CAAC;IACvB,yEAAyE;IACzE,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD,gEAAgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAClC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IAC5B,wEAAwE;IACxE,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,wEAAwE;IACxE,cAAc,EAAE,OAAO,CAAC;IACxB,uIAAuI;IACvI,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IACxE;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAAC;IAClD;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC5C;;;OAGG;IACH,oBAAoB,CAAC,EAAE,SAAS;QAC9B,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,EAAE,CAAC;IACJ,mEAAmE;IACnE,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAcrD;AA0TD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAU1D;AAkCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,OAAO,EACrB,iBAAiB,EAAE,OAAO,GACzB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAUhE;AA84CD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,cAAc,GACtB,cAAc,CAmNhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,OAAO,EAAE,cAAc,GACtB,iBAAiB,EAAE,CA2CrB"}
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/verdict/evaluate.ts"],"names":[],"mappings":"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"}
@@ -801,6 +801,31 @@ const CRUD_CAPABILITY = {
801
801
  };
802
802
  registerContractCapabilities(PERSISTENCE_CAPABILITY);
803
803
  registerContractCapabilities(CRUD_CAPABILITY);
804
+ /**
805
+ * Capability metadata for the page namespace (0.13): the two page
806
+ * promises are proven through witnessed page visits — the witness's own
807
+ * Chromium CDP drive of a passing test's page (channel `observed`) or
808
+ * the engine's independent post-suite sweep of routes without clean
809
+ * test proof (channel `swept`). The registry lists EXACTLY
810
+ * `page:loads`/`page:data-ok`: any other `page:*` contract stays
811
+ * unimplemented, so it keeps mapping to VERIFIER_UNSUPPORTED — the
812
+ * capability never pretends a proof channel exists for a contract the
813
+ * page grader does not grade. Adapter-free by design: page grading
814
+ * needs no business classification, so the capability record carries no
815
+ * classifier requirement either.
816
+ */
817
+ const PAGE_CAPABILITY = {
818
+ namespace: 'page',
819
+ contracts: ['page:loads', 'page:data-ok'],
820
+ unavailableContracts: [],
821
+ observer: 'the witness CDP drive: settled, route-matched visits recorded by the independent witness ' +
822
+ 'browser (channel observed, from a passing test) or the engine\'s own post-suite sweep of ' +
823
+ 'routes without clean test proof (channel swept) — both graded by the same route, redirect, ' +
824
+ 'crash, error-marker and API-response rules; suite-submitted page records never substitute',
825
+ testKinds: ['browser-e2e', 'observed-e2e'],
826
+ availability: { status: 'available' },
827
+ };
828
+ registerContractCapabilities(PAGE_CAPABILITY);
804
829
  /**
805
830
  * The built-in persistence grader: dispatches between the three evidence
806
831
  * channels an obligation's claim may be proven through.
@@ -1705,6 +1730,170 @@ function gradeAlembicObligation(obligation, context) {
1705
1730
  recordIds: failed?.recordId === undefined ? [] : [failed.recordId],
1706
1731
  };
1707
1732
  }
1733
+ /**
1734
+ * Grades `page:loads` / `page:data-ok` obligations from engine-issued
1735
+ * page.observed records. Chronology rule (0.13): observations from the
1736
+ * SAME test resolve to only their LATEST `observationSequence`, so an
1737
+ * early clean visit cannot survive a later refusal by the same test,
1738
+ * while a different passing test's latest clean record still satisfies.
1739
+ * The sequence must be a nonnegative integer and the payload must carry
1740
+ * `apiRequestsSettled: true` before a record can satisfy. The engine
1741
+ * stamps both onto every payload, but they fail differently: a missing
1742
+ * or malformed sequence makes the record INADMISSIBLE — it can neither
1743
+ * satisfy nor carry a refusal — while a valid-sequence record whose
1744
+ * settled flag is missing or false still participates in the latest
1745
+ * selection and is REFUSED with typed `PAGE_API_UNSETTLED`.
1746
+ * Unsettled or refused latest records remain refusals, never proof.
1747
+ * Grouping is by testId with a max-sequence reduction and sorted test
1748
+ * iteration, so input array order never affects the outcome and no
1749
+ * clock is read. Duplicates at one test's top sequence are admitted
1750
+ * only when their payloads are canonically identical (the engine reuses
1751
+ * ONE payload for both page promises); a contradiction fails that test
1752
+ * closed.
1753
+ */
1754
+ function gradePageObligation(obligation, records) {
1755
+ const verdictKey = obligation.contract === 'page:loads' ? 'loads' : 'dataOk';
1756
+ const pageRecords = records
1757
+ .map(asRecord)
1758
+ .filter((record) => record !== null &&
1759
+ record.obligationId === obligation.id &&
1760
+ record.kind === 'page.observed' &&
1761
+ record.origin === 'engine-observed' &&
1762
+ record.testId !== undefined &&
1763
+ trustOf(record) === 'witnessed' &&
1764
+ payloadOf(record)?.['routeId'] === obligation.resourceId);
1765
+ const latestByTest = new Map();
1766
+ for (const record of pageRecords) {
1767
+ const testId = typeof record.testId === 'string' ? record.testId : null;
1768
+ const rawSequence = payloadOf(record)?.['observationSequence'];
1769
+ const sequence = typeof rawSequence === 'number' && Number.isInteger(rawSequence) && rawSequence >= 0
1770
+ ? rawSequence
1771
+ : null;
1772
+ if (testId === null || sequence === null)
1773
+ continue;
1774
+ const current = latestByTest.get(testId);
1775
+ if (current === undefined || current.sequence < sequence) {
1776
+ latestByTest.set(testId, { testId, sequence, record, contradictory: false });
1777
+ continue;
1778
+ }
1779
+ if (current.sequence !== sequence || current.contradictory)
1780
+ continue;
1781
+ const fresh = payloadOf(record);
1782
+ const held = payloadOf(current.record);
1783
+ // Canonical payload equality is the deterministic "prove identical"
1784
+ // check: a payload with no JSON representation never proves it.
1785
+ const differs = !isJsonValue(fresh) || !isJsonValue(held) ||
1786
+ canonicalJson(fresh) !== canonicalJson(held);
1787
+ if (differs)
1788
+ latestByTest.set(testId, { ...current, contradictory: true });
1789
+ }
1790
+ const observations = [...latestByTest.values()].sort((a, b) => compareStrings(a.testId, b.testId));
1791
+ let satisfiedId = null;
1792
+ for (const observation of observations) {
1793
+ if (observation.contradictory)
1794
+ continue;
1795
+ const payload = payloadOf(observation.record);
1796
+ if (payload === undefined || payload['apiRequestsSettled'] !== true)
1797
+ continue;
1798
+ const pageVerdict = payload[verdictKey];
1799
+ if (!isPlainObject(pageVerdict) || pageVerdict['satisfied'] !== true ||
1800
+ !Array.isArray(pageVerdict['refusalReasons']) || pageVerdict['refusalReasons'].length !== 0)
1801
+ continue;
1802
+ if (typeof observation.record.recordId !== 'string')
1803
+ continue;
1804
+ satisfiedId = observation.record.recordId;
1805
+ break;
1806
+ }
1807
+ if (satisfiedId !== null) {
1808
+ return { verdict: 'satisfied', reason: null, recordIds: [satisfiedId] };
1809
+ }
1810
+ const refused = observations[0];
1811
+ if (refused !== undefined) {
1812
+ const payload = payloadOf(refused.record);
1813
+ const pageVerdict = payload?.[verdictKey];
1814
+ const reasons = isPlainObject(pageVerdict) && Array.isArray(pageVerdict['refusalReasons'])
1815
+ ? pageVerdict['refusalReasons'].filter((reason) => typeof reason === 'string')
1816
+ : [];
1817
+ if (payload?.['apiRequestsSettled'] !== true)
1818
+ reasons.push('PAGE_API_UNSETTLED');
1819
+ if (refused.contradictory) {
1820
+ reasons.push(`PAGE_OBSERVATION_CONFLICT (test '${refused.testId}' carries different payloads at ` +
1821
+ `observation sequence ${refused.sequence})`);
1822
+ }
1823
+ return {
1824
+ verdict: 'invalid',
1825
+ reason: reasons.length > 0 ? `witness refused page observation: ${reasons.join(', ')}` : 'witness page observation was not clean',
1826
+ recordIds: typeof refused.record.recordId === 'string' ? [refused.record.recordId] : [],
1827
+ };
1828
+ }
1829
+ return {
1830
+ verdict: 'missing',
1831
+ reason: `no clean witness-observed page visit from a passing test exists for '${obligation.resourceId}'`,
1832
+ recordIds: [],
1833
+ };
1834
+ }
1835
+ /**
1836
+ * The waiver evaluation shared by the ordinary lane and the adapter-free
1837
+ * page lane: exact (resourceId, fingerprint) scope only (D4). Precedence:
1838
+ * unexpired non-stale → waived; expired → invalid (D4); stale owner →
1839
+ * stale (GF-17). Sorted for determinism. The reason strings are part of
1840
+ * the pinned verdict contract — never reworded at one call site.
1841
+ *
1842
+ * Args:
1843
+ * verified: the schema-validated obligation under evaluation.
1844
+ * waivers: the run's waivers (with the engine-only stale-owner flag).
1845
+ * now: the parsed injected run instant.
1846
+ *
1847
+ * Returns:
1848
+ * VerdictOutcome | null: the waiver's outcome, or null when no waiver
1849
+ * speaks for this obligation (grading continues).
1850
+ */
1851
+ function waiverOutcome(verified, waivers, now) {
1852
+ const fp = fingerprintObligation(verified);
1853
+ const matching = waivers
1854
+ .map((entry) => {
1855
+ // Strip the engine-only flag before strict validation; a waiver
1856
+ // entry the schema rejects can never match exactly, so it degrades.
1857
+ const { ownerStale, ...plain } = entry;
1858
+ const parsed = WaiverSchema.safeParse(plain);
1859
+ return parsed.success ? { ownerStale: Boolean(ownerStale), waiver: parsed.data } : null;
1860
+ })
1861
+ .filter((entry) => entry !== null)
1862
+ .filter(({ waiver }) => waiver.scope.kind === 'exact' &&
1863
+ waiver.scope.resourceId === verified.resourceId &&
1864
+ waiver.scope.fingerprint === fp)
1865
+ .sort((a, b) => compareStrings(`${a.waiver.expiresAt}\u0000${a.waiver.owner}`, `${b.waiver.expiresAt}\u0000${b.waiver.owner}`));
1866
+ const unexpired = matching.filter((entry) => now.getTime() < Date.parse(entry.waiver.expiresAt) && !entry.ownerStale);
1867
+ if (unexpired.length > 0 && unexpired[0] !== undefined) {
1868
+ const waiver = unexpired[0].waiver;
1869
+ return {
1870
+ verdict: 'waived',
1871
+ reason: `waived by '${waiver.owner}' until '${waiver.expiresAt}' ` +
1872
+ `(approver '${waiver.approver}', ${waiver.justificationUrl})`,
1873
+ recordIds: [],
1874
+ };
1875
+ }
1876
+ const expired = matching.filter((entry) => now.getTime() >= Date.parse(entry.waiver.expiresAt) && !entry.ownerStale);
1877
+ if (expired.length > 0 && expired[0] !== undefined) {
1878
+ const waiver = expired[0].waiver;
1879
+ return {
1880
+ verdict: 'invalid',
1881
+ reason: `waiver by '${waiver.owner}' expired at '${waiver.expiresAt}'; expired waivers block ` +
1882
+ `as 'invalid' (ADR 0001 D4), obligation '${verified.id}'`,
1883
+ recordIds: [],
1884
+ };
1885
+ }
1886
+ const staleOwner = matching.find((entry) => entry.ownerStale);
1887
+ if (staleOwner !== undefined) {
1888
+ return {
1889
+ verdict: 'stale',
1890
+ reason: `waiver owner '${staleOwner.waiver.owner}' is stale (owner check failed); renewal with a new ` +
1891
+ `review is required (GF-17), obligation '${verified.id}'`,
1892
+ recordIds: [],
1893
+ };
1894
+ }
1895
+ return null;
1896
+ }
1708
1897
  /**
1709
1898
  * Evaluates ONE obligation against the run's claims, records, waivers,
1710
1899
  * classification, and injected clock (pin #9). Pure and deterministic:
@@ -1734,6 +1923,20 @@ export function evaluateObligation(obligation, context) {
1734
1923
  if (verified.contract.startsWith('alembic:')) {
1735
1924
  return gradeAlembicObligation(verified, context);
1736
1925
  }
1926
+ if (verified.contract === 'page:loads' || verified.contract === 'page:data-ok') {
1927
+ // Adapter-free page lane: page promises grade on witnessed page
1928
+ // visits alone — no classification/exposure gate (the page reader's
1929
+ // contract). A WAIVER is owner intent about the obligation itself,
1930
+ // though, so it applies here exactly as on the ordinary lane: same
1931
+ // exact scope, expiry, and stale-owner rules, decided BEFORE the
1932
+ // evidence grade. A revoked waiver simply stops speaking, and the
1933
+ // page grades on its evidence again — it never turns into adopted
1934
+ // debt through the waiver path.
1935
+ const waiver = waiverOutcome(verified, context.waivers, parseInstant(context.now));
1936
+ if (waiver !== null)
1937
+ return waiver;
1938
+ return gradePageObligation(verified, context.records ?? []);
1939
+ }
1737
1940
  const now = parseInstant(context.now);
1738
1941
  // 1. Unclassified resources block (invariant 1); unresolved resources
1739
1942
  // never reach this evaluator (the policy engine emits blocking
@@ -1770,50 +1973,12 @@ export function evaluateObligation(obligation, context) {
1770
1973
  }
1771
1974
  // 3. Waivers: exact (resourceId, fingerprint) scope only (D4).
1772
1975
  // Precedence: unexpired non-stale → waived; expired → invalid (D4);
1773
- // stale owner → stale (GF-17). Sorted for determinism.
1774
- const fp = fingerprintObligation(verified);
1775
- const matching = context.waivers
1776
- .map((entry) => {
1777
- // Strip the engine-only flag before strict validation; a waiver
1778
- // entry the schema rejects can never match exactly, so it degrades.
1779
- const { ownerStale, ...plain } = entry;
1780
- const parsed = WaiverSchema.safeParse(plain);
1781
- return parsed.success ? { ownerStale: Boolean(ownerStale), waiver: parsed.data } : null;
1782
- })
1783
- .filter((entry) => entry !== null)
1784
- .filter(({ waiver }) => waiver.scope.kind === 'exact' &&
1785
- waiver.scope.resourceId === verified.resourceId &&
1786
- waiver.scope.fingerprint === fp)
1787
- .sort((a, b) => compareStrings(`${a.waiver.expiresAt}\u0000${a.waiver.owner}`, `${b.waiver.expiresAt}\u0000${b.waiver.owner}`));
1788
- const unexpired = matching.filter((entry) => now.getTime() < Date.parse(entry.waiver.expiresAt) && !entry.ownerStale);
1789
- if (unexpired.length > 0 && unexpired[0] !== undefined) {
1790
- const waiver = unexpired[0].waiver;
1791
- return {
1792
- verdict: 'waived',
1793
- reason: `waived by '${waiver.owner}' until '${waiver.expiresAt}' ` +
1794
- `(approver '${waiver.approver}', ${waiver.justificationUrl})`,
1795
- recordIds: [],
1796
- };
1797
- }
1798
- const expired = matching.filter((entry) => now.getTime() >= Date.parse(entry.waiver.expiresAt) && !entry.ownerStale);
1799
- if (expired.length > 0 && expired[0] !== undefined) {
1800
- const waiver = expired[0].waiver;
1801
- return {
1802
- verdict: 'invalid',
1803
- reason: `waiver by '${waiver.owner}' expired at '${waiver.expiresAt}'; expired waivers block ` +
1804
- `as 'invalid' (ADR 0001 D4), obligation '${verified.id}'`,
1805
- recordIds: [],
1806
- };
1807
- }
1808
- const staleOwner = matching.find((entry) => entry.ownerStale);
1809
- if (staleOwner !== undefined) {
1810
- return {
1811
- verdict: 'stale',
1812
- reason: `waiver owner '${staleOwner.waiver.owner}' is stale (owner check failed); renewal with a new ` +
1813
- `review is required (GF-17), obligation '${verified.id}'`,
1814
- recordIds: [],
1815
- };
1816
- }
1976
+ // stale owner → stale (GF-17). Sorted for determinism. The rule
1977
+ // lives in ONE helper ({@link waiverOutcome}) shared with the
1978
+ // adapter-free page lane, so both lanes can never drift.
1979
+ const waiver = waiverOutcome(verified, context.waivers, now);
1980
+ if (waiver !== null)
1981
+ return waiver;
1817
1982
  // 4. Claims on this obligation, deterministically ordered.
1818
1983
  const claims = context.claims
1819
1984
  .map((claim) => ClaimSchema.safeParse(claim))