@gate-forge/witness 0.0.0-stage → 0.9.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 (123) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +114 -2
  3. package/dist/adapter/contract-suite.d.ts +167 -0
  4. package/dist/adapter/contract-suite.d.ts.map +1 -0
  5. package/dist/adapter/contract-suite.js +348 -0
  6. package/dist/adapter/contract-suite.js.map +1 -0
  7. package/dist/adapter/contract.d.ts +266 -0
  8. package/dist/adapter/contract.d.ts.map +1 -0
  9. package/dist/adapter/contract.js +2 -0
  10. package/dist/adapter/contract.js.map +1 -0
  11. package/dist/adapter/index.d.ts +8 -0
  12. package/dist/adapter/index.d.ts.map +1 -0
  13. package/dist/adapter/index.js +2 -0
  14. package/dist/adapter/index.js.map +1 -0
  15. package/dist/adapter-kit/config.d.ts +163 -0
  16. package/dist/adapter-kit/config.d.ts.map +1 -0
  17. package/dist/adapter-kit/config.js +21 -0
  18. package/dist/adapter-kit/config.js.map +1 -0
  19. package/dist/adapter-kit/define.d.ts +47 -0
  20. package/dist/adapter-kit/define.d.ts.map +1 -0
  21. package/dist/adapter-kit/define.js +336 -0
  22. package/dist/adapter-kit/define.js.map +1 -0
  23. package/dist/adapter-kit/index.d.ts +34 -0
  24. package/dist/adapter-kit/index.d.ts.map +1 -0
  25. package/dist/adapter-kit/index.js +32 -0
  26. package/dist/adapter-kit/index.js.map +1 -0
  27. package/dist/adapter-kit/projection.d.ts +69 -0
  28. package/dist/adapter-kit/projection.d.ts.map +1 -0
  29. package/dist/adapter-kit/projection.js +85 -0
  30. package/dist/adapter-kit/projection.js.map +1 -0
  31. package/dist/adapter-kit/session.d.ts +105 -0
  32. package/dist/adapter-kit/session.d.ts.map +1 -0
  33. package/dist/adapter-kit/session.js +230 -0
  34. package/dist/adapter-kit/session.js.map +1 -0
  35. package/dist/client/witness-client.d.ts +193 -0
  36. package/dist/client/witness-client.d.ts.map +1 -0
  37. package/dist/client/witness-client.js +321 -0
  38. package/dist/client/witness-client.js.map +1 -0
  39. package/dist/constants.d.ts +189 -0
  40. package/dist/constants.d.ts.map +1 -0
  41. package/dist/constants.js +197 -0
  42. package/dist/constants.js.map +1 -0
  43. package/dist/index.d.ts +15 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +20 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/json.d.ts +16 -0
  48. package/dist/json.d.ts.map +1 -0
  49. package/dist/json.js +21 -0
  50. package/dist/json.js.map +1 -0
  51. package/dist/queue/bullmq.d.ts +28 -0
  52. package/dist/queue/bullmq.d.ts.map +1 -0
  53. package/dist/queue/bullmq.js +210 -0
  54. package/dist/queue/bullmq.js.map +1 -0
  55. package/dist/queue/observer.d.ts +130 -0
  56. package/dist/queue/observer.d.ts.map +1 -0
  57. package/dist/queue/observer.js +179 -0
  58. package/dist/queue/observer.js.map +1 -0
  59. package/dist/surface.d.ts +211 -0
  60. package/dist/surface.d.ts.map +1 -0
  61. package/dist/surface.js +268 -0
  62. package/dist/surface.js.map +1 -0
  63. package/dist/witness/adapter-registry.d.ts +92 -0
  64. package/dist/witness/adapter-registry.d.ts.map +1 -0
  65. package/dist/witness/adapter-registry.js +370 -0
  66. package/dist/witness/adapter-registry.js.map +1 -0
  67. package/dist/witness/behavior-request.d.ts +80 -0
  68. package/dist/witness/behavior-request.d.ts.map +1 -0
  69. package/dist/witness/behavior-request.js +335 -0
  70. package/dist/witness/behavior-request.js.map +1 -0
  71. package/dist/witness/behavior.d.ts +52 -0
  72. package/dist/witness/behavior.d.ts.map +1 -0
  73. package/dist/witness/behavior.js +118 -0
  74. package/dist/witness/behavior.js.map +1 -0
  75. package/dist/witness/bin.d.ts +21 -0
  76. package/dist/witness/bin.d.ts.map +1 -0
  77. package/dist/witness/bin.js +254 -0
  78. package/dist/witness/bin.js.map +1 -0
  79. package/dist/witness/browser.d.ts +221 -0
  80. package/dist/witness/browser.d.ts.map +1 -0
  81. package/dist/witness/browser.js +644 -0
  82. package/dist/witness/browser.js.map +1 -0
  83. package/dist/witness/chaos.d.ts +179 -0
  84. package/dist/witness/chaos.d.ts.map +1 -0
  85. package/dist/witness/chaos.js +270 -0
  86. package/dist/witness/chaos.js.map +1 -0
  87. package/dist/witness/classifications.d.ts +42 -0
  88. package/dist/witness/classifications.d.ts.map +1 -0
  89. package/dist/witness/classifications.js +87 -0
  90. package/dist/witness/classifications.js.map +1 -0
  91. package/dist/witness/env-attestation.d.ts +98 -0
  92. package/dist/witness/env-attestation.d.ts.map +1 -0
  93. package/dist/witness/env-attestation.js +202 -0
  94. package/dist/witness/env-attestation.js.map +1 -0
  95. package/dist/witness/fixture-provider.d.ts +89 -0
  96. package/dist/witness/fixture-provider.d.ts.map +1 -0
  97. package/dist/witness/fixture-provider.js +116 -0
  98. package/dist/witness/fixture-provider.js.map +1 -0
  99. package/dist/witness/loopback-pins.d.ts +79 -0
  100. package/dist/witness/loopback-pins.d.ts.map +1 -0
  101. package/dist/witness/loopback-pins.js +244 -0
  102. package/dist/witness/loopback-pins.js.map +1 -0
  103. package/dist/witness/run-options.d.ts +47 -0
  104. package/dist/witness/run-options.d.ts.map +1 -0
  105. package/dist/witness/run-options.js +179 -0
  106. package/dist/witness/run-options.js.map +1 -0
  107. package/dist/witness/server.d.ts +34 -0
  108. package/dist/witness/server.d.ts.map +1 -0
  109. package/dist/witness/server.js +4963 -0
  110. package/dist/witness/server.js.map +1 -0
  111. package/dist/witness/task.d.ts +77 -0
  112. package/dist/witness/task.d.ts.map +1 -0
  113. package/dist/witness/task.js +201 -0
  114. package/dist/witness/task.js.map +1 -0
  115. package/dist/witness/twin-shapes.d.ts +51 -0
  116. package/dist/witness/twin-shapes.d.ts.map +1 -0
  117. package/dist/witness/twin-shapes.js +127 -0
  118. package/dist/witness/twin-shapes.js.map +1 -0
  119. package/dist/witness/types.d.ts +1094 -0
  120. package/dist/witness/types.d.ts.map +1 -0
  121. package/dist/witness/types.js +2 -0
  122. package/dist/witness/types.js.map +1 -0
  123. package/package.json +98 -4
