@openwop/openwop-conformance 2.40.3 → 2.41.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.
@@ -23,6 +23,15 @@
23
23
  * The RFC defers reference-host implementation; the witness comes from a host
24
24
  * that runs real orchestrator turns and summarizes.
25
25
  *
26
+ * STRENGTHENED 2.41.0 (the RFC 0111 correction on record, 2026-09-26). Equal
27
+ * `summaryRef`s prove the fork cited the same artifact; they do not prove the
28
+ * MODEL saw the same summary. When the transcript-window seam serves
29
+ * `entries[]` for both runs, the summary texts the host fed on the fork MUST be
30
+ * the source's, byte for byte — the replay MUST NOT re-summarize, on the text.
31
+ * Both majors: major 2 reads `summarization` by presence (the v2 schema has no
32
+ * `supported` seat) and records `inapplicable`, never a strict-mode failure,
33
+ * when it is absent. Gated on the live fixture `conformance-context-budget-live`.
34
+ *
26
35
  * @see RFCS/0111-context-economy.md
27
36
  * @see spec/v1/multi-agent-execution.md §"Context economy (RFC 0111)"
28
37
  */
@@ -36,122 +45,113 @@ import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
36
45
  import { queryTestEvents, type TestEvent } from '../lib/event-log-query.js';
37
46
  import { req } from '../lib/requirement-ids.js';
38
47
  import { softSkip } from '../lib/soft-skip.js';
48
+ import { seamsProfileAdvertised, targetMajor } from '../lib/seams.js';
49
+ import { familyAdvertised, v2Discovery } from '../lib/v2.js';
50
+ import { runsPath } from '../lib/memoryAttribution.js';
51
+ import { parseTranscriptWindow, summarizationDeclared, summaryTexts, type LogEvent } from '../lib/context-budget.js';
39
52
 
40
- const FIXTURE = 'conformance-context-budget-multiturn';
53
+ const FIXTURE = 'conformance-context-budget-live';
41
54
  const PROFILE = 'openwop-context-summarization';
55
+ const ID = 'openwop.it.context-summarization-replay.replay-reuses-the-recorded-context-summarized-summaryref-never-re-summarizes';
56
+ const MAX_ITERATIONS_PROBED = 16;
42
57
 
43
- interface SummarizationCap {
44
- readonly supported?: boolean;
45
- }
46
- interface ContextBudgetCap {
47
- readonly summarization?: SummarizationCap;
48
- }
49
- interface ExecutionModelCap {
50
- readonly contextBudget?: ContextBudgetCap;
51
- }
52
- interface MultiAgentCap {
53
- readonly executionModel?: ExecutionModelCap;
54
- }
55
-
56
- // ── cast-free typed accessors (no `as`) ──────────────────────────────────
57
58
  function isRecord(v: unknown): v is Record<string, unknown> {
58
59
  return typeof v === 'object' && v !== null && !Array.isArray(v);
59
60
  }
