@openwop/openwop-conformance 1.153.0 → 1.155.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/CHANGELOG.md +17 -0
- package/README.md +11 -2
- package/api/openapi.yaml +17 -1
- package/coverage.md +3 -0
- package/dist/cli.js +7 -2
- package/package.json +31 -2
- package/requirements.json +256 -20
- package/schemas/CORPUS-STAMP.json +100 -3
- package/schemas/README.md +1 -0
- package/schemas/capabilities.schema.json +7 -0
- package/schemas/run-event-payloads.schema.json +9 -0
- package/schemas/run-snapshot.schema.json +4 -0
- package/schemas/subject.schema.json +56 -0
- package/src/cli.ts +7 -5
- package/src/global-setup.ts +13 -0
- package/src/lib/corpus-stamp.ts +125 -0
- package/src/scenarios/anonymous-actor-shape.test.ts +6 -1
- package/src/scenarios/auth-subject-link.test.ts +20 -1
- package/src/scenarios/discovery.test.ts +12 -0
- package/src/scenarios/identity-owner-shape.test.ts +7 -3
- package/src/scenarios/owner-subject-echo.test.ts +127 -0
- package/src/scenarios/owner-subject-shape.test.ts +96 -0
- package/src/scenarios/protocol-versions-array.test.ts +103 -0
- package/src/scenarios/webhook-signed-delivery.test.ts +22 -0
- package/src/lib/capabilities-auth-subject-link.test.ts +0 -103
- package/src/lib/fork-availability.test.ts +0 -69
- package/src/lib/global-setup.test.ts +0 -76
- package/src/lib/grpc-framing.test.ts +0 -96
- package/src/lib/oidc-issuer.test.ts +0 -328
- package/src/lib/otel-collector-grpc.test.ts +0 -191
- package/src/lib/otel-collector.test.ts +0 -303
- package/src/lib/otlp-protobuf.test.ts +0 -461
- package/src/lib/polling.test.ts +0 -80
- package/src/lib/requirement-ids.test.ts +0 -83
- package/src/lib/requirement-ledger.test.ts +0 -75
- package/src/lib/risk-disposition.test.ts +0 -91
- package/src/lib/saml-idp.test.ts +0 -127
- package/src/lib/spec-coherence-registry.test.ts +0 -155
- package/src/lib/webhook-receiver.test.ts +0 -144
- package/src/scenarios/artifact-schema-compile-bounded.test.ts +0 -126
- package/src/scenarios/artifact-type-legacy-ids.test.ts +0 -124
- package/src/scenarios/capability-example-root-layout.test.ts +0 -272
- package/src/scenarios/certification-floor-enforcement.test.ts +0 -204
- package/src/scenarios/chain-subchain-unsupported-refused.test.ts +0 -70
- package/src/scenarios/compensation-profile.test.ts +0 -340
- package/src/scenarios/core-manifest-and-extension-registry.test.ts +0 -250
- package/src/scenarios/discovery-canonical-family-no-shadow.test.ts +0 -219
- package/src/scenarios/edge-condition-truthy-falsy.test.ts +0 -108
- package/src/scenarios/effect-identity-composition.test.ts +0 -129
- package/src/scenarios/effect-identity-cross-scope.test.ts +0 -82
- package/src/scenarios/error-envelope-canonical-shape.test.ts +0 -64
- package/src/scenarios/form-content-packs.test.ts +0 -415
- package/src/scenarios/multi-region-effect-vocabulary.test.ts +0 -175
- package/src/scenarios/normative-example-extraction.test.ts +0 -242
- package/src/scenarios/openapi-asyncapi-sdk-parity.test.ts +0 -309
- package/src/scenarios/pack-manifest-extensions.test.ts +0 -203
- package/src/scenarios/protocol-version-grammar.test.ts +0 -119
- package/src/scenarios/registry-declarative-kinds.test.ts +0 -121
- package/src/scenarios/rfc-0147-self-audit.test.ts +0 -104
- package/src/scenarios/rfc-lifecycle-coherence.test.ts +0 -215
- package/src/scenarios/semantic-digest-v2.test.ts +0 -128
- package/src/scenarios/spec-corpus-validity.test.ts +0 -1727
- package/src/scenarios/spec-section-citations.test.ts +0 -132
- package/src/scenarios/tool-result-trust-monotone.test.ts +0 -168
- package/src/scenarios/versioned-composition-profiles.test.ts +0 -201
- package/src/scenarios/workflow-chain-internal-flag.test.ts +0 -84
- package/src/scenarios/workload-identity-profile.test.ts +0 -184
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RFC 0149 §D — an `Accepted` RFC's unticked acceptance items must say why.
|
|
3
|
-
*
|
|
4
|
-
* §D asks the corpus generator to fail when an `Accepted` RFC "retains an
|
|
5
|
-
* unresolved acceptance blocker not explicitly carried to a register/known-limit".
|
|
6
|
-
* The obvious signal is the `- [ ]` boxes under §"Acceptance criteria", and the
|
|
7
|
-
* obvious gate — every box ticked before `Accepted` — was measured and rejected.
|
|
8
|
-
*
|
|
9
|
-
* `docs/RFC-LIFECYCLE-COHERENCE.md` recorded the distribution: of 141 `Accepted`
|
|
10
|
-
* RFCs, 42% ticked every box, 25% ticked none, 24% ticked some. A blanket gate
|
|
11
|
-
* fails 69 RFCs on its first run, mostly for an authoring convention, and a gate
|
|
12
|
-
* that fires 69 times on its first run gets disabled rather than fixed.
|
|
13
|
-
*
|
|
14
|
-
* The first triage hypothesis was also wrong, and correcting it produced the rule
|
|
15
|
-
* this gate actually enforces. The partially-ticked RFCs are not a blocker
|
|
16
|
-
* backlog: reading `0027`/`0028`/`0029`/`0040`/`0041` shows every trailing item
|
|
17
|
-
* deliberately unticked AND annotated with why — "(Will land alongside the first
|
|
18
|
-
* non-steward advertisement.)", "(Path-to-Accepted.)", "(Follow-up — … not
|
|
19
|
-
* normative gate-blockers.)". That inline annotation IS §D's "explicitly
|
|
20
|
-
* carried", just carried in a parenthetical rather than a register row.
|
|
21
|
-
*
|
|
22
|
-
* So the signal is **annotated vs bare, not ticked vs unticked**. An unticked
|
|
23
|
-
* item with no explanation is indistinguishable from one nobody checked; an
|
|
24
|
-
* unticked item that states its external gate is a decision on record. Ticking
|
|
25
|
-
* nothing stays legal — the RFC did not use the mechanism — but leaving a box
|
|
26
|
-
* unticked and unexplained does not.
|
|
27
|
-
*
|
|
28
|
-
* Applies from `LIFECYCLE_RULE_RFC` forward. Earlier RFCs are the dated record
|
|
29
|
-
* of a period when the convention did not exist, and rewriting them would make
|
|
30
|
-
* the record lie; the boundary is asserted here rather than assumed, so a NEW
|
|
31
|
-
* RFC cannot inherit the exemption. Same carve-out shape as RFC 0149 §B's
|
|
32
|
-
* root-layout lint.
|
|
33
|
-
*
|
|
34
|
-
* Server-free. `RFCS/` is repository-only, so this self-skips under the
|
|
35
|
-
* published tarball layout.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
import { describe, it, expect } from 'vitest';
|
|
39
|
-
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
40
|
-
import { join, resolve as pathResolve } from 'node:path';
|
|
41
|
-
import { V1_DIR } from '../lib/paths.js';
|
|
42
|
-
|
|
43
|
-
/** RFC 0149 §D takes effect with the RFC 0147 program's own cohort. */
|
|
44
|
-
const LIFECYCLE_RULE_RFC = 147;
|
|
45
|
-
|
|
46
|
-
const RFCS_DIR = V1_DIR === null ? null : pathResolve(V1_DIR, '..', '..', 'RFCS');
|
|
47
|
-
|
|
48
|
-
interface BareItem {
|
|
49
|
-
readonly rfc: number;
|
|
50
|
-
readonly file: string;
|
|
51
|
-
readonly line: number;
|
|
52
|
-
readonly text: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* An item is "carried" when it states a reason: a parenthetical, or a pointer to
|
|
57
|
-
* a gap register / known-limit / follow-up RFC. Anything else is bare.
|
|
58
|
-
*/
|
|
59
|
-
function isAnnotated(text: string): boolean {
|
|
60
|
-
if (/\([^)]{12,}\)/.test(text)) return true;
|
|
61
|
-
return /\b(gap register|known.limit|register row|carried|deferred|blocked on|gated on)\b/i.test(text);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function statusOf(doc: string): string | null {
|
|
65
|
-
const m = /^\|\s*\*\*Status\*\*\s*\|\s*`([^`]+)`/m.exec(doc);
|
|
66
|
-
return m === null ? null : m[1]!;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Unticked acceptance items lacking a stated reason, for `Accepted` RFCs at or after the cutoff. */
|
|
70
|
-
function bareItems(dir: string): BareItem[] {
|
|
71
|
-
const found: BareItem[] = [];
|
|
72
|
-
for (const name of readdirSync(dir).filter((f) => /^\d{4}-.*\.md$/.test(f)).sort()) {
|
|
73
|
-
const rfc = Number.parseInt(name.slice(0, 4), 10);
|
|
74
|
-
if (!Number.isFinite(rfc) || rfc < LIFECYCLE_RULE_RFC) continue;
|
|
75
|
-
const doc = readFileSync(join(dir, name), 'utf8');
|
|
76
|
-
if (statusOf(doc) !== 'Accepted') continue;
|
|
77
|
-
const lines = doc.split('\n');
|
|
78
|
-
let inAcceptance = false;
|
|
79
|
-
for (let i = 0; i < lines.length; i++) {
|
|
80
|
-
const line = lines[i]!;
|
|
81
|
-
if (/^##\s/.test(line)) inAcceptance = /^##\s+Acceptance criteria\s*$/.test(line);
|
|
82
|
-
if (!inAcceptance) continue;
|
|
83
|
-
if (!/^\s*-\s*\[ \]\s/.test(line)) continue;
|
|
84
|
-
const text = line.replace(/^\s*-\s*\[ \]\s*/, '');
|
|
85
|
-
if (!isAnnotated(text)) found.push({ rfc, file: name, line: i + 1, text });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return found;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
describe.skipIf(RFCS_DIR === null || !existsSync(RFCS_DIR))('RFC 0149 §D — lifecycle coherence', () => {
|
|
92
|
-
const dir = RFCS_DIR as string;
|
|
93
|
-
|
|
94
|
-
it('the scan reaches Accepted RFCs in the governed range', () => {
|
|
95
|
-
// Guard: a status regex that matched nothing, or a heading regex that never
|
|
96
|
-
// entered the section, would make the assertion below vacuously true. That
|
|
97
|
-
// is the failure RFC 0148 exists to close, and it is especially easy here
|
|
98
|
-
// because the gate's PASSING state and its BROKEN state look identical.
|
|
99
|
-
const governed = readdirSync(dir)
|
|
100
|
-
.filter((f) => /^\d{4}-.*\.md$/.test(f))
|
|
101
|
-
.filter((f) => Number.parseInt(f.slice(0, 4), 10) >= LIFECYCLE_RULE_RFC)
|
|
102
|
-
.filter((f) => statusOf(readFileSync(join(dir, f), 'utf8')) === 'Accepted');
|
|
103
|
-
expect(
|
|
104
|
-
governed.length,
|
|
105
|
-
`at least one Accepted RFC numbered >= ${LIFECYCLE_RULE_RFC} MUST exist for this gate to mean anything`,
|
|
106
|
-
).toBeGreaterThan(0);
|
|
107
|
-
|
|
108
|
-
const withBoxes = governed.filter((f) =>
|
|
109
|
-
/^##\s+Acceptance criteria\s*$/m.test(readFileSync(join(dir, f), 'utf8')),
|
|
110
|
-
);
|
|
111
|
-
expect(withBoxes.length, 'the acceptance-criteria heading MUST be found').toBeGreaterThan(0);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('every unticked acceptance item states why it is unticked', () => {
|
|
115
|
-
const bare = bareItems(dir).map((b) => `RFCS/${b.file}:${b.line} — ${b.text}`);
|
|
116
|
-
expect(
|
|
117
|
-
bare,
|
|
118
|
-
'RFC 0149 §D: an unticked acceptance item on an `Accepted` RFC MUST carry its reason — an ' +
|
|
119
|
-
'external gate, a follow-up note, or a register / known-limit pointer. Unticked-and-' +
|
|
120
|
-
'unexplained is indistinguishable from unchecked, which is what makes the checkbox ' +
|
|
121
|
-
'signal unusable. Tick it, annotate it, or carry it to a register row.\n ' +
|
|
122
|
-
bare.join('\n '),
|
|
123
|
-
).toEqual([]);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('the rule binds the cohort that proposed it', () => {
|
|
127
|
-
// RFC 0149 is itself in the governed range. A gate whose author exempted
|
|
128
|
-
// their own RFC would be the shape RFC 0147 §A.10 forbids — citing a
|
|
129
|
-
// program's status as evidence its gaps are closed.
|
|
130
|
-
const self = readFileSync(join(dir, '0149-machine-contract-and-version-reconciliation.md'), 'utf8');
|
|
131
|
-
expect(statusOf(self), 'RFC 0149 is `Accepted`').toBe('Accepted');
|
|
132
|
-
expect(
|
|
133
|
-
149 >= LIFECYCLE_RULE_RFC,
|
|
134
|
-
'RFC 0149 MUST fall inside the range its own §D governs',
|
|
135
|
-
).toBe(true);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Every non-terminal RFC either carries the **Parked** annotation with a named
|
|
141
|
-
* tripwire, or is genuinely in flight.
|
|
142
|
-
*
|
|
143
|
-
* `RFCS/README.md` §"Parked RFCs" extends the annotation from `Draft` to
|
|
144
|
-
* `Active`, and the reason is the confusion this corpus has now made in both
|
|
145
|
-
* directions: **an RFC blocked on the world and an RFC blocked on somebody's
|
|
146
|
-
* unfinished work look identical from an index.** RFC 0038 was parked on an
|
|
147
|
-
* external tripwire while three of its acceptance criteria were plain
|
|
148
|
-
* repository work nobody had done; RFC 0121 sits on an unresolved legal
|
|
149
|
-
* question and kept re-reading as an open action item.
|
|
150
|
-
*
|
|
151
|
-
* What this leg defends is the honesty of the annotation, not its presence. A
|
|
152
|
-
* Parked RFC MUST name a **falsifiable** tripwire — a condition someone could
|
|
153
|
-
* check and declare met. "Needs more adoption" is not one. Without this leg the
|
|
154
|
-
* annotation would be a label anybody could apply to make an index look
|
|
155
|
-
* finished, which is precisely the move RFC 0147 §A exists to stop: **Parked is
|
|
156
|
-
* a reason an RFC has not graduated, never a substitute for having graduated.**
|
|
157
|
-
*/
|
|
158
|
-
describe.skipIf(RFCS_DIR === null || !existsSync(RFCS_DIR))('RFCS/README §Parked — a park names its tripwire', () => {
|
|
159
|
-
const dir = RFCS_DIR as string;
|
|
160
|
-
|
|
161
|
-
function nonTerminal(): { file: string; doc: string; status: string }[] {
|
|
162
|
-
const out: { file: string; doc: string; status: string }[] = [];
|
|
163
|
-
for (const name of readdirSync(dir).filter((f) => /^\d{4}-.*\.md$/.test(f)).sort()) {
|
|
164
|
-
if (name.startsWith('0000')) continue; // the template is not an RFC
|
|
165
|
-
const doc = readFileSync(join(dir, name), 'utf8');
|
|
166
|
-
const st = statusOf(doc);
|
|
167
|
-
if (st === 'Draft' || st === 'Active') out.push({ file: name, doc, status: st });
|
|
168
|
-
}
|
|
169
|
-
return out;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
it('the scan finds the non-terminal RFCs it is meant to govern', () => {
|
|
173
|
-
// Guard: if every RFC were `Accepted` this leg would pass over an empty set,
|
|
174
|
-
// which is the vacuity RFC 0148 §C found in the floor verifier. If the corpus
|
|
175
|
-
// ever genuinely reaches zero, delete this leg deliberately rather than
|
|
176
|
-
// letting it go quiet.
|
|
177
|
-
expect(
|
|
178
|
-
nonTerminal().length,
|
|
179
|
-
'RFCS/README §"Status index" records non-terminal RFCs; a zero here means the scan broke, ' +
|
|
180
|
-
'not that the corpus finished',
|
|
181
|
-
).toBeGreaterThan(0);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('every Parked RFC names a falsifiable tripwire', () => {
|
|
185
|
-
const offenders: string[] = [];
|
|
186
|
-
for (const { file, doc } of nonTerminal()) {
|
|
187
|
-
const statusLine = /^\|\s*\*\*Status\*\*\s*\|[^\n]*$/m.exec(doc)?.[0] ?? '';
|
|
188
|
-
if (!/\(\*\*Parked\*\*\)/.test(statusLine)) continue;
|
|
189
|
-
const updated = /^\|\s*\*\*Updated\*\*\s*\|([^\n]*)$/m.exec(doc)?.[1] ?? '';
|
|
190
|
-
if (!/tripwire/i.test(updated) || updated.length < 80) {
|
|
191
|
-
offenders.push(`${file}: Parked without a named tripwire in \`Updated\``);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
expect(
|
|
195
|
-
offenders,
|
|
196
|
-
'RFCS/README.md §"Parked RFCs" rule 1: a Parked RFC MUST name the condition that un-parks ' +
|
|
197
|
-
'it, in `Updated`. An unfalsifiable park is a label that makes an index look finished ' +
|
|
198
|
-
'while nothing was decided — Parked records why an RFC has NOT graduated, and is never a ' +
|
|
199
|
-
'substitute for having graduated.\n ' + offenders.join('\n '),
|
|
200
|
-
).toEqual([]);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('no Parked RFC claims to be Accepted', () => {
|
|
204
|
-
// Rule 2. The annotation confers nothing: an `Active` (**Parked**) RFC is
|
|
205
|
-
// exactly as unimplemented as an `Active` one, and licenses no advertisement.
|
|
206
|
-
for (const { file, doc, status } of nonTerminal()) {
|
|
207
|
-
const statusLine = /^\|\s*\*\*Status\*\*\s*\|[^\n]*$/m.exec(doc)?.[0] ?? '';
|
|
208
|
-
if (!/\(\*\*Parked\*\*\)/.test(statusLine)) continue;
|
|
209
|
-
expect(
|
|
210
|
-
status,
|
|
211
|
-
`${file}: Parked applies to \`Draft\` and \`Active\` only — it never stands in for \`Accepted\``,
|
|
212
|
-
).not.toBe('Accepted');
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RFC 0150 §C — the semantic request digest, and three defects in `replay.md`.
|
|
3
|
-
*
|
|
4
|
-
* **1. The exclusion list forbids exactly what §C requires.** `replay.md` §A
|
|
5
|
-
* says "Fields NOT in this set MUST NOT influence the cache key — including but
|
|
6
|
-
* not limited to: `max_tokens`, `stop`, … `seed`". §C says the digest "MUST
|
|
7
|
-
* cover the complete semantic provider request … maximum output bound, stop
|
|
8
|
-
* conditions, seed". Those are direct opposites, and §A's side is wrong: a
|
|
9
|
-
* request with `stop: ["END"]` and one without produce different completions, so
|
|
10
|
-
* a cache keyed identically for both **returns the wrong response** — not a
|
|
11
|
-
* miss, a wrong hit. Same for `seed`, whose entire purpose is to change output,
|
|
12
|
-
* and for the output bound, which decides whether a response is truncated.
|
|
13
|
-
*
|
|
14
|
-
* **2. It prescribes a normalization JCS does not perform.** Step 2 says
|
|
15
|
-
* canonicalize "via RFC 8785 JCS", then tells hosts without JCS to emit "UTF-8
|
|
16
|
-
* NFC for all strings". JCS does **not** apply NFC. So the two paths the same
|
|
17
|
-
* sentence offers produce **different bytes for the same input** whenever a
|
|
18
|
-
* string is not already NFC — which is the portability property §D claims as a
|
|
19
|
-
* normative invariant. §C: "Implementations MUST NOT add Unicode normalization
|
|
20
|
-
* outside JCS."
|
|
21
|
-
*
|
|
22
|
-
* **3. It cites a formula that no longer exists.** §C quotes the Layer-2 id as
|
|
23
|
-
* `sha256(runId ':' nodeId ':' attempt ':' providerKey)` — the composition RFC
|
|
24
|
-
* 0150 §B retired as a safety-fix, because `attempt` in the preimage guaranteed
|
|
25
|
-
* a duplicate effect on every retry. `idempotency.md` is v1.4; `replay.md` was
|
|
26
|
-
* left quoting v1.1. This one is a cross-document staleness the §B change itself
|
|
27
|
-
* introduced and did not catch.
|
|
28
|
-
*
|
|
29
|
-
* Server-free; reads the corpus. `spec/v1/` is repository-only, so it self-skips
|
|
30
|
-
* under the published tarball layout.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import { describe, it, expect } from 'vitest';
|
|
34
|
-
import { readFileSync } from 'node:fs';
|
|
35
|
-
import { join } from 'node:path';
|
|
36
|
-
import { V1_DIR } from '../lib/paths.js';
|
|
37
|
-
|
|
38
|
-
/** Fields whose presence changes the model's output, so they change its identity. */
|
|
39
|
-
const OUTCOME_AFFECTING = ['seed', 'stop', 'maxOutputTokens'];
|
|
40
|
-
|
|
41
|
-
describe.skipIf(V1_DIR === null)('RFC 0150 §C — semantic request digest v2', () => {
|
|
42
|
-
const doc = V1_DIR === null ? '' : readFileSync(join(V1_DIR as string, 'replay.md'), 'utf8');
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Normative text only. Blockquote lines are excluded because this document
|
|
46
|
-
* deliberately QUOTES the rules it retired, so a reader can see what changed
|
|
47
|
-
* and why — and a naive substring search cannot tell a live rule from a
|
|
48
|
-
* quoted-and-retired one. Same carve-out shape as RFC 0149 §B's lint, which
|
|
49
|
-
* exempts pre-0073 RFCs as the dated record of what was proposed.
|
|
50
|
-
*
|
|
51
|
-
* The exemption is narrow on purpose: it covers `>` blocks, not the body, so
|
|
52
|
-
* a retired rule cannot be resurrected into normative voice unnoticed.
|
|
53
|
-
*/
|
|
54
|
-
const normative = doc
|
|
55
|
-
.split('\n')
|
|
56
|
-
.filter((l) => !/^\s*>/.test(l))
|
|
57
|
-
.join('\n');
|
|
58
|
-
const plain = normative.replace(/[`*_]/g, '').replace(/\s+/g, ' ');
|
|
59
|
-
/** Full text including quotes — used only where a quote is the thing under test. */
|
|
60
|
-
const everything = doc.replace(/[`*_]/g, '').replace(/\s+/g, ' ');
|
|
61
|
-
|
|
62
|
-
it('the digest recipe section is found at all', () => {
|
|
63
|
-
// Guard: an empty read makes every leg below vacuously true.
|
|
64
|
-
expect(doc.length, 'replay.md MUST be readable').toBeGreaterThan(1000);
|
|
65
|
-
expect(plain, 'the cache-key recipe MUST exist').toMatch(/cache key/i);
|
|
66
|
-
// The blockquote filter must not swallow the document.
|
|
67
|
-
expect(
|
|
68
|
-
plain.length / everything.length,
|
|
69
|
-
'the normative-text filter MUST retain most of the document, or every leg below is vacuous',
|
|
70
|
-
).toBeGreaterThan(0.7);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('outcome-affecting fields are covered, not excluded', () => {
|
|
74
|
-
for (const field of OUTCOME_AFFECTING) {
|
|
75
|
-
expect(
|
|
76
|
-
plain.includes(field),
|
|
77
|
-
`RFC 0150 §C: the digest MUST cover \`${field}\`. Excluding it means two requests that ` +
|
|
78
|
-
'produce different completions share a cache key — a wrong hit, not a miss.',
|
|
79
|
-
).toBe(true);
|
|
80
|
-
}
|
|
81
|
-
// The v1 exclusion sentence named them as MUST NOT influence. It must be gone.
|
|
82
|
-
expect(
|
|
83
|
-
/Fields NOT in this set MUST NOT influence the cache key/.test(plain),
|
|
84
|
-
'RFC 0150 §C: the v1 exclusion list forbade the very fields §C requires. It cannot survive ' +
|
|
85
|
-
'alongside the v2 recipe — a reader following it would build the colliding digest.',
|
|
86
|
-
).toBe(false);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('no Unicode normalization is prescribed outside JCS', () => {
|
|
90
|
-
expect(
|
|
91
|
-
/UTF-8 NFC for all strings/.test(plain),
|
|
92
|
-
'RFC 0150 §C: "Implementations MUST NOT add Unicode normalization outside JCS." JCS does ' +
|
|
93
|
-
'not apply NFC, so offering NFC as the no-JCS fallback makes the two paths produce ' +
|
|
94
|
-
'different bytes for the same input — defeating the portability §D asserts.',
|
|
95
|
-
).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('the recipe carries a version stamp so v1 and v2 digests cannot collide', () => {
|
|
99
|
-
expect(
|
|
100
|
-
plain,
|
|
101
|
-
'RFC 0150 §C: the canonical object carries `recipe: "openwop-semantic-request-v2"`, so a ' +
|
|
102
|
-
'digest computed under the old rules is distinguishable rather than silently comparable.',
|
|
103
|
-
).toContain('openwop-semantic-request-v2');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('unknown provider options are carried, not dropped', () => {
|
|
107
|
-
expect(
|
|
108
|
-
/providerOptions/.test(plain),
|
|
109
|
-
'RFC 0150 §C: unknown provider options MUST go in a closed, namespaced `providerOptions` ' +
|
|
110
|
-
'object before hashing. "Silently dropping them is nonconformant" — a dropped option that ' +
|
|
111
|
-
'changes output is the collision this section exists to prevent.',
|
|
112
|
-
).toBe(true);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('the Layer-2 cross-reference matches the composition that actually exists', () => {
|
|
116
|
-
// Cross-document staleness introduced by RFC 0150 §B and not caught by it.
|
|
117
|
-
expect(
|
|
118
|
-
/attempt \|\| ':' \|\| providerKey|nodeId ':' attempt/.test(plain),
|
|
119
|
-
'RFC 0150 §B retired the `attempt`-bearing Layer-2 composition as a safety-fix. `replay.md` ' +
|
|
120
|
-
'MUST NOT keep quoting it — a reader following this section would rebuild the exact ' +
|
|
121
|
-
'defect §B removed, and would do so believing they were following the spec.',
|
|
122
|
-
).toBe(false);
|
|
123
|
-
expect(
|
|
124
|
-
plain,
|
|
125
|
-
'replay.md MUST cite the current Layer-2 identity',
|
|
126
|
-
).toContain('logicalInvocationId');
|
|
127
|
-
});
|
|
128
|
-
});
|