@@ -0,0 +1,1094 @@
1
+ /**
2
+ * Witness-side data shapes (pin #7 wire + adapter contract, pin #8).
3
+ */
4
+ import type { Server } from 'node:http';
5
+ import type { EvidenceRecord, TracedSession, TrustTier, TwinShape } from '@gate-forge/core';
6
+ import type { ChaosOptions } from './chaos.js';
7
+ import type { TwinShapePlan } from './twin-shapes.js';
8
+ /**
9
+ * Supervisor-issued session credential (plan Phase 1, work item 2): the
10
+ * trusted supervisor (the Playwright reporter process) opens one session
11
+ * per started test and the worker-side fixture RESOLVES it by the exact
12
+ * (workerIndex, testId) pair. The session token is issued by the witness,
13
+ * never derivable by the suite: submissions without a valid OPEN session
14
+ * are rejected fail-closed, so a suite-supplied testId or annotation
15
+ * alone cannot mint records for an arbitrary session.
16
+ */
17
+ export interface SessionCredential {
18
+ /** Witness-issued session id (UUID). */
19
+ sessionId: string;
20
+ /** Per-session secret; required on every submission. */
21
+ sessionToken: string;
22
+ /** The supervisor-registered testId (records are forced onto it). */
23
+ testId: string;
24
+ /** The worker the session is bound to. */
25
+ workerIndex: number;
26
+ /**
27
+ * The session's DEDICATED observation-proxy origin (its own loopback
28
+ * port), when the run wires an observation proxy: the worker's browser
29
+ * uses this origin for the whole test, so every request on it —
30
+ * absolute paths included — is attributed to THIS session. Null when
31
+ * no observation proxy is active (nothing to attribute).
32
+ */
33
+ proxyUrl: string | null;
34
+ /**
35
+ * The supervisor-registered obligation claims for this test (Phase 4
36
+ * claim injection): the merged native-annotation + sidecar-mapping
37
+ * claims the orchestrating CLI resolved from the tracked mappings and
38
+ * the supervisor carried on the session-open path. Declarations only —
39
+ * they route evidence onto obligation identities and never satisfy
40
+ * anything by themselves; every record is still forced onto the
41
+ * supervisor-registered session/test identity and graded from
42
+ * witnessed evidence. Empty/absent for runs without mappings.
43
+ */
44
+ claims?: string[];
45
+ }
46
+ /**
47
+ * `POST /records` body (pin #7, Phase 1 extension): a test-side primitive
48
+ * submitting UI-observed evidence. `claimId` is the claimed obligation id —
49
+ * the annotation's `<resourceId>:<contract>` — and `testId` binds the record
50
+ * to the test that produced the claim. Phase 1: `sessionId` +
51
+ * `sessionToken` carry the supervisor-issued session credential; the
52
+ * witness rejects submissions without a valid OPEN session and forces the
53
+ * record's testId onto the session's supervisor-registered value.
54
+ */
55
+ export interface RecordsRequest {
56
+ claimId: string;
57
+ kind: string;
58
+ payload: unknown;
59
+ testId: string;
60
+ sessionId: string;
61
+ sessionToken: string;
62
+ }
63
+ /** `POST /records` response (pin #7, minimal wire contract). */
64
+ export interface RecordsResponse {
65
+ recordId: string;
66
+ trust: TrustTier;
67
+ runId: string;
68
+ }
69
+ /**
70
+ * `POST /witness/persistence` body (pin #7): the fixture asks the
71
+ * engine-side witness to run the resource's reviewed adapter (GET-only)
72
+ * for one entity. The issued record carries the ENGINE OBSERVATION
73
+ * (`found`, adapter-normalized `fields`, and a `before` link when a
74
+ * pre-observation was consumed) — expectations NEVER come from the
75
+ * suite (audit round 5).
76
+ *
77
+ * `preObservationId` references a witness-issued pre-observation (`POST
78
+ * /witness/pre-observation`): an id-set snapshot for create
79
+ * postconditions (`before: {entityAbsent}`), or an entity-fields
80
+ * snapshot for update postconditions (`before: {found, fields}`).
81
+ */
82
+ export interface PersistenceRequest {
83
+ resourceId: string;
84
+ entityId: unknown;
85
+ preObservationId?: string;
86
+ /** Closed UI-action interval whose post-state this persistence read binds to. */
87
+ anchorId?: string;
88
+ }
89
+ /**
90
+ * `POST /witness/pre-observation` body: an engine-side snapshot taken
91
+ * BEFORE a claimed action. With `entityId` the witness snapshots that
92
+ * entity's observed fields (update postconditions); without it, the
93
+ * resource's observed id set (create postconditions). Phase 1: requires
94
+ * the supervisor-issued session credential.
95
+ */
96
+ export interface PreObservationRequest {
97
+ resourceId: string;
98
+ testId: string;
99
+ claimId: string;
100
+ entityId?: unknown;
101
+ sessionId: string;
102
+ sessionToken: string;
103
+ }
104
+ /** `POST /witness/pre-observation` response. */
105
+ export interface PreObservationResponse {
106
+ observationId: string;
107
+ observed: number;
108
+ }
109
+ /** `POST /witness/persistence` response (pin #7). */
110
+ export interface PersistenceResponse {
111
+ recordId: string;
112
+ runId: string;
113
+ verdictRelevant: {
114
+ found: boolean;
115
+ fieldsMatch: boolean;
116
+ mismatches?: string[];
117
+ };
118
+ }
119
+ /**
120
+ * `POST /runs/server-e2e-declarations` body (SUPERVISOR ONLY): the
121
+ * obligation ids the trusted mapping layer declared kind `server-e2e`.
122
+ * Registration is a PRE-run fact (like the expected set): bound once,
123
+ * identical re-registration idempotent, any change or late registration
124
+ * refused — the witness never stamps `channel: 'server'` records for
125
+ * obligations outside this set, so the suite cannot steer an intent onto
126
+ * a browser-kind obligation and a bearer of an intent can never
127
+ * self-verify.
128
+ */
129
+ export interface ServerE2eDeclarationsRequest {
130
+ obligations: readonly string[];
131
+ }
132
+ /** `POST /runs/server-e2e-declarations` response. */
133
+ export interface ServerE2eDeclarationsResponse {
134
+ bound: true;
135
+ count: number;
136
+ obligations: string[];
137
+ }
138
+ /**
139
+ * `POST /runs/observe-declarations` body (SUPERVISOR ONLY): the
140
+ * obligation ids the trusted mapping layer declared kind `observed-e2e`
141
+ * (Observe channel, Phase 2). Registration is a PRE-run fact like the
142
+ * server-e2e set: bound once, identical re-registration idempotent, any
143
+ * change or late registration refused — the witness never stamps
144
+ * `channel: 'observe'` records for obligations outside this set, so a
145
+ * suite-driven test can never steer observe evidence onto an
146
+ * engine-kind obligation.
147
+ */
148
+ export interface ObserveDeclarationsRequest {
149
+ obligations: readonly string[];
150
+ }
151
+ /** `POST /runs/observe-declarations` response. */
152
+ export interface ObserveDeclarationsResponse {
153
+ bound: true;
154
+ count: number;
155
+ obligations: string[];
156
+ }
157
+ /**
158
+ * `POST /observe/finalize` body (SUPERVISOR ONLY — the drain calls it
159
+ * after a passed test, before sealing the session): resolve one
160
+ * session's observe-eligible claims against the session's own proxied
161
+ * traffic plus independent adapter reads, and stamp witnessed
162
+ * `persistence.observed` records for whatever resolves.
163
+ *
164
+ * The session must still be OPEN (finalize runs before seal; a sealed
165
+ * session is refused) so no record is ever injected after the test
166
+ * ended. Ambiguity, missing traffic, unparsable bodies, and adapter
167
+ * trouble resolve to typed NOTES in the response — never to
168
+ * satisfaction, never to a run failure.
169
+ */
170
+ export interface ObserveFinalizeRequest {
171
+ sessionId: string;
172
+ }
173
+ /** One obligation the finalize resolved into a witnessed record. */
174
+ export interface ObserveFinalizedObligation {
175
+ /** Obligation id the record was issued under. */
176
+ obligationId: string;
177
+ /** The issued witnessed record id. */
178
+ recordId: string;
179
+ /** The CRUD operation the binding proved. */
180
+ operation: 'create' | 'read' | 'update' | 'delete';
181
+ /** The witness-resolved entity id (scalar or column-keyed object). */
182
+ entityId: unknown;
183
+ }
184
+ /** `POST /observe/finalize` response. */
185
+ export interface ObserveFinalizeResponse {
186
+ finalized: ObserveFinalizedObligation[];
187
+ /** Typed non-satisfaction notes (missing traffic, ambiguity, …). */
188
+ notes: string[];
189
+ }
190
+ /**
191
+ * `POST /witness/server-persistence` body (SUPERVISOR ONLY — the drain
192
+ * forwards it with the verifier key; the suite can only write intent
193
+ * spool lines, never call this): one persistence claim intent, already
194
+ * drained from the runner-side intents spool.
195
+ *
196
+ * - create: `pre` (expect-absent) BEFORE the mutation stores a witness
197
+ * pre-observation; `post` (expect-present) probes, consumes it, and
198
+ * stamps `before: {entityAbsent}` into the record — the same shape the
199
+ * browser path grades.
200
+ * - update: `pre` (expect-present) snapshots the entity's fields;
201
+ * `post` consumes it into `before: {found, fields}` (the update delta
202
+ * grades against the classification's updateableFields in the engine).
203
+ * - read: `post` (expect-present) only. delete: `post` (expect-absent
204
+ * for hard; expect-present for archive states) only.
205
+ *
206
+ * `sequence` is strictly increasing per claimId; a replayed or
207
+ * out-of-order line resolves to a typed failure (fail closed), so no
208
+ * bearer of an intent can re-drive a stale observation.
209
+ */
210
+ export interface ServerPersistenceIntentRequest {
211
+ resourceId: string;
212
+ /** Obligation id `<resourceId>:persistence:<op>` the intent serves. */
213
+ claimId: string;
214
+ operation: 'create' | 'read' | 'update' | 'delete';
215
+ phase: 'pre' | 'post';
216
+ intent: 'expect-present' | 'expect-absent';
217
+ /** The entity key: scalar for single-column PKs, column-keyed object for composite. */
218
+ key: unknown;
219
+ /** Strictly increasing per claimId (replay → typed failure). */
220
+ sequence: number;
221
+ /** The claiming test's id (diagnostic attribution; the claim join key). */
222
+ testId: string;
223
+ }
224
+ /** `POST /witness/server-persistence` response for a `pre` intent. */
225
+ export interface ServerPreObservationResponse {
226
+ resolved: 'pre';
227
+ found: boolean;
228
+ }
229
+ /** `POST /witness/server-persistence` response for a `post` intent. */
230
+ export interface ServerPersistenceResponse {
231
+ recordId: string;
232
+ runId: string;
233
+ trust: TrustTier;
234
+ channel: 'server';
235
+ verdictRelevant: {
236
+ found: boolean;
237
+ };
238
+ }
239
+ /** `POST /sessions/open` body (Phase 1; Phase 4 adds claim injection). */
240
+ export interface SessionOpenRequest {
241
+ /** The test's runner-assigned id (same id the reporter writes to claims.json). */
242
+ testId: string;
243
+ /** The worker the test runs on (binds worker → session). */
244
+ workerIndex: number;
245
+ /**
246
+ * Supervisor-carried identity of the started test (enforcement-review
247
+ * fix 2a/3): repo-relative file + title path + project, as drained from
248
+ * the runner's lifecycle spool. When an expected set is registered,
249
+ * THIS identity decides membership — a test outside the registered set
250
+ * is refused. Optional only for runs without a registered expected set.
251
+ */
252
+ file?: string;
253
+ titlePath?: readonly string[];
254
+ project?: string | null;
255
+ /**
256
+ * Phase 4 claim injection: the mapped obligation claims this test must
257
+ * land its evidence on (resolved from the sidecar/native mappings by
258
+ * the orchestrating CLI and carried by the supervisor on the
259
+ * session-open path). Obligation-id-shaped, deduplicated by the
260
+ * witness; empty/absent for annotation-claimed tests, which keep
261
+ * working unchanged. Claims remain DECLARATIONS: they never satisfy
262
+ * anything by themselves.
263
+ */
264
+ claims?: readonly string[];
265
+ }
266
+ /** `POST /sessions/open` response: the session binding (runId, sessionId, testId, worker). */
267
+ export interface SessionOpenResponse extends SessionCredential {
268
+ /** Witness-monotonic tick at open (the session clock origin). */
269
+ openedTick: number;
270
+ }
271
+ /** `POST /sessions/close` body: the supervisor seals the session with the observed outcome. */
272
+ export interface SessionCloseRequest {
273
+ sessionId: string;
274
+ /** The observed test outcome (e.g. 'passed' | 'failed'); recorded, never graded here. */
275
+ outcome?: string;
276
+ }
277
+ /** `POST /sessions/close` response. */
278
+ export interface SessionCloseResponse {
279
+ sealed: true;
280
+ }
281
+ /**
282
+ * `POST /sessions/release` body: the supervisor releases a session's
283
+ * WORKER SLOT before the runner's outcome for that test has arrived.
284
+ *
285
+ * The supervisor calls it on the WORKER's own lifecycle end (the end
286
+ * the worker spools as soon as the test itself is finished), so the
287
+ * next test that same worker runs opens its session immediately instead
288
+ * of queueing behind a main-process event that may still be seconds
289
+ * away. Nothing is credited: the session stops accepting submissions,
290
+ * its proxy dies, and only the runner's later `POST /sessions/close`
291
+ * records the outcome — an outcome that never arrives leaves the
292
+ * session without one, which grades not-passed.
293
+ */
294
+ export interface SessionReleaseRequest {
295
+ sessionId: string;
296
+ }
297
+ /** `POST /sessions/release` response. */
298
+ export interface SessionReleaseResponse {
299
+ released: true;
300
+ }
301
+ /** `POST /sessions/resolve` body: the worker proves WHICH open session it runs under. */
302
+ export interface SessionResolveRequest {
303
+ testId: string;
304
+ workerIndex: number;
305
+ }
306
+ /** `POST /sessions/resolve` response (a resolvable credential, proxy prefix included). */
307
+ export interface SessionResolveResponse extends SessionCredential {
308
+ openedTick: number;
309
+ }
310
+ /** `POST /sessions/intervals/open` body: the fixture marks a UI-action observation interval. */
311
+ export interface IntervalOpenRequest {
312
+ sessionId: string;
313
+ sessionToken: string;
314
+ /** The UI operation the interval covers ('create' | 'read' | 'update' | 'delete'). */
315
+ operation: string;
316
+ }
317
+ /** `POST /sessions/intervals/open` response. */
318
+ export interface IntervalOpenResponse {
319
+ intervalId: string;
320
+ startTick: number;
321
+ }
322
+ /** `POST /sessions/intervals/close` body. */
323
+ export interface IntervalCloseRequest {
324
+ sessionId: string;
325
+ sessionToken: string;
326
+ intervalId: string;
327
+ }
328
+ /** `POST /sessions/intervals/close` response. */
329
+ export interface IntervalCloseResponse {
330
+ startTick: number;
331
+ endTick: number;
332
+ }
333
+ /**
334
+ * `POST /browser/surface` body (plan Phase 1 item 4): the worker-side
335
+ * fixture registers the consumer-declared surface descriptor for one
336
+ * open session. The descriptor's selectors are locators only — the
337
+ * engine verifies every outcome itself. The driven ORIGIN is never part
338
+ * of this body (a present `appBaseUrl` is rejected): the engine drives
339
+ * exactly the provisioned attested subject from trusted witness
340
+ * configuration. Requires the supervisor-issued session credential on
341
+ * an OPEN session; the record's testId is forced onto the session's
342
+ * value.
343
+ */
344
+ export interface BrowserSurfaceRequest {
345
+ sessionId: string;
346
+ sessionToken: string;
347
+ testId: string;
348
+ /** The consumer-declared surface descriptor (validated engine-side). */
349
+ surface: unknown;
350
+ }
351
+ /**
352
+ * `POST /browser/action` body: perform ONE constrained surface
353
+ * operation on the session's engine-owned page and issue
354
+ * engine-observed records for it.
355
+ */
356
+ export interface BrowserActionRequest {
357
+ sessionId: string;
358
+ sessionToken: string;
359
+ testId: string;
360
+ /** Obligation ids to issue the engine-observed records under. */
361
+ claimIds: string[];
362
+ /** The constrained operation to perform. */
363
+ operation: 'create' | 'read' | 'update' | 'delete';
364
+ /** Entered input (create/update); target entity (read/update/delete). */
365
+ fields?: Record<string, string>;
366
+ entityId?: string;
367
+ }
368
+ /** `POST /browser/action` response: the engine's own observation. */
369
+ export interface BrowserActionResponse {
370
+ /** Entity id OBSERVED from the rendered list (never declared). */
371
+ entityId: string;
372
+ /** The exact input values the engine typed. */
373
+ enteredFields: Record<string, string>;
374
+ /** The rendered fields the engine read back. */
375
+ renderedFields: Record<string, string>;
376
+ /** The app response status the engine captured for the mutation. */
377
+ appStatus: number;
378
+ /** Engine-side pre-observation id (create/update; consumed by persistence verify). */
379
+ preObservationId: string | null;
380
+ /** Closed UI-action interval used to bind the observed action and persistence read. */
381
+ anchorId: string;
382
+ /** Record ids the engine issued for this action (per claim). */
383
+ recordIds: string[];
384
+ }
385
+ /**
386
+ * `POST /browser/visible` body: re-read the rendered result for the
387
+ * engine-observed entity on the session's engine page and issue
388
+ * engine-observed visible-result records.
389
+ */
390
+ export interface BrowserVisibleRequest {
391
+ sessionId: string;
392
+ sessionToken: string;
393
+ testId: string;
394
+ /** Obligation ids to issue the engine-observed records under. */
395
+ claimIds: string[];
396
+ /** The engine-observed entity id (must match the action's). */
397
+ entityId: string;
398
+ /** The original action (decides row vs form readback). */
399
+ operation: 'create' | 'read' | 'update' | 'delete';
400
+ /** Optional for legacy clients; new fixture receipts bind visible reads to one action. */
401
+ anchorId?: string;
402
+ }
403
+ /** `POST /browser/visible` response. */
404
+ export interface BrowserVisibleResponse {
405
+ entityId: string;
406
+ /** The rendered fields the engine read. */
407
+ fields: Record<string, string>;
408
+ /** Record ids the engine issued (per claim). */
409
+ recordIds: string[];
410
+ }
411
+ /**
412
+ * Witness-held session state (plan Phase 1). Created only by
413
+ * `POST /sessions/open` (the trusted supervisor, verifier-key
414
+ * authenticated — enforcement-review fix 3); sealed by
415
+ * `POST /sessions/close` (same authority). Sealed sessions answer no
416
+ * submission, no interval, and no resolve call.
417
+ */
418
+ export interface TestSession {
419
+ /** Witness-issued session id (UUID). */
420
+ sessionId: string;
421
+ /** Per-session secret required on every submission. */
422
+ token: string;
423
+ /** The supervisor-registered testId (every record is forced onto it). */
424
+ testId: string;
425
+ /** The worker the session is bound to (one OPEN session per worker). */
426
+ workerIndex: number;
427
+ /**
428
+ * `open` accepts submissions; `sealed` and `outcome-pending` reject
429
+ * everything (fail closed). `outcome-pending` is a RELEASED session:
430
+ * the worker's own lifecycle end unbound its worker slot (so the
431
+ * worker's next test opens its session without waiting for the
432
+ * runner's main process), but the session's OUTCOME is still owed by
433
+ * the runner's reporter and is recorded by the later close.
434
+ */
435
+ status: 'open' | 'sealed' | 'outcome-pending';
436
+ /** Witness-monotonic tick at open. */
437
+ openedTick: number;
438
+ /** Witness-monotonic tick at seal (null while open). */
439
+ sealedTick: number | null;
440
+ /** The outcome the supervisor observed at close (recorded, never graded). */
441
+ outcome: string | null;
442
+ /**
443
+ * UI-action observation intervals keyed by intervalId. End ticks are
444
+ * null while the interval is open. Exchanges are consumable as this
445
+ * session's evidence only when their tick falls inside one of these
446
+ * intervals — setup traffic outside every interval is never credited.
447
+ */
448
+ intervals: Map<string, {
449
+ startTick: number;
450
+ endTick: number | null;
451
+ operation: string;
452
+ }>;
453
+ /**
454
+ * The session's dedicated observation-proxy origin when the run wires
455
+ * an observation proxy; null otherwise.
456
+ */
457
+ proxyUrl: string | null;
458
+ /** The dedicated proxy server (closed when the session seals). */
459
+ proxyServer: Server | null;
460
+ /**
461
+ * Phase 4 claim injection: the mapped obligation claims the supervisor
462
+ * registered at open (sorted, deduplicated, obligation-id-shaped;
463
+ * empty for annotation-only tests). Echoed in the session view so the
464
+ * supervisor writes claims.json from what the witness registered.
465
+ */
466
+ claims: string[];
467
+ /**
468
+ * The registered expected-set identity this session was minted for
469
+ * (enforcement-review fix 2a/2b); null when the run has no registered
470
+ * expected set. Groups the execution trace by expected test.
471
+ */
472
+ registered: {
473
+ testId: string | null;
474
+ project: string | null;
475
+ file: string;
476
+ titlePath: string[];
477
+ } | null;
478
+ /**
479
+ * Witness-side activity bound to this session: a monotonically
480
+ * increasing count of everything the witness itself observed under
481
+ * the session — ledger records issued with the session credential,
482
+ * recorded UI-action intervals, consumed engine-observed exchanges,
483
+ * and pre-observations. Diagnostic corroboration only (execution
484
+ * authority is the supervisor-observed trusted lifecycle).
485
+ */
486
+ activity: number;
487
+ /**
488
+ * Engine-browser surface registration (plan Phase 1 item 4): the
489
+ * validated consumer descriptor the engine drives for this session.
490
+ * The driven ORIGIN is never stored here — it resolves from trusted
491
+ * witness configuration (`targetBaseUrl`) on every call, so a
492
+ * suite-named frontend can never become the engine target.
493
+ */
494
+ engineSurface: {
495
+ surface: Record<string, unknown>;
496
+ } | null;
497
+ /**
498
+ * Twin path coverage (E64): true when the supervisor marked this
499
+ * session OBSERVATION-ONLY (it is a raw twin). Such a session's
500
+ * requests are recorded as shapes and nothing else: every submission
501
+ * from it is refused, so it can issue no record, no attestation and
502
+ * satisfy nothing. Never true for a session with claims.
503
+ */
504
+ observationOnly: boolean;
505
+ /**
506
+ * The request shapes this session's proxied traffic exercised, in
507
+ * first-seen order. Computed at record time from the method and
508
+ * target, so no raw URL and no non-allowlisted query value is ever
509
+ * stored. Empty in every run that did not ask for twin shapes.
510
+ */
511
+ twinShapes: TwinShape[];
512
+ }
513
+ /** One test's recorded twin shapes, as the supervisor read surface returns them. */
514
+ export interface TwinShapeReport {
515
+ /**
516
+ * The registered identity of the test these shapes belong to: the
517
+ * join key both sides speak (project, file, titlePath). Null only when
518
+ * the run registered no expected set, in which case the caller has no
519
+ * honest way to name the test and should say so.
520
+ */
521
+ identity: {
522
+ file: string;
523
+ titlePath: string[];
524
+ project: string | null;
525
+ } | null;
526
+ /** The runner-assigned test id the session ran under (diagnostic). */
527
+ testId: string;
528
+ /** True when this session was marked observation-only (a raw twin). */
529
+ observationOnly: boolean;
530
+ /** The shapes it exercised, in first-seen order. */
531
+ shapes: readonly TwinShape[];
532
+ }
533
+ /** `GET /runs/twin-shapes` response (SUPERVISOR ONLY). */
534
+ export interface TwinShapesResponse {
535
+ /** True when this run recorded twin shapes at all. */
536
+ enabled: boolean;
537
+ /** Per test id, sorted; empty when the run recorded nothing. */
538
+ twins: readonly TwinShapeReport[];
539
+ }
540
+ /**
541
+ * One expected test registered by the supervisor BEFORE the run
542
+ * (enforcement-review fix 2a). Identity is (project, file, titlePath);
543
+ * `testId` is diagnostic (runner-assigned ids are not stable across
544
+ * enumeration and execution).
545
+ */
546
+ export interface ExpectedTestRegistration {
547
+ /** The runner-assigned test id, when enumeration bound one. */
548
+ testId?: string | null;
549
+ /** Runner project, or null when the runner reports none. */
550
+ project: string | null;
551
+ /** Repo-relative posix file (the identity join key). */
552
+ file: string;
553
+ /** Full title path (the identity join key). */
554
+ titlePath: readonly string[];
555
+ /**
556
+ * Twin path coverage (E64): the supervisor marked this test as the RAW
557
+ * twin of a witnessed one. Absent/false = an ordinary session.
558
+ *
559
+ * The mark travels with the REGISTRATION rather than as a list of
560
+ * runner test ids, because a runner-assigned id is not the id the test
561
+ * runs under: Playwright hashes the test's file path relative to the
562
+ * config it loaded, so an id enumerated from the repository's own
563
+ * config is not the id a supervised run opens the session with. The
564
+ * identity (project, file, titlePath) is what both sides speak.
565
+ */
566
+ observationOnly?: boolean;
567
+ }
568
+ /** `POST /runs/expected-set` body (SUPERVISOR ONLY). */
569
+ export interface ExpectedSetRequest {
570
+ /** The expected tests, fixed before the run. */
571
+ tests: readonly ExpectedTestRegistration[];
572
+ }
573
+ /** `POST /runs/expected-set` response. */
574
+ export interface ExpectedSetResponse {
575
+ /** The set is bound to this run. */
576
+ bound: true;
577
+ /** Domain-separated digest over the registered set. */
578
+ enumerationDigest: string;
579
+ /** Number of registered expected tests. */
580
+ count: number;
581
+ }
582
+ /** `GET /runs/execution-trace` response (SUPERVISOR ONLY; fix 2b). */
583
+ export interface ExecutionTraceResponse {
584
+ /** The digest of the registered expected set (null when none). */
585
+ enumerationDigest: string | null;
586
+ /** Per expected test, every session the witness recorded. */
587
+ tests: Array<{
588
+ testId: string | null;
589
+ project: string | null;
590
+ file: string;
591
+ titlePath: string[];
592
+ sessions: TracedSession[];
593
+ }>;
594
+ }
595
+ /** Witness runtime configuration (env-derived by the bin, explicit in tests). */
596
+ export interface WitnessOptions {
597
+ /**
598
+ * ADR 0004 D7 (plan §8 / D1): when set, the witness also starts a
599
+ * loopback reverse proxy forwarding to this base URL and records
600
+ * every forwarded request as an engine observation (the runtime HTTP
601
+ * evidence channel). Transport-only: the observation proves the
602
+ * witness observed an HTTP exchange; test attribution is
603
+ * suite-claimed. Must be loopback.
604
+ */
605
+ proxyTarget?: string;
606
+ /**
607
+ * Timing chaos (E63): the seeded release plan the observation proxy
608
+ * applies to proxied app RESPONSES. Null/absent is the
609
+ * byte-identical no-chaos path; a set plan changes timing only —
610
+ * bytes, status, headers and evidence semantics never move.
611
+ */
612
+ chaos?: ChaosOptions | null;
613
+ /**
614
+ * Twin path coverage (E64): the observation-only shape recording the
615
+ * proxy keeps for every request, so the CLI can compare a raw test
616
+ * with its witnessed twin by request shape. Null/absent is the
617
+ * byte-identical path: no shape is computed and none is stored. A
618
+ * shape is never evidence — it cannot issue a record, satisfy an
619
+ * obligation, or enter an attestation.
620
+ */
621
+ twinShapes?: TwinShapePlan | null;
622
+ /**
623
+ * Observation-proxy mount prefix (with `proxyTarget`; e.g. `/api`).
624
+ *
625
+ * WHY this is an explicit deployment-topology declaration (same
626
+ * philosophy as `urlBuilders[].base`): in a real deployment the
627
+ * browser reaches the backend THROUGH the frontend — a dev proxy or
628
+ * edge serves `/api/ops/...` while the backend route is `/ops/...`.
629
+ * Whether such a prefix exists is a property of the deployment the
630
+ * engine cannot derive from source, and guessing wrong would silently
631
+ * mismatch obligation identities. When set, the observation proxy
632
+ * forwards the STRIPPED path to the proxy target AND records the
633
+ * STRIPPED path in observation records, so observations match the
634
+ * backend-derived obligation identities the suite claims
635
+ * (`evidence.http.observe({path: '/ops/x'})`). Requests outside the
636
+ * prefix pass through and are recorded unstripped. Absent/null strips
637
+ * nothing — forwarding and recording stay byte-identical to an
638
+ * unmounted proxy.
639
+ */
640
+ mountPath?: string | null;
641
+ /** Run manifest identity (pin #4). */
642
+ runId: string;
643
+ /** Per-run token; every call must carry `x-gateforge-run: <token>`. */
644
+ token: string;
645
+ /**
646
+ * Verifier key for the attestation surface (authenticated
647
+ * `POST /run-context` binding, `GET /ledger-attestation`, and the
648
+ * manifest v2 `attestation` envelope): shared by the orchestrator
649
+ * with the witness and the evaluating CLI, NEVER with the tested
650
+ * suite. When absent the witness serves no attestation and its
651
+ * manifest append stays unauthenticated (downstream evaluation fails
652
+ * closed for witnessed records). Environment ONLY (see
653
+ * `witness/bin.ts`) — never argv, stdout, state files, or suite env.
654
+ */
655
+ verifierKey?: string | null;
656
+ /** Run-state dir; the witness appends its issued recordIds to manifest.json at shutdown. */
657
+ stateDir?: string | null;
658
+ /** Directory of reviewed `.mjs` adapters (default `.gateforge/adapters`). */
659
+ adaptersDir?: string | null;
660
+ /**
661
+ * Operator-issued credential the witness presents on its OWN engine-side
662
+ * adapter reads (GF-10 mediation; dogfood deployment, 2026-09-05/07).
663
+ *
664
+ * WHY: adapters perform GET-only reads of the app's own collection
665
+ * routes to observe persisted state, and those routes authenticate —
666
+ * an unauthenticated loopback read is a 401, so the engine could not
667
+ * observe state at all. The credential is issued by the DEPLOYMENT
668
+ * OPERATOR to the WITNESS ONLY (env `GATEFORGE_ADAPTER_READ_AUTHORIZATION`
669
+ * or `--adapter-read-authorization`), never to the tested suite, and is
670
+ * a read-only service principal of the same trust class as the verifier
671
+ * key. When unset, adapter reads stay unauthenticated (previous
672
+ * behavior) and protected collections simply fail closed with 401/409.
673
+ */
674
+ adapterReadAuthorization?: string | null;
675
+ /** Classifications document path (YAML) for the primaryKey map + adapter aliases. */
676
+ classificationsPath?: string | null;
677
+ /**
678
+ * Attestation subject (the SUT the UI drives). MUST be loopback
679
+ * (GF-10); the witness probes its env-fingerprint marker at startup
680
+ * when `targetFingerprint` is set (GF-13 minimal v1 attestation).
681
+ */
682
+ targetBaseUrl?: string | null;
683
+ /** Expected `x-gateforge-env-fingerprint` marker at the attestation subject. */
684
+ targetFingerprint?: string | null;
685
+ /** Default base for adapter reads; a per-adapter `baseUrl` wins. */
686
+ adapterBaseUrl?: string | null;
687
+ /** Per-witness-call timeout (pin #7 default 5s). */
688
+ requestTimeoutMs?: number;
689
+ /**
690
+ * Completion-barrier deadline for async behavior effects (Phase 5):
691
+ * `awaitBarrier` observers get this long to report a real checkpoint.
692
+ * Default 30s. A timeout is failure, never success.
693
+ */
694
+ barrierTimeoutMs?: number;
695
+ /** Injected clock for `issuedAt` (ISO-8601); default = system now. */
696
+ now?: () => string;
697
+ /** Host to bind; default `127.0.0.1` (loopback). */
698
+ host?: string;
699
+ /**
700
+ * Engine-browser launcher override (programmatic use only — never
701
+ * env-derived): the executable the engine drives stays engine code
702
+ * regardless of which Chromium launches. Tests inject a failing
703
+ * launcher to prove the fail-closed path (E16); production leaves it
704
+ * unset for the pinned Chromium.
705
+ */
706
+ engineBrowserLauncher?: import('./browser.js').EngineBrowserLauncher;
707
+ /**
708
+ * Trusted fixture/actor provider (plan 2026-09-19 §4.5, Phase 4):
709
+ * injected by the trusted controller harness (engine-owned bundle),
710
+ * never by suite code. Null (default) means strong behavior cases
711
+ * block with a missing-provider cause — suite-supplied fixtures are
712
+ * never a fallback.
713
+ */
714
+ fixtureProvider?: import('./fixture-provider.js').FixtureProvider | null;
715
+ /**
716
+ * Engine-owned queue channel: the
717
+ * witness's own read of a background queue plus the write side that
718
+ * produces the deliveries it grades. Null (default) means the
719
+ * repository declares no `queueObserver`, so every `engine-task` case
720
+ * blocks with a naming cause — never a suite-supplied substitute.
721
+ */
722
+ queueChannel?: import('../queue/observer.js').QueueChannel | null;
723
+ }
724
+ /**
725
+ * The normalized adapter module contract (pin #8).
726
+ *
727
+ * `probeServer` (optional; the server-witnessed persistence channel) is
728
+ * the adapter's server-side probe: the witness executes it ONLY in the
729
+ * witness process (trusted side) against the app's own database/state —
730
+ * for backend-only tables (a transactional outbox) that can never
731
+ * honestly appear in a UI. Contract:
732
+ *
733
+ * ```js
734
+ * export default {
735
+ * ...,
736
+ * async probeServer(ctx, subject) {
737
+ * const row = await db.query('select * from outbox where id = $1', [subject]);
738
+ * return { found: row !== undefined, fields: row ?? null };
739
+ * },
740
+ * };
741
+ * ```
742
+ *
743
+ * `subject` is the entity key EXACTLY as the persistence intent declared
744
+ * it (scalar for single-column primary keys, column-keyed object for
745
+ * composite keys); the return MUST be
746
+ * `{found: boolean, fields: Record<string, unknown> | null}`. A throw or
747
+ * a malformed shape resolves the intent as a typed
748
+ * SERVER_PROBE_UNAVAILABLE failure — never to satisfaction (fail
749
+ * closed). Adapters without the export simply cannot serve the server
750
+ * channel; the browser path is unaffected.
751
+ */
752
+ export interface EvidenceAdapter {
753
+ /** GET-only transport. Returns the raw entity body, or null when absent. */
754
+ read: (ctx: AdapterContext, id: unknown) => Promise<unknown> | unknown;
755
+ /**
756
+ * Optional GET-only listing of the resource's entities (raw bodies).
757
+ * Powers engine-side pre-observations for create postconditions; when
758
+ * absent the witness refuses pre-observations for the resource.
759
+ */
760
+ list?: (ctx: AdapterContext) => Promise<unknown[]> | unknown[];
761
+ /** Enables entity-scoped create absence checks without a collection list. */
762
+ identity?: 'natural-key';
763
+ /** Declares normalized fields the adapter projects for persistence evidence. */
764
+ fields?: readonly string[];
765
+ /**
766
+ * Declares fields the SERVER computes on its own (a derived label, a
767
+ * server-side normalization, a counter). The engine skips the
768
+ * exact-value echo for exactly these keys and REPORTS the skip — a
769
+ * server-changed field is a declared fact about the app, never a
770
+ * silently ignored mismatch. Absent on every hand-written adapter
771
+ * that does not declare one (the previous behavior, unchanged).
772
+ */
773
+ volatileFields?: readonly string[];
774
+ /** Projects the raw body onto {entityId, fields} — stamped from the RESPONSE. */
775
+ normalize: (body: unknown) => {
776
+ entityId: unknown;
777
+ fields: unknown;
778
+ };
779
+ /** Removal semantics the adapter's resource uses. */
780
+ deletion: 'hard' | 'archive';
781
+ /** Fingerprint the adapter's target environment must present. */
782
+ environmentFingerprint: string;
783
+ /** Optional base override for THIS adapter's reads. */
784
+ baseUrl?: string;
785
+ /**
786
+ * Optional SERVER PROBE, executed witness-side only (see the interface
787
+ * doc): observes the app database directly and reports the entity's
788
+ * presence + observed column state.
789
+ */
790
+ probeServer?: (ctx: AdapterContext, subject: unknown) => Promise<ServerProbeResult> | ServerProbeResult;
791
+ /**
792
+ * Optional OBSERVE binding (Observe channel, Phase 2): declares which
793
+ * proxied HTTP exchanges count as mutations of this resource when a
794
+ * suite-driven browser test runs. Each declared operation names the
795
+ * method + backend-facing path template; `{id}` marks the single
796
+ * segment carrying the entity id (required on read/update/delete,
797
+ * forbidden on create — a create id comes from the response the
798
+ * witness proxied, verified against the list-diff). Only
799
+ * declared operations are observe-eligible; anything else grades
800
+ * typed-missing. Observe additionally requires `list` (before-
801
+ * snapshots); an adapter without it can serve no observe obligation.
802
+ */
803
+ observe?: ObserveBinding;
804
+ /**
805
+ * Optional TRUSTED scope observation (plan 2026-09-19 §4.4, Phase 4):
806
+ * observes an independently controlled state source (read-only
807
+ * database credentials, a trusted state sidecar, or equivalent
808
+ * owner-provisioned observer outside candidate code) for one approved
809
+ * scope key within one fixture namespace. Reading only the
810
+ * candidate's own GET handler is insufficient for the protected
811
+ * profile. Adapters lacking this method still serve legacy proofs;
812
+ * they cannot satisfy strong contracts requiring scoped observation.
813
+ */
814
+ snapshotScope?: (ctx: AdapterContext, input: SnapshotScopeInput) => Promise<ScopeSnapshot> | ScopeSnapshot;
815
+ /**
816
+ * Optional completion-barrier observer (plan 2026-09-19 §4.4): waits
817
+ * for a real completion/queue checkpoint for an engine-issued
818
+ * operation. An observer, not a state maker: it must not sleep-then-
819
+ * true, mutate state, or return true on timeout.
820
+ */
821
+ awaitBarrier?: (ctx: AdapterContext, input: BarrierInput) => Promise<{
822
+ complete: boolean;
823
+ checkpoint: string;
824
+ }> | {
825
+ complete: boolean;
826
+ checkpoint: string;
827
+ };
828
+ }
829
+ /**
830
+ * One observe-eligible mutation shape: the HTTP method (uppercase) and
831
+ * the backend-facing path template. `{id}` matches exactly one non-
832
+ * empty path segment and binds the entity id for read/update/delete.
833
+ * A read may instead declare `collection`, naming its entities in the
834
+ * returned rows instead of in the path.
835
+ */
836
+ export interface ObserveMutation {
837
+ /** Concrete uppercase HTTP method, e.g. `'POST'`. */
838
+ method: string;
839
+ /** Backend-facing absolute path, e.g. `'/api/v2/accounts/{id}'`. */
840
+ path: string;
841
+ /**
842
+ * Optional COLLECTION read declaration (read + GET only): the
843
+ * entities this operation names come from the returned rows of the
844
+ * response the witness proxied, resolved against the witness's own
845
+ * session-open snapshot. Forbidden on create/update/delete and on
846
+ * any read whose path carries `{id}`; absent, the read keeps binding
847
+ * `{id}` from the path exactly as before.
848
+ */
849
+ collection?: ObserveCollection;
850
+ }
851
+ /**
852
+ * The COLLECTION shape of an observe read: the response the real UI
853
+ * rendered lists its entities, so the entity ids come from the rows
854
+ * themselves rather than from a path template. `rowsKey` names the
855
+ * object property holding the row array; omitting it declares that the
856
+ * response ROOT is the row array. `idKey` names the row property
857
+ * carrying the entity id — the only field the witness reads out of a
858
+ * returned row.
859
+ *
860
+ * Declared, never inferred: an adapter whose read binding carries no
861
+ * `collection` keeps the by-`{id}` behavior unchanged.
862
+ */
863
+ export interface ObserveCollection {
864
+ /** Property holding the row array; absent = the root is the array. */
865
+ rowsKey?: string;
866
+ /** Row property carrying the entity id. */
867
+ idKey: string;
868
+ }
869
+ /** Per-operation observe bindings for one resource adapter. */
870
+ export interface ObserveBinding {
871
+ create?: ObserveMutation;
872
+ read?: ObserveMutation;
873
+ update?: ObserveMutation;
874
+ delete?: ObserveMutation;
875
+ }
876
+ /** The shape an adapter `probeServer` must return (validated witness-side). */
877
+ export interface ServerProbeResult {
878
+ /** Whether the probed entity exists in the engine-observed state. */
879
+ found: boolean;
880
+ /** The observed column state when found (null when absent). */
881
+ fields: Record<string, unknown> | null;
882
+ }
883
+ /** `POST /runs/behavior-catalog` request (supervisor only, one-time bind). */
884
+ export interface BehaviorCatalogRequest {
885
+ /** Compiled behavior catalog (validated against the core schema). */
886
+ catalog: unknown;
887
+ /**
888
+ * Allowed case/test assignments: testId → required case ids the
889
+ * supervisor permits that test to execute. Every case id must exist
890
+ * in the catalog; every id must belong to an obligation the mapping
891
+ * already claims (checked by the supervisor before sending).
892
+ */
893
+ assignments: Record<string, string[]>;
894
+ /**
895
+ * Complete route inventory for principal attribution (resourceId,
896
+ * method, canonicalPath per route, sorted). The grader re-resolves
897
+ * every attempt against this exact set — never a claim or payload.
898
+ */
899
+ routes: unknown;
900
+ /**
901
+ * Authority profile digest the witness seals into every case record
902
+ * (engine bundle binding, provisioned by the trusted controller).
903
+ */
904
+ authorityProfileDigest: string;
905
+ }
906
+ /** `POST /runs/behavior-catalog` response. */
907
+ export interface BehaviorCatalogResponse {
908
+ bound: true;
909
+ caseCount: number;
910
+ assignmentCount: number;
911
+ routeCount: number;
912
+ }
913
+ /** `POST /behavior/execute` request: exactly these keys, nothing else. */
914
+ export interface BehaviorExecuteRequest {
915
+ sessionId: string;
916
+ sessionToken: string;
917
+ caseId: string;
918
+ }
919
+ /**
920
+ * `POST /behavior/execute` response: a REDACTED result reference. Actor
921
+ * credential material, fixture subjects, and expectations never appear
922
+ * here — the worker may name an allowed case but can never read or
923
+ * override actor credentials, expectations, before-state, or origin.
924
+ */
925
+ export interface BehaviorExecuteResponse {
926
+ caseId: string;
927
+ executionId: string;
928
+ /** Isolated fixture namespace for this execution. */
929
+ namespace: string;
930
+ /** Authoritative before checkpoint (empty until the principal runs). */
931
+ beforeCheckpoint: string | null;
932
+ /** Lifecycle state after this call. */
933
+ state: string;
934
+ }
935
+ /** `POST /behavior/principal` request: drive the principal operation. */
936
+ export interface BehaviorPrincipalRequest {
937
+ sessionId: string;
938
+ sessionToken: string;
939
+ executionId: string;
940
+ }
941
+ /**
942
+ * `POST /behavior/principal` response: redacted seal reference. The
943
+ * witness-owned driver executed, the barrier resolved, after-state was
944
+ * observed, and the `behavior.case` record(s) were issued — grading
945
+ * happens core-side, never here.
946
+ */
947
+ export interface BehaviorPrincipalResponse {
948
+ caseId: string;
949
+ executionId: string;
950
+ recordIds: string[];
951
+ state: string;
952
+ }
953
+ /** Witness-side lifecycle state of one required-case execution. */
954
+ export type CaseExecutionState = 'fixture-prepared' | 'before-snapshot-complete' | 'principal-executing' | 'principal-captured' | 'barrier-reached' | 'after-snapshot-complete' | 'sealed' | 'failed';
955
+ /** One entity inside a trusted scope snapshot. */
956
+ export interface SnapshotEntity {
957
+ /**
958
+ * Entity identity: scalar for single-column keys, complete
959
+ * column-keyed object for composite keys (matching core identity
960
+ * behavior). Unknown identity blocks the case.
961
+ */
962
+ entityId: unknown;
963
+ /** Projected field map (exactly the declared scope fields). */
964
+ fields: Record<string, unknown>;
965
+ }
966
+ /**
967
+ * Strict trusted scope snapshot (plan 2026-09-19 §4.4): data, never a
968
+ * verdict. `complete:false`, pagination not exhausted, omitted declared
969
+ * fields, unknown identity, inconsistent checkpoints, or size-limit
970
+ * truncation blocks the case — the harness never silently samples.
971
+ */
972
+ export interface ScopeSnapshot {
973
+ /** Approved snapshot scope key. */
974
+ scope: string;
975
+ /** Fixture namespace actually observed (must equal the case lease). */
976
+ fixtureNamespace: string;
977
+ /** False when collection is partial for any reason. */
978
+ complete: boolean;
979
+ /** Authoritative checkpoint (before/after comparison + barriers). */
980
+ checkpoint: string;
981
+ /** Entities in canonical order (duplicate identity rejected). */
982
+ entities: SnapshotEntity[];
983
+ /** True when pagination was exhausted (absent = unknown = incomplete). */
984
+ exhausted?: boolean;
985
+ /** Total size when the scope enforces a limit (truncation blocks). */
986
+ totalSize?: number;
987
+ }
988
+ /** Input to an adapter `snapshotScope` observation. */
989
+ export interface SnapshotScopeInput {
990
+ /** Approved snapshot scope key. */
991
+ scope: string;
992
+ /** Fixture namespace to observe (never the whole database). */
993
+ fixtureNamespace: string;
994
+ }
995
+ /** Input to an adapter `awaitBarrier` observation. */
996
+ export interface BarrierInput {
997
+ /** Approved snapshot scope key. */
998
+ scope: string;
999
+ /** Fixture namespace to observe. */
1000
+ fixtureNamespace: string;
1001
+ /** Engine-issued operation id awaiting completion. */
1002
+ operationId: string;
1003
+ /** Hard deadline in milliseconds (timeout is failure, not success). */
1004
+ deadlineMs: number;
1005
+ }
1006
+ /** The transport handed to `read` (GET-only, engine-mediated). */
1007
+ /**
1008
+ * A per-session adapter identity (plan 2026-09-25 Phase 4b item 3b):
1009
+ * the credential one OPEN session registered for ITSELF through
1010
+ * `POST /sessions/identity`, so an adapter read that must happen inside a
1011
+ * tenant the test just created can run as that tenant.
1012
+ *
1013
+ * It changes WHO the engine reads as — nothing else. The engine still
1014
+ * performs every read; a wrong tenant simply makes the row unfound
1015
+ * (the app answers 403/404 and the entity grades absent), so the failure
1016
+ * mode is a closed door, never an open verdict. The values live in
1017
+ * witness memory for the length of the session and are never written to
1018
+ * a record, the run state, a log or a report.
1019
+ */
1020
+ export interface SessionIdentity {
1021
+ /** The adapter seat this identity serves (must be a declared seat). */
1022
+ readonly seat: string;
1023
+ /**
1024
+ * The seat's credential VALUES, keyed by the same witness environment
1025
+ * variable names the seat declares (`auth.seats.<seat>.credentials`
1026
+ * maps login field to env var). Every variable the seat declares must
1027
+ * be present: a partial identity fails closed instead of silently
1028
+ * completing itself from the process-global seat.
1029
+ */
1030
+ readonly values: Readonly<Record<string, string>>;
1031
+ }
1032
+ /** `POST /sessions/identity` request (session-authenticated). */
1033
+ export interface SessionIdentityRequest extends SessionIdentity {
1034
+ /** The session the identity belongs to (its own, never another's). */
1035
+ sessionId: string;
1036
+ /** The session's witness-issued secret; authorizes this call alone. */
1037
+ sessionToken: string;
1038
+ }
1039
+ /**
1040
+ * `POST /sessions/identity` response. It echoes the SEAT NAME only — the
1041
+ * credential itself never leaves the witness process.
1042
+ */
1043
+ export interface SessionIdentityResponse {
1044
+ registered: true;
1045
+ seat: string;
1046
+ }
1047
+ /** The transport handed to `read` (GET-only, engine-mediated). */
1048
+ export interface AdapterContext {
1049
+ /**
1050
+ * The supervisor-opened session this read runs under, when the caller
1051
+ * is a test session (absent for engine-driven and probe reads). It is
1052
+ * what a {@link SessionIdentity} is scoped to.
1053
+ */
1054
+ sessionId?: string;
1055
+ /**
1056
+ * The identity THAT session registered for itself, or null. Present
1057
+ * only while the read belongs to the registering session.
1058
+ */
1059
+ sessionIdentity?: SessionIdentity | null;
1060
+ /** The resolved read base for this adapter. */
1061
+ baseUrl: string;
1062
+ /** The resource this adapter serves. */
1063
+ resourceId: string;
1064
+ /**
1065
+ * The ONLY outbound primitive available to adapters: a GET returning
1066
+ * a minimal response view (status + JSON/text body + headers).
1067
+ */
1068
+ get: (path: string) => Promise<{
1069
+ status: number;
1070
+ json(): Promise<unknown>;
1071
+ text(): Promise<string>;
1072
+ headers: Headers;
1073
+ }>;
1074
+ /**
1075
+ * Headers adapters MUST attach to any direct fetch they perform
1076
+ * instead of `get` (dogfood, 2026-09-05): carries the operator-issued
1077
+ * engine read credential (`WitnessOptions.adapterReadAuthorization`)
1078
+ * so engine-side GET-only state observation can authenticate against
1079
+ * the app's own collection routes. Never suite-supplied, never
1080
+ * attached to browser traffic.
1081
+ */
1082
+ headers?: Record<string, string>;
1083
+ }
1084
+ /** Full witness-issued record (superset of the pinned wire response). */
1085
+ export type IssuedRecord = EvidenceRecord;
1086
+ /** Result of spawning/attaching a witness service. */
1087
+ export interface WitnessHandle {
1088
+ /** Base URL (loopback, OS-assigned port). */
1089
+ url: string;
1090
+ /** Stop the server; appends issued recordIds to the run manifest (pin #4/#7). */
1091
+ proxyUrl: string | null;
1092
+ stop: () => Promise<void>;
1093
+ }
1094
+ //# sourceMappingURL=types.d.ts.map