wicked-crew 0.15.2 → 0.15.4

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 (36) hide show
  1. package/dist/api/gate-cache.d.ts +98 -17
  2. package/dist/api/gate-cache.d.ts.map +1 -1
  3. package/dist/api/gate-cache.js +136 -25
  4. package/dist/api/gate-cache.js.map +1 -1
  5. package/dist/api/recording.d.ts +3 -2
  6. package/dist/api/recording.d.ts.map +1 -1
  7. package/dist/api/recording.js +13 -1
  8. package/dist/api/recording.js.map +1 -1
  9. package/dist/api/routes.d.ts.map +1 -1
  10. package/dist/api/routes.js +537 -344
  11. package/dist/api/routes.js.map +1 -1
  12. package/dist/api/seat-work-default.d.ts +22 -0
  13. package/dist/api/seat-work-default.d.ts.map +1 -0
  14. package/dist/api/seat-work-default.js +91 -0
  15. package/dist/api/seat-work-default.js.map +1 -0
  16. package/dist/api/server.js +1 -1
  17. package/dist/api/server.js.map +1 -1
  18. package/dist/studio/assets/{index-BhpoNOI0.js → index-CK-ppFAd.js} +218 -217
  19. package/dist/studio/assets/index-Chzb3FOa.css +32 -0
  20. package/dist/studio/editors/wicked-deck/editor.json +1 -1
  21. package/dist/studio/editors/wicked-deck/index.html +1 -1
  22. package/dist/studio/editors/wicked-doc/editor.json +1 -1
  23. package/dist/studio/editors/wicked-doc/index.html +1 -1
  24. package/dist/studio/editors/wicked-page/editor.json +1 -1
  25. package/dist/studio/editors/wicked-page/index.html +1 -1
  26. package/dist/studio/editors/wicked-video/editor.json +1 -1
  27. package/dist/studio/editors/wicked-video/index.html +1 -1
  28. package/dist/studio/index.html +2 -2
  29. package/dist/studio/testid-inventory.json +87 -27
  30. package/dist/team/routes.d.ts +11 -1
  31. package/dist/team/routes.d.ts.map +1 -1
  32. package/dist/team/routes.js +58 -48
  33. package/dist/team/routes.js.map +1 -1
  34. package/endpoint-manifest.json +1 -1
  35. package/package.json +4 -3
  36. package/dist/studio/assets/index-BEFOUR6c.css +0 -32
@@ -25,6 +25,14 @@ export interface GateCacheEntry {
25
25
  * ABSENT when the engine did not say (an older engine, a row written before the field).
26
26
  */
27
27
  gateKind?: string;
28
+ /**
29
+ * The answer tokens the engine offered (`awaitingHuman.choices`; issue #969) — e.g. a
30
+ * `team_dispute` gate's `['approve', 'request_changes', 'reject']`. Without them a client that
31
+ * missed the live frame could draw only Approve/Reject. ABSENT when the engine offered none.
32
+ */
33
+ choices?: string[];
34
+ /** The index into `choices` of the engine's recommended answer. ABSENT without `choices`. */
35
+ recommended?: number;
28
36
  }