60
- function isString(v: unknown): v is string {
61
- return typeof v === 'string';
62
- }
63
- function stringOf(v: unknown): string | undefined {
64
- return isString(v) ? v : undefined;
61
+ function recordAt(v: unknown, ...keys: string[]): Record<string, unknown> | undefined {
62
+ let cur: unknown = v;
63
+ for (const k of keys) cur = isRecord(cur) ? cur[k] : undefined;
64
+ return isRecord(cur) ? cur : undefined;
65
65
  }
66
66
  function stringArrayOf(v: unknown): string[] | undefined {
67
- return Array.isArray(v) && v.every(isString) ? v : undefined;
67
+ return Array.isArray(v) && v.every((x) => typeof x === 'string') ? v : undefined;
68
68
  }
69
69
  function runIdOf(v: unknown): string | undefined {
70
- return isRecord(v) ? stringOf(v['runId']) : undefined;
70
+ const r = isRecord(v) ? v['runId'] : undefined;
71
+ return typeof r === 'string' ? r : undefined;
71
72
  }
72
73
  function replayModesOf(v: unknown): string[] {
73
- if (!isRecord(v)) return [];
74
- const replay = v['replay'];
75
- if (!isRecord(replay)) return [];
76
- return stringArrayOf(replay['modes']) ?? [];
74
+ return stringArrayOf(recordAt(v, 'replay')?.['modes']) ?? [];
77
75
  }
78
-
79
76
  /** A summary fingerprint: summaryRef plus the (ordered) replaced-turn ids. */
80
- function summaryFingerprint(e: TestEvent): string | undefined {
81
- const ref = stringOf(e.payload['summaryRef']);
82
- const replaced = stringArrayOf(e.payload['replacedTurns']);
83
- if (ref === undefined || replaced === undefined) return undefined;
84
- return `${ref}::${replaced.join(',')}`;
85
- }
86
-
87
77
  function summaryFingerprints(events: readonly TestEvent[]): string[] {
88
78
  const out: string[] = [];
89
79
  for (const e of events) {
90
80
  if (e.type !== 'context.summarized') continue;
91
- const fp = summaryFingerprint(e);
92
- expect(fp, 'a context.summarized event MUST carry summaryRef + replacedTurns').toBeDefined();
93
- if (fp !== undefined) out.push(fp);
81
+ const ref = e.payload['summaryRef'];
82
+ const replaced = stringArrayOf(e.payload['replacedTurns']);
83
+ expect(typeof ref === 'string' && replaced !== undefined, req(ID, 'RFC 0111', 'a context.summarized event MUST carry summaryRef + replacedTurns')).toBe(true);
84
+ if (typeof ref === 'string' && replaced !== undefined) out.push(`${ref}::${replaced.join(',')}`);
94
85
  }
95
86
  return out.sort();
96
87
  }
88
+ const toLog = (events: readonly TestEvent[]): LogEvent[] => events.map((e) => ({ eventId: e.eventId, type: e.type, sequence: e.sequence, payload: e.payload }));
89
+
90
+ /** The summary texts the host fed across a run's iterations, via the seam; null when the seam or entries[] are not served. */
91
+ async function fedSummaryTexts(runId: string, log: readonly LogEvent[]): Promise<string[] | null> {
92
+ const out: string[] = [];
93
+ let served = false;
94
+ for (let iteration = 1; iteration <= MAX_ITERATIONS_PROBED; iteration += 1) {
95
+ const res = await driver.get(`/v1/host/sample/agent/transcript-window?runId=${encodeURIComponent(runId)}&iteration=${iteration}`);
96
+ if (res.status !== 200) break;
97
+ const w = parseTranscriptWindow(res.json);
98
+ if (w?.entries === undefined) return null;
99
+ served = true;
100
+ out.push(...summaryTexts(w, log));
101
+ }
102
+ return served ? out : null;
103
+ }
97
104
 
98
105
  describe('context-summarization-replay (RFC 0111 §"Replay determinism")', () => {
99
106
  it('replay reuses the recorded context.summarized summaryRef — never re-summarizes', async () => {
100
- const ma = await readCapabilityFamily<MultiAgentCap>('multiAgent');
101
- const summarizationSupported = ma?.executionModel?.contextBudget?.summarization?.supported === true;
102
- if (!behaviorGate(PROFILE, summarizationSupported)) return;
103
- if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!isFixtureAdvertised(FIXTURE)` returned early (fixture-gated soft-skip)'); // fixture-gated soft-skip
107
+ const major = targetMajor();
108
+ let wellKnown: unknown;
109
+ if (major === 2) {
110
+ const doc = await v2Discovery();
111
+ if (!doc) return softSkip('blocked', 'v2 discovery unreachable');
112
+ const cb = recordAt(await familyAdvertised('multiAgent'), 'executionModel', 'contextBudget');
113
+ if (!summarizationDeclared(cb, 2)) return softSkip('inapplicable', 'multiAgent.executionModel.contextBudget.summarization is not advertised at major 2');
114
+ if (!seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'the run event-log is read through the seams profile — conformance.seamsProfile is not openwop-conformance-seams-v2');
115
+ wellKnown = doc;
116
+ } else {
117
+ const cb = recordAt(await readCapabilityFamily<Record<string, unknown>>('multiAgent'), 'executionModel', 'contextBudget');
118
+ if (!behaviorGate(PROFILE, summarizationDeclared(cb, 1))) return;
119
+ wellKnown = (await driver.get('/.well-known/openwop')).json;
120
+ }
121
+ if (!isFixtureAdvertised(FIXTURE)) return softSkip('inapplicable', `the live fixture ${FIXTURE} is not advertised — RFC 0111 §Scope forbids a mock supervisor from advertising contextBudget`);
104
122
 
105
- // Drive the multi-turn orchestrator run.
106
- const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
107
- expect(create.status).toBe(201);
123
+ const create = await driver.post(runsPath(), { workflowId: FIXTURE });
124
+ expect(create.status, req(ID, 'RFC 0111', `POST ${runsPath()} MUST create the live-fixture run`)).toBe(201);
108
125
  const sourceRunId = runIdOf(create.json);
109
- expect(sourceRunId, req('openwop.it.context-summarization-replay.replay-reuses-the-recorded-context-summarized-summaryref-never-re-summarizes', 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'POST /v1/runs MUST return a runId')).toBeDefined();
110
- if (sourceRunId === undefined) return softSkip('blocked', 'precondition not met — `sourceRunId === undefined` returned early (seam, prior step, or fixture unavailable)');
126
+ expect(sourceRunId, req(ID, 'rest-endpoints.md POST /v1/runs', 'the create response MUST carry a runId')).toBeDefined();
127
+ if (sourceRunId === undefined) return softSkip('blocked', 'no runId');
111
128
  await pollUntilTerminal(sourceRunId);
112
129
 
113
- // Read the recorded summarization records (OPTIONAL event-log seam).
114
- const sourceQ = await queryTestEvents(sourceRunId, { type: 'context.summarized' });
115
- if (!sourceQ.ok) return softSkip('blocked', 'precondition not met — `!sourceQ.ok` returned early (event-log seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // event-log seam unwired — soft-skip
130
+ const sourceQ = await queryTestEvents(sourceRunId);
131
+ if (!sourceQ.ok) return softSkip('blocked', 'the run event-log seam is unavailable');
116
132
  const sourceFingerprints = summaryFingerprints(sourceQ.events);
117
133
  if (sourceFingerprints.length === 0) {
118
- // The run did not summarize (budget not exceeded on this host) — nothing
119
- // to prove about reuse. Honest soft-skip; not a vacuous pass of the MUST.
120
- // eslint-disable-next-line no-console
121
- console.warn(`[${PROFILE}] run produced no context.summarized events; replay-reuse leg soft-skipped`);
122
- return softSkip('blocked', 'precondition not met — `sourceFingerprints.length === 0` returned early ([…] run produced no context.summarized events; replay-reuse leg soft-skipped) (seam, prior step, or fixture unavailable)');
134
+ return softSkip('blocked', 'the live run produced no context.summarized event — a host advertising summarization must summarize this fixture for reuse to be observable');
123
135
  }
136
+ if (!replayModesOf(wellKnown).includes('replay')) return softSkip('inapplicable', 'the host advertises no replay fork mode');
124
137
 
125
- // Only attempt replay when the host advertises the replay fork mode.
126
- const wellKnown = await driver.get('/.well-known/openwop');
127
- if (!replayModesOf(wellKnown.json).includes('replay')) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `!replayModesOf(wellKnown.json).includes(\'replay\')` returned early');
128
-
129
- const fork = await driver.post(
130
- `/v1/runs/${encodeURIComponent(sourceRunId)}:fork`,
131
- { fromSeq: 0, mode: 'replay' },
132
- );
133
- if (fork.status === 501 || fork.status === 404) return softSkip('blocked', 'precondition not met — `fork.status === 501 || fork.status === 404` returned early (replay not implemented for this run — soft-skip) (seam, prior step, or fixture unavailable)'); // replay not implemented for this run — soft-skip
134
- expect(
135
- fork.status,
136
- req('openwop.it.context-summarization-replay.replay-reuses-the-recorded-context-summarized-summaryref-never-re-summarizes', 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'replay fork MUST return 201'),
137
- ).toBe(201);
138
+ const fork = await driver.post(`${runsPath()}/${encodeURIComponent(sourceRunId)}:fork`, { fromSeq: 0, mode: 'replay' });
139
+ if (fork.status === 501 || fork.status === 404) return softSkip('blocked', `replay fork answered ${fork.status}`);
140
+ expect(fork.status, req(ID, 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'replay fork MUST return 201')).toBe(201);
138
141
  const forkRunId = runIdOf(fork.json);
139
- expect(forkRunId, req('openwop.it.context-summarization-replay.replay-reuses-the-recorded-context-summarized-summaryref-never-re-summarizes', 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'replay fork MUST return a runId')).toBeDefined();
140
- if (forkRunId === undefined) return softSkip('blocked', 'precondition not met — `forkRunId === undefined` returned early (seam, prior step, or fixture unavailable)');
142
+ expect(forkRunId, req(ID, 'rest-endpoints.md POST /v1/runs/{runId}:fork', 'replay fork MUST return a runId')).toBeDefined();
143
+ if (forkRunId === undefined) return softSkip('blocked', 'no fork runId');
141
144
  await pollUntilTerminal(forkRunId);
142
145
 
143
- const forkQ = await queryTestEvents(forkRunId, { type: 'context.summarized' });
144
- if (!forkQ.ok) return softSkip('blocked', 'precondition not met — `!forkQ.ok` returned early (event-log seam unwired for the fork — soft-skip) (seam, prior step, or fixture unavailable)'); // event-log seam unwired for the fork — soft-skip
145
- const forkFingerprints = summaryFingerprints(forkQ.events);
146
+ const forkQ = await queryTestEvents(forkRunId);
147
+ if (!forkQ.ok) return softSkip('blocked', 'the event-log seam is unavailable for the fork');
148
+ expect(summaryFingerprints(forkQ.events), req(ID, 'RFC 0111 §"Replay determinism"', 'a replay fork MUST reuse the recorded context.summarized summaryRef (never re-summarize to a different transcript)')).toEqual(sourceFingerprints);
146
149
 
147
- // The replay MUST reuse the recorded summaries (same summaryRef + replacedTurns),
148
- // NOT regenerate them — the direct analogue of RFC 0041 envelope-refusal recovery.
149
- expect(
150
- forkFingerprints,
151
- req('openwop.it.context-summarization-replay.replay-reuses-the-recorded-context-summarized-summaryref-never-re-summarizes',
152
- 'RFC 0111 §"Replay determinism"',
153
- 'a replay fork MUST reuse the recorded context.summarized summaryRef (never re-summarize to a different transcript)',
154
- ),
155
- ).toEqual(sourceFingerprints);
150
+ // The model-facing half: the summary TEXT the host fed on the fork.
151
+ const sourceTexts = await fedSummaryTexts(sourceRunId, toLog(sourceQ.events));
152
+ const forkTexts = await fedSummaryTexts(forkRunId, toLog(forkQ.events));
153
+ if (sourceTexts === null || forkTexts === null) return softSkip('inapplicable', 'the transcript-window seam serves no entries[] for these runs, so the model-facing summary text was not compared (summaryRef reuse was)');
154
+ expect(sourceTexts.length, req(ID, 'RFC 0111 §"Replay determinism"', 'the source run summarized, so its transcript windows MUST carry the summary text it fed')).toBeGreaterThan(0);
155
+ expect(forkTexts, req(ID, 'RFC 0111 §"Replay determinism"', 'the replay MUST feed the model the recorded summary text, byte for byte — never a re-summarization')).toEqual(sourceTexts);
156
156
  });
157
157
  });
@@ -36,6 +36,8 @@ import { scaledTimeoutMs } from '../lib/polling.js';
36
36
  import { req } from '../lib/requirement-ids.js';
37
37
  import { projectBoundId, BOUND_ID } from '../lib/bound-id.js';
38
38
 
39
+ export const REQUIRES_HOST_CALLBACK = 'the host POSTs a webhook delivery to the suite-owned scoped receiver behind OPENWOP_WEBHOOK_RECEIVER_URL';
40
+
39
41
  const DOC = 'spec/v2/core/identity.md §5 (RFC 0187 §A)';
40
42
  const WEBHOOK_ID = 'openwop.requirement.0187.bound-id-kinds.webhook';
41
43
  const PER_KIND = 'openwop.requirement.0187.bound-id-kinds.per-kind';
@@ -75,6 +75,8 @@ import { startEffectReceiver, waitForFirstArrival } from '../lib/effect-receiver
75
75
  import { watchForResumption, type Observation, type Watch } from '../lib/durability-watch.js';
76
76
  import { noteEvidence, parseRecoveryBounds, EVIDENCE_NAME_PATTERN } from '../lib/durability-evidence.js';
77
77
 
78
+ export const REQUIRES_HOST_CALLBACK = 'the host makes an outbound effect call to the suite-owned effect receiver (OPENWOP_WEBHOOK_RECEIVER_PORT)';
79
+
78
80
  const FIXTURE = 'conformance-noop';
79
81
  const FAILURE_FIXTURE = 'conformance-failure';
80
82
 
@@ -39,6 +39,8 @@ import { req } from '../lib/requirement-ids.js';
39
39
  import { startEffectReceiver, waitForFirstArrival } from '../lib/effect-receiver.js';
40
40
  import { terminalShapeViolation, TERMINAL_RUN_EVENTS } from '../lib/terminal-shape.js';
41
41
 
42
+ export const REQUIRES_HOST_CALLBACK = 'the host makes an outbound effect call to the suite-owned effect receiver (OPENWOP_WEBHOOK_RECEIVER_PORT)';
43
+
42
44
  const ONCE = 'openwop.requirement.0194.terminal-once';
43
45
  const DUP = 'openwop.requirement.0194.terminal-once.duplicate-delivery';
44
46
  const DOC = 'spec/v2/core/events.md §The terminal event (RFC 0194 §A)';
@@ -55,6 +55,8 @@ import { blockedDespiteAssertions, softSkip } from '../lib/soft-skip.js';
55
55
  import { req } from '../lib/requirement-ids.js';
56
56
  import { RETRY_WAIT_FLOOR_MS } from '../lib/webhook-retry-window.js';
57
57
 
58
+ export const REQUIRES_HOST_CALLBACK = 'the host POSTs webhook deliveries to nine suite-owned subscriptions on the scoped receiver behind OPENWOP_WEBHOOK_RECEIVER_URL, eight of which the suite holds open';
59
+
58
60
  const ID = 'openwop.requirement.0215.no-head-of-line';
59
61
  const DOC = 'webhooks.md §Durability (RFC 0215 §A.1, §A.2)';
60
62
  const FIXTURE = 'conformance-delay';
@@ -57,6 +57,8 @@ import { req } from '../lib/requirement-ids.js';
57
57
  import { era2Gate, seedEra2Log, v1FixtureLog } from '../lib/era2-seed.js';
58
58
  import { SCHEMAS_DIR } from '../lib/paths.js';
59
59
 
60
+ export const REQUIRES_HOST_CALLBACK = 'the host POSTs a webhook delivery to the suite-owned scoped receiver behind OPENWOP_WEBHOOK_RECEIVER_URL';
61
+
60
62
  const ID = 'openwop.requirement.0171.webhook-delivery-shape';
61
63
  const DOC = 'spec/v2/core/webhooks.md §Delivery';
62
64
  const FIXTURE = 'conformance-noop';
@@ -37,6 +37,8 @@ import { blockedDespiteAssertions, softSkip } from '../lib/soft-skip.js';
37
37
  import { req } from '../lib/requirement-ids.js';
38
38
  import { retryWaitCapMs, retryWaitFor, windowClosedNote } from '../lib/webhook-retry-window.js';
39
39
 
40
+ export const REQUIRES_HOST_CALLBACK = 'the host POSTs and retries webhook deliveries to the suite-owned scoped receiver behind OPENWOP_WEBHOOK_RECEIVER_URL';
41
+
40
42
  const FIXTURE = 'conformance-noop';
41
43
  const TERMINAL = new Set(['completed', 'failed', 'cancelled']);
42
44
 
@@ -46,6 +46,8 @@ import { blockedDespiteAssertions, softSkip } from '../lib/soft-skip.js';
46
46
  import { req } from '../lib/requirement-ids.js';
47
47
  import { retryWaitCapMs, retryWaitFor } from '../lib/webhook-retry-window.js';
48
48
 
49
+ export const REQUIRES_HOST_CALLBACK = 'the host POSTs and retries webhook deliveries to the suite-owned scoped receiver behind OPENWOP_WEBHOOK_RECEIVER_URL';
50
+
49
51
  const ID = 'openwop.requirement.0215.unregister-stops-delivery';
50
52
  const DOC = 'webhooks.md §Durability (RFC 0215 §B)';
51
53
  const FIXTURE = 'conformance-noop';
@@ -113,6 +113,8 @@ import { discoverOwnedTenant } from '../lib/webhook-receiver.js';
113
113
  import { absenceIsUnmeasured, noDeliveryCause, startScopedReceiver, type ScopedReceiver } from '../lib/scoped-receiver.js';
114
114
  import { req } from '../lib/requirement-ids.js';
115
115
 
116
+ export const REQUIRES_HOST_CALLBACK = 'the host POSTs a signed webhook delivery to the suite-owned scoped receiver behind OPENWOP_WEBHOOK_RECEIVER_URL';
117
+
116
118
  interface DeliveredRequest {
117
119
  readonly headers: Record<string, string>;
118
120
  readonly body: string;