@openwop/openwop-conformance 2.45.3 → 2.45.5

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.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * v2 — a run budget is reserved, crossed, exhausted and enforced
3
+ * (`spec/v2/core/runs.md` §`budget` section). The v1 twin is
4
+ * `budget-enforcement`, which drives a host seam; at major 2 the budget rides
5
+ * on `createRun` (`configurable.budget`), so this witness is unaided. The
6
+ * logic is `lib/budget-witness.ts`; what differs between majors is the profile
7
+ * row in `lib/major-profile.ts`.
8
+ *
9
+ * The suite creates a run of `conformance-budget-tool-calls` (three scripted
10
+ * tool calls) with `{ maxToolCalls: 2, thresholdPercent: 50, onExhaustion:
11
+ * "fail" }` and reads its log through the poll.
12
+ *
13
+ * lifecycle `budget.reserved`, `budget.threshold-crossed` (numeric
14
+ * `percent`) and `budget.exhausted`, in that order. Both
15
+ * enforce modes owe these.
16
+ * enforcement `enforce: hard`: `cap.breached` with kind
17
+ * `budget-tool-calls`, not before `budget.exhausted`, and the
18
+ * run fails `budget_exhausted`. `enforce: advisory`: the run is
19
+ * not stopped.
20
+ * content-free no `budget.*` or `cap.breached` payload carries a rate card,
21
+ * a unit price or a credential.
22
+ *
23
+ * Dispositions: no `budget`, `toolCalls` not in `budget.dimensions`, or the
24
+ * fixture unadvertised ⇒ `inapplicable`. The fixture is the opt-in: a host
25
+ * that advertises `budget` and has not seeded it is unwitnessed here, not
26
+ * blocked. A valid budgeted create that is refused ⇒ `executed-fail`.
27
+ *
28
+ * Not ported: v1's model-denied leg (`budget_model_denied`). It needs a
29
+ * fixture that resolves a model, and none does so without a seam.
30
+ *
31
+ * Proven both ways against the scratch host in `lib/budget-witness.test.ts`.
32
+ *
33
+ * @see spec/v2/core/runs.md §`budget` section
34
+ * @see conformance/fixtures.md §conformance-budget-tool-calls
35
+ */
36
+
37
+ import { describe, it, expect } from 'vitest';
38
+ import { v2Discovery } from '../lib/v2.js';
39
+ import { softSkip } from '../lib/soft-skip.js';
40
+ import { req } from '../lib/requirement-ids.js';
41
+ import { majorProfile } from '../lib/major-profile.js';
42
+ import { drive, judge, type BudgetRun, type Finding } from '../lib/budget-witness.js';
43
+
44
+ const PROFILE = majorProfile(2);
45
+ const DOC = 'spec/v2/core/runs.md §budget section';
46
+ const ID_LIFECYCLE = 'openwop.requirement.runs.budget-lifecycle';
47
+ const ID_ENFORCEMENT = 'openwop.requirement.runs.budget-enforcement';
48
+ const ID_CONTENT_FREE = 'openwop.requirement.runs.budget-content-free';
49
+
50
+ /** One budgeted run per file: three legs read the same log. */
51
+ let once: Promise<BudgetRun> | undefined;
52
+ function budgetRun(): Promise<BudgetRun> {
53
+ once ??= (async (): Promise<BudgetRun> => {
54
+ let doc: Record<string, unknown> | null;
55
+ try { doc = await v2Discovery(); } catch { doc = null; }
56
+ if (!doc) return { kind: 'skip', disposition: 'blocked', reason: 'v2 discovery unreachable — /.well-known/openwop did not answer 200 with a JSON body under OpenWOP-Version: 2.0' };
57
+ return drive(PROFILE, doc);
58
+ })();
59
+ return once;
60
+ }
61
+
62
+ type Leg = { readonly skip: { readonly disposition: 'inapplicable' | 'blocked'; readonly reason: string } } | { readonly findings: Finding[] };
63
+
64
+ /** The findings for one leg, or why there are none. A refused create fails the leg here. */
65
+ async function leg(id: string, rules: ReadonlyArray<Finding['rule']>): Promise<Leg> {
66
+ const r = await budgetRun();
67
+ if (r.kind === 'skip') return { skip: { disposition: r.disposition, reason: r.reason } };
68
+ if (r.kind === 'refused') {
69
+ expect(r.status, req(id, DOC, `a host that advertises budget and the fixture MUST accept a valid configurable.budget (got ${r.status} ${r.code ?? ''})`.trim())).toBe(201);
70
+ return { findings: [] }; // unreachable: a refusal is never 201
71
+ }
72
+ return { findings: judge(PROFILE, r.observation).filter((f) => rules.includes(f.rule)) };
73
+ }
74
+
75
+ describe('v2 budget enforcement (runs.md §budget section)', () => {
76
+ it('a budgeted run emits budget.reserved, budget.threshold-crossed and budget.exhausted in order', async () => {
77
+ const l = await leg(ID_LIFECYCLE, ['lifecycle']);
78
+ if ('skip' in l) return softSkip(l.skip.disposition, l.skip.reason);
79
+ for (const f of l.findings) expect(f.ok, req(ID_LIFECYCLE, f.doc, f.message)).toBe(true);
80
+ });
81
+
82
+ it('a hard host stops the run budget_exhausted after cap.breached, and an advisory host does not stop it', async () => {
83
+ const l = await leg(ID_ENFORCEMENT, ['hard-stop', 'advisory']);
84
+ if ('skip' in l) return softSkip(l.skip.disposition, l.skip.reason);
85
+ if (l.findings.length === 0) return softSkip('inapplicable', 'budget.enforce is not advertised — neither the hard stop nor the advisory rule binds');
86
+ for (const f of l.findings) expect(f.ok, req(ID_ENFORCEMENT, f.doc, f.message)).toBe(true);
87
+ });
88
+
89
+ it('no budget.* or cap.breached payload carries pricing or a credential', async () => {
90
+ const l = await leg(ID_CONTENT_FREE, ['content-free']);
91
+ if ('skip' in l) return softSkip(l.skip.disposition, l.skip.reason);
92
+ for (const f of l.findings) expect(f.ok, req(ID_CONTENT_FREE, f.doc, f.message)).toBe(true);
93
+ });
94
+ });
@@ -0,0 +1,74 @@
1
+ /**
2
+ * v2 — a host at capacity answers `503 service_unavailable` with `Retry-After`
3
+ * (`spec/v2/core/conformance.md` §Production profile, `backpressure` facet).
4
+ * The v1 twin is `production-backpressure`; the logic both share is
5
+ * `lib/backpressure-witness.ts`, and what differs between majors is the
6
+ * profile row in `lib/major-profile.ts`.
7
+ *
8
+ * Unaided. Gated on `production.backpressure.inflightCap`, the number a host
9
+ * advertises so the suite can saturate it: `inflightCap` event streams hold
10
+ * the slots and one more request is sent.
11
+ *
12
+ * refusal the extra request answers `503`, code `service_unavailable`,
13
+ * with `Retry-After`; where `retryAfterSeconds` is advertised
14
+ * the header equals it.
15
+ * retry timing the refusal carries no `details.retryAfter*`
16
+ * (`errors.md` §Retry timing). This is where v2 differs from
17
+ * v1, which required `details.retryAfter`.
18
+ *
19
+ * Dispositions: no `production`, no `backpressure`, no `inflightCap`, an
20
+ * `inflightCap` above 64 (the most streams the suite holds open), or the hold
21
+ * or probe fixture unadvertised ⇒ `inapplicable`. A slot that cannot be
22
+ * filled, or a probe with no response ⇒ `blocked`.
23
+ *
24
+ * v1's "discovery is exempt from the cap" leg is not ported: no v2 document
25
+ * states it.
26
+ *
27
+ * Run with `--no-file-parallelism`. Proven both ways against the scratch host
28
+ * in `lib/backpressure-witness.test.ts`.
29
+ *
30
+ * @see spec/v2/core/conformance.md §Production profile
31
+ * @see spec/v2/core/errors.md §Retry timing
32
+ */
33
+
34
+ import { describe, it, expect } from 'vitest';
35
+ import { v2Discovery } from '../lib/v2.js';
36
+ import { softSkip } from '../lib/soft-skip.js';
37
+ import { req } from '../lib/requirement-ids.js';
38
+ import { majorProfile } from '../lib/major-profile.js';
39
+ import { judge, saturate, type Saturation } from '../lib/backpressure-witness.js';
40
+
41
+ const PROFILE = majorProfile(2);
42
+ const ID_REFUSAL = 'openwop.requirement.production.backpressure-refusal';
43
+ const ID_TIMING = 'openwop.requirement.0171.error-registry.no-retry-details';
44
+
45
+ /** One saturation per file: two legs read the same refusal. */
46
+ let once: Promise<Saturation> | undefined;
47
+ function saturation(): Promise<Saturation> {
48
+ once ??= (async (): Promise<Saturation> => {
49
+ let doc: Record<string, unknown> | null;
50
+ try { doc = await v2Discovery(); } catch { doc = null; }
51
+ if (!doc) return { kind: 'skip', disposition: 'blocked', reason: 'v2 discovery unreachable — /.well-known/openwop did not answer 200 with a JSON body under OpenWOP-Version: 2.0' };
52
+ return saturate(PROFILE, doc);
53
+ })();
54
+ return once;
55
+ }
56
+
57
+ describe('v2 production backpressure (conformance.md §Production profile)', () => {
58
+ it('with inflightCap slots held, the next request is refused 503 service_unavailable with Retry-After', async () => {
59
+ const s = await saturation();
60
+ if (s.kind === 'skip') return softSkip(s.disposition, s.reason);
61
+ for (const f of judge(PROFILE, s.refusal).filter((x) => x.rule !== 'retry-timing')) {
62
+ expect(f.ok, req(ID_REFUSAL, f.doc, f.message)).toBe(true);
63
+ }
64
+ });
65
+
66
+ it('the refusal carries its retry timing in the Retry-After header only', async () => {
67
+ const s = await saturation();
68
+ if (s.kind === 'skip') return softSkip(s.disposition, s.reason);
69
+ if (s.refusal.status !== 503) return softSkip('blocked', `no 503 was observed at inflightCap + 1 (got ${s.refusal.status}) — there is no refusal whose details could be read`);
70
+ for (const f of judge(PROFILE, s.refusal).filter((x) => x.rule === 'retry-timing')) {
71
+ expect(f.ok, req(ID_TIMING, f.doc, f.message)).toBe(true);
72
+ }
73
+ });
74
+ });