29
37
  /**
30
38
  * Detect whether a gate prompt is a PURE sandbox/tool refusal — the worker reporting it could not
@@ -41,6 +49,41 @@ export declare function detectRefusal(text: string): {
41
49
  matched: boolean;
42
50
  reason: string;
43
51
  } | undefined;
52
+ /**
53
+ * The answer set an `awaitingHuman` frame offered, kept only when well-formed (issue #969):
54
+ * `choices` must be a non-empty array of strings, and `recommended` an integer index inside it.
55
+ * A malformed value is dropped rather than served — a skin would render it as buttons. Spread the
56
+ * result, so a gate the engine offered nothing on stays byte-identical on the wire.
57
+ */
58
+ export declare function offeredChoices(event: CoreEvent): {
59
+ choices?: string[];
60
+ recommended?: number;
61
+ };
62
+ /**
63
+ * The open gate a run's event history records, or `undefined` — the last `awaitingHuman` not
64
+ * followed by a resumed/terminal event. `events` is the run's log oldest-first. Pure: the same
65
+ * `fold` the live stream uses, over a scratch map, so a replay cannot disagree with what the live
66
+ * stream would have produced from the same events (FINDING-051).
67
+ */
68
+ export declare function replayOpenGate(runId: string, events: CoreEvent[]): GateCacheEntry | undefined;
69
+ /**
70
+ * What one read of a run's open gate from the record yields: the gate; `null` (the record holds
71
+ * no open gate); `'no-log'` (this build cannot read the log, so nothing can be said);
72
+ * `'ambiguous'` (the engine's open gate rows disagree on the ord, so the record cannot say which
73
+ * gate is open — crew#1083 round 5).
74
+ */
75
+ export type GateRead = GateCacheEntry | null | 'no-log' | 'ambiguous';
76
+ /**
77
+ * What `GateCache.resolve` yields: a read's result, or `'unstable'` — live gate events kept landing
78
+ * while the record was read, so nothing could be verified (crew#1083 round 3). Retryable.
79
+ */
80
+ export type GateResolution = GateRead | 'unstable';
81
+ /** Narrows a resolution to an open gate: `null`, `'no-log'`, `'ambiguous'` and `'unstable'` are not one. */
82
+ export declare function isOpenGate(resolution: GateResolution | undefined): resolution is GateCacheEntry;
83
+ /** The one log call the cache makes (`resolve`): `console` by default, the daemon's logger in `server.ts`. */
84
+ export interface GateCacheLog {
85
+ warn(message: string): void;
86
+ }
44
87
  /**
45
88
  * Gate-prompt cache over a durable log (DES-STUDIO-001 §3.3).
46
89
  *
@@ -52,8 +95,9 @@ export declare function detectRefusal(text: string): {
52
95
  * prune-only, so a daemon restart dropped the map and **nothing could put an entry back**. A parked
53
96
  * run then kept holding the operator without being able to say what it was asking. The prompt was
54
97
  * never actually lost — core records `awaitingHuman` to the event log (core#139) — it was simply
55
- * never read. `rebuild` is that read, which demotes this map to what it was always meant to be: a
56
- * latency optimisation, not the system of record.
98
+ * never read. `resolve`, fed the route's read of the durable row and the log, is that read, which
99
+ * demotes this map to what it was always meant to be: a latency optimisation, not the system of
100
+ * record.
57
101
  *
58
102
  * NOTE: the full HITL lifecycle from §3.3 (advisory / hard-expiry timers, append-only message log,
59
103
  * auto-cancel-on-expiry, synthesized `gateLifecycle` frames) is a deliberate follow-on. This
@@ -61,30 +105,67 @@ export declare function detectRefusal(text: string): {
61
105
  * always `open`.
62
106
  */
