@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.
- package/dist/baselines/adoption.d.ts +70 -3
- package/dist/baselines/adoption.d.ts.map +1 -1
- package/dist/baselines/adoption.js +144 -5
- package/dist/baselines/adoption.js.map +1 -1
- package/dist/classifier/classify.d.ts +3 -0
- package/dist/classifier/classify.d.ts.map +1 -1
- package/dist/classifier/classify.js +39 -18
- package/dist/classifier/classify.js.map +1 -1
- package/dist/classifier/schema.d.ts +1 -0
- package/dist/classifier/schema.d.ts.map +1 -1
- package/dist/config/index.d.ts +46 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +51 -0
- package/dist/config/index.js.map +1 -1
- package/dist/graph/schema.d.ts +2 -0
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/mapping/resolve.d.ts +28 -0
- package/dist/mapping/resolve.d.ts.map +1 -1
- package/dist/mapping/resolve.js +66 -25
- package/dist/mapping/resolve.js.map +1 -1
- package/dist/policy/business-rules.d.ts +18 -0
- package/dist/policy/business-rules.d.ts.map +1 -1
- package/dist/policy/business-rules.js +23 -0
- package/dist/policy/business-rules.js.map +1 -1
- package/dist/policy/index.d.ts +1 -1
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +1 -1
- package/dist/policy/index.js.map +1 -1
- package/dist/schemas/adoption.d.ts +43 -0
- package/dist/schemas/adoption.d.ts.map +1 -1
- package/dist/schemas/adoption.js +102 -0
- package/dist/schemas/adoption.js.map +1 -1
- package/dist/schemas/classification.d.ts +7 -8
- package/dist/schemas/classification.d.ts.map +1 -1
- package/dist/schemas/classification.js +15 -20
- package/dist/schemas/classification.js.map +1 -1
- package/dist/verdict/cause.d.ts.map +1 -1
- package/dist/verdict/cause.js +30 -6
- package/dist/verdict/cause.js.map +1 -1
- package/dist/verdict/evaluate.d.ts.map +1 -1
- package/dist/verdict/evaluate.js +209 -44
- package/dist/verdict/evaluate.js.map +1 -1
- 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
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
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
|
|
114
|
+
* Which evidence lane proves a user-facing resource. Omitted means
|
|
118
115
|
* the default `'adapter'` lane: a reviewed EntityAdapter must collect
|
|
119
|
-
*
|
|
120
|
-
* `'
|
|
121
|
-
*
|
|
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":"AAAA
|
|
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":"
|
|
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"}
|
package/dist/verdict/cause.js
CHANGED
|
@@ -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
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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":"AAAA
|
|
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":"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
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/verdict/evaluate.ts"],"names":[],"mappings":"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"}
|
package/dist/verdict/evaluate.js
CHANGED
|
@@ -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
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
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))
|