63
107
  export declare class GateCache {
108
+ private readonly log;
64
109
  private readonly entries;
110
+ /**
111
+ * Per-run count of the LIVE gate-opening and gate-closing events `ingest` has folded. `resolve`
112
+ * compares it across an async read of the record: a read some event overlapped is run again,
113
+ * never adopted. Never pruned — one number per run seen.
114
+ */
115
+ private readonly revisions;
116
+ constructor(log?: GateCacheLog);
65
117
  /** Fold one live CoreEvent into the cache. */
66
118
  ingest(event: CoreEvent): void;
119
+ private revision;
67
120
  /**
68
- * Rebuild one run's gate from its durable event history and adopt the result (FINDING-051).
121
+ * The run's open gate: the cached entry, else what `read` finds in the record — the durable row,
122
+ * the log — adopted and returned once a read completes with no live gate event of the run's
123
+ * having landed meanwhile. `read` must not consult this cache: it is run again when one does.
124
+ *
125
+ * Neither side of such an overlap is known to be the newer (crew#1083 review). The record may
126
+ * have moved on since the read began — a live `awaitingHuman` for the next gate while an older
127
+ * row was being read — or the engine's pump may have delivered an OLDER frame late: an
128
+ * `awaitingHuman` for a gate the row already shows answered, or that gate's `resumed`. The
129
+ * revision counts arrivals, not order, so the cache cannot pick a winner; the first cut let the
130
+ * live side win and served a late ord-3 frame over a current ord-4 row, which let a decision
131
+ * naming ord 3 pass the gate check and answer ord 4. So no winner is picked. The read is run
132
+ * again, from scratch: the engine writes the row with the pause, so a re-read begun after the
133
+ * overlapping frame arrived reflects whatever that frame announced, and the first read that
134
+ * completes with nothing having landed is adopted.
69
135
  *
70
- * `events` is the run's log oldest-first. Folding all of it yields the last `awaitingHuman` not
71
- * followed by a resumed/terminal event — i.e. an open gate, or nothing.
136
+ * A clean read is the record's word in BOTH directions (crew#1083 round 3): a gate it finds
137
+ * replaces whatever an overlapped `ingest` folded meanwhile, and "no open gate" / "cannot read"
138
+ * EVICTS it — otherwise a late frame folded during attempt one outlived the clean attempt two
139
+ * that found its gate already answered, and was served as a cache hit, unread, from then on. A
140
+ * read that throws evicts the same way. After `RESOLVE_ATTEMPTS` overlapped reads nothing was
141
+ * verified, so nothing is served: the entry is evicted and the result is `'unstable'`, never the
142
+ * unverified entry — an ordinary ordered backlog (`awaitingHuman(2)`, `resumed(2)`,
143
+ * `awaitingHuman(3)`, one landing per read) left the cache holding ord 3 while every read found
144
+ * ord 4. The run is logged once.
72
145
  *
73
- * Deliberately does NOT cross-check the run's live status first: that costs a `sessionsDetail()`
74
- * on a read path, and the write side is already guarded — approving a run that has moved on is
75
- * refused by the `status === 'awaiting_human'` check on `POST /runs/:id/gate`. So the worst a
76
- * stale replay can do is show a prompt that then declines to be approved, and `reconcile` drops
77
- * it on the next list. The failure mode is a confusing read, never a bad write.
146
+ * One read in flight per run (agy, round 4): while a read is running, an entry a live frame
147
+ * folds is unverified, so a concurrent `resolve` awaits the read in flight instead of taking the
148
+ * entry as a hit, and the entry is evicted before a re-read so a `get` meanwhile serves nothing
149
+ * unverified either.
150
+ */
151
+ resolve(runId: string, read: () => Promise<GateRead>): Promise<GateResolution>;
152
+ /** In-flight resolutions per run: `resolve` hands every concurrent caller the same one. */
153
+ private readonly inflight;
154
+ private hydrate;
155
+ /**
156
+ * The cached open gate for a run, if any — and nothing while a read of it is in flight: an entry
157
+ * a live frame folded meanwhile is unverified until that read settles, so a sync caller (the GET
158
+ * route's hit path, a steering prompt) falls through to `resolve` and waits (agy, round 4).
78
159
  */
79
- rebuild(runId: string, events: CoreEvent[]): GateCacheEntry | undefined;
80
- /** The cached open gate for a run, if any. */
81
160
  get(runId: string): GateCacheEntry | undefined;
82
161
  /**
83
- * Adopt an entry hydrated from the DURABLE `interaction_requests` table (DES-PROJECT-001
84
- * §5.3). With that table, this cache is finally what its own comments always claimed — a
85
- * latency layer over durable truth: the route reads the table on a miss and parks the result
86
- * here so the next poll is a map hit. `rebuild` (event-log replay) remains the fallback for
87
- * engines predating the table.
162
+ * Drop the run's cached gate: the engine's record shows another gate open, or none (crew#1083
163
+ * round 7). The next read hydrates from the record.
164
+ */
165
+ forget(runId: string): void;
166
+ /**
167
+ * Seed the run's gate synchronously. Nothing can land between the caller's decision and this
168
+ * set, so it needs no guard; a read that awaited anything goes through `resolve`.
88
169
  */
89
170
  adopt(runId: string, entry: GateCacheEntry): void;
90
171
  /** Drop cache entries whose run is no longer `awaiting_human` (self-healing on reconcile). */
@@ -1 +1 @@
1
- {"version":3,"file":"gate-cache.d.ts","sourceRoot":"","sources":["../../src/api/gate-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/D,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoB5F;AAoDD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAE7D,8CAA8C;IAC9C,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAI9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,cAAc,GAAG,SAAS;IAQvE,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI9C;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI;IAIjD,8FAA8F;IAC9F,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;CAQtC"}
1
+ {"version":3,"file":"gate-cache.d.ts","sourceRoot":"","sources":["../../src/api/gate-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/D,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,6FAA6F;IAC7F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoB5F;AAcD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAM7F;AA4CD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,cAAc,GAAG,SAAS,CAI7F;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD,4GAA4G;AAC5G,wBAAgB,UAAU,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,GAAG,UAAU,IAAI,cAAc,CAE/F;AAED,8GAA8G;AAC9G,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,SAAS;IASR,OAAO,CAAC,QAAQ,CAAC,GAAG;IARhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;gBAE1B,GAAG,GAAE,YAAsB;IAExD,8CAA8C;IAC9C,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAO9B,OAAO,CAAC,QAAQ;IAIhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAYpF,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8C;YAEzD,OAAO;IA0BrB;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI9C;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI;IAIjD,8FAA8F;IAC9F,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;CAQtC"}
@@ -41,6 +41,20 @@ export function detectRefusal(text) {
41
41
  function observedAt(event) {
42
42
  return new Date(typeof event.ts === 'number' ? event.ts : Date.now()).toISOString();
43
43
  }
44
+ /**
45
+ * The answer set an `awaitingHuman` frame offered, kept only when well-formed (issue #969):
46
+ * `choices` must be a non-empty array of strings, and `recommended` an integer index inside it.
47
+ * A malformed value is dropped rather than served — a skin would render it as buttons. Spread the
48
+ * result, so a gate the engine offered nothing on stays byte-identical on the wire.
49
+ */
50
+ export function offeredChoices(event) {
51
+ const choices = event['choices'];
52
+ if (!Array.isArray(choices) || choices.length === 0 || !choices.every((c) => typeof c === 'string'))
53
+ return {};
54
+ const recommended = event['recommended'];
55
+ const inBounds = Number.isInteger(recommended) && recommended >= 0 && recommended < choices.length;
56
+ return { choices: [...choices], ...(inBounds ? { recommended: recommended } : {}) };
57
+ }
44
58
  /**
45
59
  * The one rule that turns an event into a cache mutation.
46
60
  *
@@ -49,6 +63,8 @@ function observedAt(event) {
49
63
  * free to drift, and the drift would only ever show up after a restart — the exact condition nobody
50
64
  * exercises.
51
65
  */
66
+ /** The event types `fold` acts on — the ones that open or close a run's gate. */
67
+ const GATE_EVENTS = new Set(['awaitingHuman', 'resumed', 'sessionCompleted', 'runCancelled', 'sessionFailed']);
52
68
  function fold(entries, event) {
53
69
  const session = typeof event.session === 'string' ? event.session : undefined;
54
70
  if (session === undefined)
@@ -66,6 +82,7 @@ function fold(entries, event) {
66
82
  // Spread only when matched, so a normal gate's entry is byte-identical (no `refusal` key).
67
83
  ...(refusal !== undefined ? { refusal } : {}),
68
84
  ...(gateKind !== undefined ? { gateKind } : {}),
85
+ ...offeredChoices(event),
69
86
  });
70
87
  }
71
88
  break;
@@ -79,6 +96,24 @@ function fold(entries, event) {
79
96
  break;
80
97
  }
81
98
  }
99
+ /**
100
+ * The open gate a run's event history records, or `undefined` — the last `awaitingHuman` not
101
+ * followed by a resumed/terminal event. `events` is the run's log oldest-first. Pure: the same
102
+ * `fold` the live stream uses, over a scratch map, so a replay cannot disagree with what the live
103
+ * stream would have produced from the same events (FINDING-051).
104
+ */
105
+ export function replayOpenGate(runId, events) {
106
+ const replayed = new Map();
107
+ for (const event of events)
108
+ fold(replayed, event);
109
+ return replayed.get(runId);
110
+ }
111
+ /** Narrows a resolution to an open gate: `null`, `'no-log'`, `'ambiguous'` and `'unstable'` are not one. */
112
+ export function isOpenGate(resolution) {
113
+ return typeof resolution === 'object' && resolution !== null;
114
+ }
115
+ /** How many times `resolve` runs a read that a live gate event overlapped before it stops trying. */
116
+ const RESOLVE_ATTEMPTS = 3;
82
117
  /**
83
118
  * Gate-prompt cache over a durable log (DES-STUDIO-001 §3.3).
84
119
  *
@@ -90,8 +125,9 @@ function fold(entries, event) {
90
125
  * prune-only, so a daemon restart dropped the map and **nothing could put an entry back**. A parked
91
126
  * run then kept holding the operator without being able to say what it was asking. The prompt was
92
127
  * never actually lost — core records `awaitingHuman` to the event log (core#139) — it was simply
93
- * never read. `rebuild` is that read, which demotes this map to what it was always meant to be: a
94
- * latency optimisation, not the system of record.
128
+ * never read. `resolve`, fed the route's read of the durable row and the log, is that read, which
129
+ * demotes this map to what it was always meant to be: a latency optimisation, not the system of
130
+ * record.
95
131
  *
96
132
  * NOTE: the full HITL lifecycle from §3.3 (advisory / hard-expiry timers, append-only message log,
97
133
  * auto-cancel-on-expiry, synthesized `gateLifecycle` frames) is a deliberate follow-on. This
@@ -99,42 +135,117 @@ function fold(entries, event) {
99
135
  * always `open`.
100
136
  */
101
137
  export class GateCache {
138
+ log;
102
139
  entries = new Map();
140
+ /**
141
+ * Per-run count of the LIVE gate-opening and gate-closing events `ingest` has folded. `resolve`
142
+ * compares it across an async read of the record: a read some event overlapped is run again,
143
+ * never adopted. Never pruned — one number per run seen.
144
+ */
145
+ revisions = new Map();
146
+ constructor(log = console) {
147
+ this.log = log;
148
+ }
103
149
  /** Fold one live CoreEvent into the cache. */
104
150
  ingest(event) {
151
+ if (typeof event.session === 'string' && GATE_EVENTS.has(event.type)) {
152
+ this.revisions.set(event.session, this.revision(event.session) + 1);
153
+ }
105
154
  fold(this.entries, event);
106
155
  }
156
+ revision(runId) {
157
+ return this.revisions.get(runId) ?? 0;
158
+ }
107
159
  /**
108
- * Rebuild one run's gate from its durable event history and adopt the result (FINDING-051).
160
+ * The run's open gate: the cached entry, else what `read` finds in the record — the durable row,
161
+ * the log — adopted and returned once a read completes with no live gate event of the run's
162
+ * having landed meanwhile. `read` must not consult this cache: it is run again when one does.
109
163
  *
110
- * `events` is the run's log oldest-first. Folding all of it yields the last `awaitingHuman` not
111
- * followed by a resumed/terminal event — i.e. an open gate, or nothing.
164
+ * Neither side of such an overlap is known to be the newer (crew#1083 review). The record may
165
+ * have moved on since the read began — a live `awaitingHuman` for the next gate while an older
166
+ * row was being read — or the engine's pump may have delivered an OLDER frame late: an
167
+ * `awaitingHuman` for a gate the row already shows answered, or that gate's `resumed`. The
168
+ * revision counts arrivals, not order, so the cache cannot pick a winner; the first cut let the
169
+ * live side win and served a late ord-3 frame over a current ord-4 row, which let a decision
170
+ * naming ord 3 pass the gate check and answer ord 4. So no winner is picked. The read is run
171
+ * again, from scratch: the engine writes the row with the pause, so a re-read begun after the
172
+ * overlapping frame arrived reflects whatever that frame announced, and the first read that
173
+ * completes with nothing having landed is adopted.
112
174
  *
113
- * Deliberately does NOT cross-check the run's live status first: that costs a `sessionsDetail()`
114
- * on a read path, and the write side is already guarded — approving a run that has moved on is
115
- * refused by the `status === 'awaiting_human'` check on `POST /runs/:id/gate`. So the worst a
116
- * stale replay can do is show a prompt that then declines to be approved, and `reconcile` drops
117
- * it on the next list. The failure mode is a confusing read, never a bad write.
175
+ * A clean read is the record's word in BOTH directions (crew#1083 round 3): a gate it finds
176
+ * replaces whatever an overlapped `ingest` folded meanwhile, and "no open gate" / "cannot read"
177
+ * EVICTS it — otherwise a late frame folded during attempt one outlived the clean attempt two
178
+ * that found its gate already answered, and was served as a cache hit, unread, from then on. A
179
+ * read that throws evicts the same way. After `RESOLVE_ATTEMPTS` overlapped reads nothing was
180
+ * verified, so nothing is served: the entry is evicted and the result is `'unstable'`, never the
181
+ * unverified entry — an ordinary ordered backlog (`awaitingHuman(2)`, `resumed(2)`,
182
+ * `awaitingHuman(3)`, one landing per read) left the cache holding ord 3 while every read found
183
+ * ord 4. The run is logged once.
184
+ *
185
+ * One read in flight per run (agy, round 4): while a read is running, an entry a live frame
186
+ * folds is unverified, so a concurrent `resolve` awaits the read in flight instead of taking the
187
+ * entry as a hit, and the entry is evicted before a re-read so a `get` meanwhile serves nothing
188
+ * unverified either.
118
189
  */
119
- rebuild(runId, events) {
120
- const replayed = new Map();
121
- for (const event of events)
122
- fold(replayed, event);
123
- const entry = replayed.get(runId);
124
- if (entry)
125
- this.entries.set(runId, entry);
126
- return entry;
190
+ async resolve(runId, read) {
191
+ const pending = this.inflight.get(runId);
192
+ if (pending !== undefined)
193
+ return pending;
194
+ const cached = this.entries.get(runId);
195
+ if (cached)
196
+ return cached;
197
+ const resolution = this.hydrate(runId, read).finally(() => {
198
+ this.inflight.delete(runId);
199
+ });
200
+ this.inflight.set(runId, resolution);
201
+ return resolution;
202
+ }
203
+ /** In-flight resolutions per run: `resolve` hands every concurrent caller the same one. */
204
+ inflight = new Map();
205
+ async hydrate(runId, read) {
206
+ for (let attempt = 1; attempt <= RESOLVE_ATTEMPTS; attempt++) {
207
+ const seen = this.revision(runId);
208
+ let found;
209
+ try {
210
+ found = await read();
211
+ }
212
+ catch (err) {
213
+ this.entries.delete(runId);
214
+ throw err;
215
+ }
216
+ if (this.revision(runId) !== seen) {
217
+ // Whatever a live frame folded during this read is unverified until the re-read settles.
218
+ this.entries.delete(runId);
219
+ continue;
220
+ }
221
+ if (isOpenGate(found))
222
+ this.entries.set(runId, found);
223
+ else
224
+ this.entries.delete(runId);
225
+ return found;
226
+ }
227
+ this.entries.delete(runId);
228
+ this.log.warn(`gate cache: live gate events for run ${runId} overlapped ${RESOLVE_ATTEMPTS} reads of its open gate; nothing verified, serving no gate`);
229
+ return 'unstable';
127
230
  }
128
- /** The cached open gate for a run, if any. */
231
+ /**
232
+ * The cached open gate for a run, if any — and nothing while a read of it is in flight: an entry
233
+ * a live frame folded meanwhile is unverified until that read settles, so a sync caller (the GET
234
+ * route's hit path, a steering prompt) falls through to `resolve` and waits (agy, round 4).
235
+ */
129
236
  get(runId) {
130
- return this.entries.get(runId);
237
+ return this.inflight.has(runId) ? undefined : this.entries.get(runId);
238
+ }
239
+ /**
240
+ * Drop the run's cached gate: the engine's record shows another gate open, or none (crew#1083
241
+ * round 7). The next read hydrates from the record.
242
+ */
243
+ forget(runId) {
244
+ this.entries.delete(runId);
131
245
  }
132
246
  /**
133
- * Adopt an entry hydrated from the DURABLE `interaction_requests` table (DES-PROJECT-001
134
- * §5.3). With that table, this cache is finally what its own comments always claimed — a
135
- * latency layer over durable truth: the route reads the table on a miss and parks the result
136
- * here so the next poll is a map hit. `rebuild` (event-log replay) remains the fallback for
137
- * engines predating the table.
247
+ * Seed the run's gate synchronously. Nothing can land between the caller's decision and this
248
+ * set, so it needs no guard; a read that awaited anything goes through `resolve`.
138
249
  */
139
250
  adopt(runId, entry) {
140
251
  this.entries.set(runId, entry);
@@ -1 +1 @@
1
- {"version":3,"file":"gate-cache.js","sourceRoot":"","sources":["../../src/api/gate-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate-cache.js","sourceRoot":"","sources":["../../src/api/gate-cache.ts"],"names":[],"mappings":"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"}
@@ -300,10 +300,11 @@ export declare function readStoryline(evidenceRoot: string, planStepId: string):
300
300
  } | null;
301
301
  } | null>;
302
302
  export interface WalkthroughRouteDeps {
303
- /** The run's open gate (the routes' shared resolution); `'no-log'` = this build cannot say. */
303
+ /** The run's open gate (the routes' shared resolution); `'no-log'` = this build cannot say;
304
+ * `'unstable'` = live gate events kept landing while it was read (retry). */
304
305
  resolveOpenGate?: (runId: string) => Promise<{
305
306
  ord: number;
306
- } | null | 'no-log'>;
307
+ } | null | 'no-log' | 'unstable' | 'ambiguous'>;
307
308
  audit?: AuditLog;
308
309
  }
309
310
  export declare function registerWalkthroughRoutes(app: FastifyInstance, adapter: CoreAdapter, deps?: WalkthroughRouteDeps): void;
@@ -1 +1 @@
1
- {"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../../src/api/recording.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../../src/api/recording.ts"],"names":[],"mappings":"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"}
@@ -1384,6 +1384,10 @@ export function registerWalkthroughRoutes(app, adapter, deps = {}) {
1384
1384
  const open = await deps.resolveOpenGate(id);
1385
1385
  if (open === 'no-log')
1386
1386
  return reply.code(503).send({ error: 'gate history is unavailable: this wicked-core build has no event-log read binding' });
1387
+ if (open === 'unstable')
1388
+ return reply.code(503).send({ error: 'the open gate could not be read: live gate events kept landing — retry', code: 'gate_unstable' });
1389
+ if (open === 'ambiguous')
1390
+ return reply.code(409).send({ error: 'the engine\'s record shows gates open before more than one unit on this run, so the escalation cannot be identified', code: 'gate_unknown' });
1387
1391
  if (open === null)
1388
1392
  return noEscalation('no gate is open');
1389
1393
  const isEscalated = (u) => u !== null && u.ord === open.ord && u.denial_reason !== null;
@@ -1409,7 +1413,15 @@ export function registerWalkthroughRoutes(app, adapter, deps = {}) {
1409
1413
  // The escalation could have been answered while the file was written: say so, rather than a
1410
1414
  // 200 that implies the next recording uses it (codex on WT-W3).
1411
1415
  const after = await deps.resolveOpenGate(id);
1412
- const still = (await runById(id))?.session.status === 'awaiting_human' && after !== null && after !== 'no-log' && after.ord === open.ord;
1416
+ if (after === 'unstable') {
1417
+ // Written, but not verifiable right now: live gate events kept landing while the gate was
1418
+ // re-read (crew#1083 round 4). Not "answered meanwhile" — that would be a guess.
1419
+ return reply.code(503).send({
1420
+ error: 'the storyline was written, but the open gate could not be re-read: live gate events kept landing — open the walkthrough again before relying on it',
1421
+ code: 'gate_unstable',
1422
+ });
1423
+ }
1424
+ const still = (await runById(id))?.session.status === 'awaiting_human' && after !== null && after !== 'no-log' && after !== 'ambiguous' && after.ord === open.ord;
1413
1425
  if (!still) {
1414
1426
  return reply.code(409).send({
1415
1427
  error: 'the storyline was written, but the escalation was answered meanwhile; open the walkthrough again before relying on it',