wicked-crew 0.7.32 → 0.7.34

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 (112) hide show
  1. package/README.md +2 -1
  2. package/dist/api/chat-turns.d.ts +87 -0
  3. package/dist/api/chat-turns.d.ts.map +1 -0
  4. package/dist/api/chat-turns.js +147 -0
  5. package/dist/api/chat-turns.js.map +1 -0
  6. package/dist/api/eval-sample.d.ts +18 -18
  7. package/dist/api/governance-steering.d.ts +4 -4
  8. package/dist/api/roster-standing.d.ts +42 -0
  9. package/dist/api/roster-standing.d.ts.map +1 -0
  10. package/dist/api/roster-standing.js +54 -0
  11. package/dist/api/roster-standing.js.map +1 -0
  12. package/dist/api/routes.d.ts +61 -3
  13. package/dist/api/routes.d.ts.map +1 -1
  14. package/dist/api/routes.js +171 -42
  15. package/dist/api/routes.js.map +1 -1
  16. package/dist/api/server.d.ts.map +1 -1
  17. package/dist/api/server.js +89 -1
  18. package/dist/api/server.js.map +1 -1
  19. package/dist/api/skills.d.ts.map +1 -1
  20. package/dist/api/skills.js +12 -3
  21. package/dist/api/skills.js.map +1 -1
  22. package/dist/api/testing.d.ts +4 -4
  23. package/dist/campaigns/routes.d.ts +14 -14
  24. package/dist/cli/index.js +23 -3
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/core/adapter.d.ts +30 -0
  27. package/dist/core/adapter.d.ts.map +1 -1
  28. package/dist/core/adapter.js +66 -2
  29. package/dist/core/adapter.js.map +1 -1
  30. package/dist/core/engine-roster.d.ts +28 -0
  31. package/dist/core/engine-roster.d.ts.map +1 -1
  32. package/dist/core/engine-roster.js +28 -0
  33. package/dist/core/engine-roster.js.map +1 -1
  34. package/dist/core/types.d.ts +9 -0
  35. package/dist/core/types.d.ts.map +1 -1
  36. package/dist/core/types.js +6 -0
  37. package/dist/core/types.js.map +1 -1
  38. package/dist/interactive/bridge-pool.d.ts +2 -2
  39. package/dist/interactive/bridge-pool.d.ts.map +1 -1
  40. package/dist/interactive/bridge-pool.js +19 -4
  41. package/dist/interactive/bridge-pool.js.map +1 -1
  42. package/dist/interactive/bus-subscriber-errors.d.ts +61 -0
  43. package/dist/interactive/bus-subscriber-errors.d.ts.map +1 -0
  44. package/dist/interactive/bus-subscriber-errors.js +85 -0
  45. package/dist/interactive/bus-subscriber-errors.js.map +1 -0
  46. package/dist/interactive/chat-events.d.ts +16 -0
  47. package/dist/interactive/chat-events.d.ts.map +1 -1
  48. package/dist/interactive/chat-events.js +59 -10
  49. package/dist/interactive/chat-events.js.map +1 -1
  50. package/dist/interactive/demo-events.d.ts +66 -0
  51. package/dist/interactive/demo-events.d.ts.map +1 -1
  52. package/dist/interactive/demo-events.js +154 -10
  53. package/dist/interactive/demo-events.js.map +1 -1
  54. package/dist/interactive/doc-delete-routes.js +2 -2
  55. package/dist/interactive/doc-delete-routes.js.map +1 -1
  56. package/dist/interactive/draft-events.d.ts +13 -0
  57. package/dist/interactive/draft-events.d.ts.map +1 -1
  58. package/dist/interactive/draft-events.js +34 -9
  59. package/dist/interactive/draft-events.js.map +1 -1
  60. package/dist/interactive/draft-skill.d.ts +68 -0
  61. package/dist/interactive/draft-skill.d.ts.map +1 -0
  62. package/dist/interactive/draft-skill.js +127 -0
  63. package/dist/interactive/draft-skill.js.map +1 -0
  64. package/dist/interactive/edit-events.d.ts +13 -0
  65. package/dist/interactive/edit-events.d.ts.map +1 -1
  66. package/dist/interactive/edit-events.js +35 -9
  67. package/dist/interactive/edit-events.js.map +1 -1
  68. package/dist/interactive/proxy-routes.d.ts +36 -0
  69. package/dist/interactive/proxy-routes.d.ts.map +1 -1
  70. package/dist/interactive/proxy-routes.js +90 -3
  71. package/dist/interactive/proxy-routes.js.map +1 -1
  72. package/dist/interactive/ws-relay.d.ts +2 -0
  73. package/dist/interactive/ws-relay.d.ts.map +1 -1
  74. package/dist/interactive/ws-relay.js +7 -3
  75. package/dist/interactive/ws-relay.js.map +1 -1
  76. package/dist/projects/activity.d.ts +17 -3
  77. package/dist/projects/activity.d.ts.map +1 -1
  78. package/dist/projects/activity.js +34 -13
  79. package/dist/projects/activity.js.map +1 -1
  80. package/dist/projects/events.d.ts +2 -0
  81. package/dist/projects/events.d.ts.map +1 -1
  82. package/dist/projects/events.js +7 -3
  83. package/dist/projects/events.js.map +1 -1
  84. package/dist/projects/state-home-preflight.d.ts +163 -0
  85. package/dist/projects/state-home-preflight.d.ts.map +1 -0
  86. package/dist/projects/state-home-preflight.js +310 -0
  87. package/dist/projects/state-home-preflight.js.map +1 -0
  88. package/dist/projects/state-home-registry.d.ts +25 -0
  89. package/dist/projects/state-home-registry.d.ts.map +1 -0
  90. package/dist/projects/state-home-registry.js +50 -0
  91. package/dist/projects/state-home-registry.js.map +1 -0
  92. package/dist/qe/gate-events.d.ts +2 -0
  93. package/dist/qe/gate-events.d.ts.map +1 -1
  94. package/dist/qe/gate-events.js +7 -3
  95. package/dist/qe/gate-events.js.map +1 -1
  96. package/dist/skills/base-skill.d.ts +92 -0
  97. package/dist/skills/base-skill.d.ts.map +1 -0
  98. package/dist/skills/base-skill.js +105 -0
  99. package/dist/skills/base-skill.js.map +1 -0
  100. package/dist/skills/runtime.d.ts +43 -1
  101. package/dist/skills/runtime.d.ts.map +1 -1
  102. package/dist/skills/runtime.js +110 -5
  103. package/dist/skills/runtime.js.map +1 -1
  104. package/dist/skills/store.d.ts +11 -0
  105. package/dist/skills/store.d.ts.map +1 -1
  106. package/dist/skills/store.js +16 -0
  107. package/dist/skills/store.js.map +1 -1
  108. package/dist/studio/assets/{index-Clm0MXDF.js → index-DhojBZ1T.js} +91 -91
  109. package/dist/studio/index.html +1 -1
  110. package/dist/studio/testid-inventory.json +51 -3
  111. package/endpoint-manifest.json +2 -1
  112. package/package.json +4 -4
@@ -7,11 +7,14 @@ import type { ElicitationCache } from './elicitation-cache.js';
7
7
  import { QeGateCache } from '../qe/gate-events.js';
8
8
  import type { ConformanceRule, SteeringType } from '../core/types.js';
9
9
  import { SeatHealthTracker } from './seat-health.js';
10
+ import { type RosterWithStanding } from './roster-standing.js';
11
+ import { ChatTurnIndex } from './chat-turns.js';
10
12
  import { ChatScopeIndex } from './chat-scope.js';
11
13
  import { DocRunIndex } from '../interactive/doc-run-index.js';
12
14
  import { TestSetIndex } from '../qe/test-sets.js';
13
15
  import { type ProjectRoutesDeps } from '../projects/routes.js';
14
16
  import { type SkillsRuntime } from '../skills/runtime.js';
17
+ import { type StateHomeWatch } from '../projects/state-home-preflight.js';
15
18
  import type { EvalRunStore } from './eval-store.js';
16
19
  import { InteractiveBridgePool } from '../interactive/bridge-pool.js';
17
20
  import type { DocGroundingStore } from '../interactive/doc-grounding.js';
@@ -82,7 +85,7 @@ export declare function policyProposalToRule(proposalId: string, kindType: strin
82
85
  * schema does not accept — so `.strict()` is what turns that trip into a 400 instead of an
83
86
  * unworkflowed run reported as `201`.
84
87
  */
85
- export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
88
+ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodObject<{
86
89
  problem: z.ZodString;
87
90
  sessionId: z.ZodOptional<z.ZodString>;
88
91
  clisJson: z.ZodOptional<z.ZodString>;
@@ -101,6 +104,12 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
101
104
  * defaults to `"pr"` (flippable via the `deliverDefault` setting), everything else to
102
105
  * `"none"` — see the resolution below. */
103
106
  deliver: z.ZodOptional<z.ZodEnum<["pr", "none"]>>;
107
+ /** F-E2E-030 — who confirms the deliver phase. `'human'` (the default when omitted): the engine
108
+ * pauses before the composed `deliver` Tool unit pushes and opens the PR, whatever
109
+ * `humanConfirm` says. `'auto'`: the caller's EXPLICIT opt-out — the push and PR follow verify
110
+ * unattended, under the daemon's active gh account; a UI sending it must name the posture
111
+ * "auto-deliver" to its operator. Additive; an older daemon's schema rejects it with a 400. */
112
+ deliverGate: z.ZodOptional<z.ZodEnum<["human", "auto"]>>;
104
113
  /** DES-UX-001 §8.3 (CREW-UX-3) — the run this launch retries. Must name an EXISTING run id
105
114
  * (the route checks the store and 400s with a named error otherwise); persisted via the
106
115
  * `run.launched` audit entry + retry index and echoed as `AgentSession.retry_of`. */
@@ -128,6 +137,7 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
128
137
  retryOf?: string | undefined;
129
138
  entityMode?: "shared" | "isolated" | undefined;
130
139
  humanConfirm?: string | undefined;
140
+ deliverGate?: "auto" | "human" | undefined;
131
141
  }, {
132
142
  problem: string;
133
143
  workflow?: string | undefined;
@@ -141,6 +151,7 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
141
151
  retryOf?: string | undefined;
142
152
  entityMode?: "shared" | "isolated" | undefined;
143
153
  humanConfirm?: string | undefined;
154
+ deliverGate?: "auto" | "human" | undefined;
144
155
  }>, {
145
156
  problem: string;
146
157
  workflow?: string | undefined;
@@ -154,6 +165,7 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
154
165
  retryOf?: string | undefined;
155
166
  entityMode?: "shared" | "isolated" | undefined;
156
167
  humanConfirm?: string | undefined;
168
+ deliverGate?: "auto" | "human" | undefined;
157
169
  }, {
158
170
  problem: string;
159
171
  workflow?: string | undefined;
@@ -167,6 +179,7 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
167
179
  retryOf?: string | undefined;
168
180
  entityMode?: "shared" | "isolated" | undefined;
169
181
  humanConfirm?: string | undefined;
182
+ deliverGate?: "auto" | "human" | undefined;
170
183
  }>, {
171
184
  problem: string;
172
185
  workflow?: string | undefined;
@@ -180,6 +193,7 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
180
193
  retryOf?: string | undefined;
181
194
  entityMode?: "shared" | "isolated" | undefined;
182
195
  humanConfirm?: string | undefined;
196
+ deliverGate?: "auto" | "human" | undefined;
183
197
  }, {
184
198
  problem: string;
185
199
  workflow?: string | undefined;
@@ -193,6 +207,35 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
193
207
  retryOf?: string | undefined;
194
208
  entityMode?: "shared" | "isolated" | undefined;
195
209
  humanConfirm?: string | undefined;
210
+ deliverGate?: "auto" | "human" | undefined;
211
+ }>, {
212
+ problem: string;
213
+ workflow?: string | undefined;
214
+ deliver?: "pr" | "none" | undefined;
215
+ projectId?: string | undefined;
216
+ repoRef?: string | undefined;
217
+ campaignId?: string | undefined;
218
+ groupLabel?: string | undefined;
219
+ clisJson?: string | undefined;
220
+ sessionId?: string | undefined;
221
+ retryOf?: string | undefined;
222
+ entityMode?: "shared" | "isolated" | undefined;
223
+ humanConfirm?: string | undefined;
224
+ deliverGate?: "auto" | "human" | undefined;
225
+ }, {
226
+ problem: string;
227
+ workflow?: string | undefined;
228
+ deliver?: "pr" | "none" | undefined;
229
+ projectId?: string | undefined;
230
+ repoRef?: string | undefined;
231
+ campaignId?: string | undefined;
232
+ groupLabel?: string | undefined;
233
+ clisJson?: string | undefined;
234
+ sessionId?: string | undefined;
235
+ retryOf?: string | undefined;
236
+ entityMode?: "shared" | "isolated" | undefined;
237
+ humanConfirm?: string | undefined;
238
+ deliverGate?: "auto" | "human" | undefined;
196
239
  }>;
197
240
  export declare const GateSchema: z.ZodObject<{
198
241
  approve: z.ZodBoolean;
@@ -285,6 +328,10 @@ export interface RuntimeDeps {
285
328
  * `chatClosed` frames (so a reclaimed chat's scratch root goes with it); a directly-driven
286
329
  * route set gets a fresh one. */
287
330
  chatScopes?: ChatScopeIndex;
331
+ /** Per-chat turn index (F-RECON-017) — `createServer` feeds one from the CoreEvent fold so
332
+ * `POST /chats/:id/messages` refuses a send to a seat still mid-turn; a directly-driven route
333
+ * set gets a fresh one (tests fold frames into it themselves). */
334
+ chatTurns?: ChatTurnIndex;
288
335
  /** Run→delivered-PR index (CREW-UX-8, crew#321) — `createServer` hydrates one from the audit
289
336
  * trail so a restarted daemon still echoes `delivery`; a directly-driven route set gets a
290
337
  * fresh one. */
@@ -326,6 +373,11 @@ export interface RuntimeDeps {
326
373
  /** Seat sign-in presence probe (seat sign-in) — injectable so route tests never read the
327
374
  * developer's real dotfiles. Defaults to the file/env heuristic in seat-signin.ts. */
328
375
  signedIn?: (seatKey: string, workerConfigRoot?: string) => boolean | null;
376
+ /** The roster WITH crew's standing (`api/roster-standing.ts`) — `createServer` injects its single
377
+ * instance so the routes, the interactive seams and the adapter's own launches all bench the
378
+ * same seats (F-RECON-002/003); a directly-driven route set builds one over `seatHealth` +
379
+ * `signedIn` above. */
380
+ rosterWithStanding?: RosterWithStanding;
329
381
  /** The `/ws` fan-out, for the few routes that say something to a thread themselves (a refused
330
382
  * chat seat, F-2R2-007). Absent (unit tests, library use) = nothing is broadcast. */
331
383
  broadcast?: (frame: CoreEvent) => void;
@@ -363,6 +415,12 @@ export interface RuntimeDeps {
363
415
  * (seeded from the installed plugin, published); a directly-driven route set gets none and
364
416
  * `/skills*` answers 503 unless a test injects one over a fixture root. */
365
417
  skills?: SkillsRuntime;
418
+ /** The live state-home classification (wicked-core#411 / crew#497) — `createServer` surveys the
419
+ * daemon state home at boot and hands the watch here; the routes re-survey on demand, report it
420
+ * on `/diagnostics.stateHome` + `/health.warnings`, and answer `POST /runs` 409 while a handed
421
+ * snapshot derives a state home with an entry the fence cannot classify. A directly-driven route
422
+ * set (tests) gets none: no survey, no warning, no gate — never a fabricated clean answer. */
423
+ stateHome?: StateHomeWatch;
366
424
  /** The document ↔ run binding (wave 6, F-4R2-006) read off the interactive seams' handoff ledgers —
367
425
  * `createServer` builds one over the four ledger sources; a directly-driven route set gets an
368
426
  * EMPTY one (`document_id: null` on every run). */
@@ -392,16 +450,16 @@ export declare const ChatOpenSchema: z.ZodObject<{
392
450
  projectId: z.ZodOptional<z.ZodString>;
393
451
  }, "strict", z.ZodTypeAny, {
394
452
  projectId?: string | undefined;
453
+ chatId?: string | undefined;
395
454
  clis?: string[] | undefined;
396
455
  repoRef?: string | undefined;
397
456
  repoRefs?: string[] | undefined;
398
- chatId?: string | undefined;
399
457
  }, {
400
458
  projectId?: string | undefined;
459
+ chatId?: string | undefined;
401
460
  clis?: string[] | undefined;
402
461
  repoRef?: string | undefined;
403
462
  repoRefs?: string[] | undefined;
404
- chatId?: string | undefined;
405
463
  }>;
406
464
  export declare function registerRoutes(app: FastifyInstance, adapter: CoreAdapter, gateCache: GateCache, elicitationCache: ElicitationCache, qeGateEvents?: QeGateCache, projects?: ProjectRoutesDeps, security?: SecurityDeps, runtime?: RuntimeDeps): void;
407
465
  //# sourceMappingURL=routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,9 @@ import { execCapped, ExecOutputTooLarge } from '../core/exec.js';
17
17
  import { callEstateTool, EstateMcpError } from '../core/estate-mcp-client.js';
18
18
  import { SeatHealthTracker } from './seat-health.js';
19
19
  import { applyWorkerConfigRoot, signedInHeuristic } from './seat-signin.js';
20
- import { chatSeatAdmission, seatStanding } from './seat-standing.js';
20
+ import { chatSeatAdmission } from './seat-standing.js';
21
+ import { rosterWithStandingFactory } from './roster-standing.js';
22
+ import { ChatTurnIndex } from './chat-turns.js';
21
23
  import { ChatScopeIndex, chatScopeDeps, prepareChatScratch, removeChatScratch, resolveChatScope, } from './chat-scope.js';
22
24
  import { allowedRootsFor, isInsideRoot, openWithSystemDefault } from './open-path.js';
23
25
  import { InvalidDiffBaseError, MERGE_BASE_LITERAL, NotARegularFileError, UnresolvableDiffBaseError, branchDiff, isPlainRef, readFileCapped, worktreeDiff, } from './run-files.js';
@@ -33,6 +35,9 @@ import { isSteeringAuthorRun, landSteeringProposal } from './steering-landing.js
33
35
  import { registerTestingRoutes } from './testing.js';
34
36
  import { registerSkillsRoutes } from './skills.js';
35
37
  import { disabledSkillsHealth } from '../skills/runtime.js';
38
+ import { STATE_HOME_BLOCKER_CODE, STATE_HOME_REMEDY, isEngineStateHomeRefusal, stateHomeBlockerBody, } from '../projects/state-home-preflight.js';
39
+ import { BASE_SKILL_POLICIES, BASE_SKILL_REF_SHAPE } from '../skills/base-skill.js';
40
+ import { noEligibleSeatBody, parseNoEligibleSeat } from '../core/engine-roster.js';
36
41
  import { ProjectSettingsStore } from '../projects/settings.js';
37
42
  import { boundOrigin, InteractiveBridgePool } from '../interactive/bridge-pool.js';
38
43
  import { composeDeliverText, factsFromRun, framedDeliverText, runUrlFor } from '../core/deliver-text.js';
@@ -339,6 +344,12 @@ export const LaunchSchema = z.object({
339
344
  * defaults to `"pr"` (flippable via the `deliverDefault` setting), everything else to
340
345
  * `"none"` — see the resolution below. */
341
346
  deliver: z.enum(['pr', 'none']).optional(),
347
+ /** F-E2E-030 — who confirms the deliver phase. `'human'` (the default when omitted): the engine
348
+ * pauses before the composed `deliver` Tool unit pushes and opens the PR, whatever
349
+ * `humanConfirm` says. `'auto'`: the caller's EXPLICIT opt-out — the push and PR follow verify
350
+ * unattended, under the daemon's active gh account; a UI sending it must name the posture
351
+ * "auto-deliver" to its operator. Additive; an older daemon's schema rejects it with a 400. */
352
+ deliverGate: z.enum(['human', 'auto']).optional(),
342
353
  /** DES-UX-001 §8.3 (CREW-UX-3) — the run this launch retries. Must name an EXISTING run id
343
354
  * (the route checks the store and 400s with a named error otherwise); persisted via the
344
355
  * `run.launched` audit entry + retry index and echoed as `AgentSession.retry_of`. */
@@ -356,6 +367,12 @@ export const LaunchSchema = z.object({
356
367
  }).strict().refine((b) => b.deliver !== 'pr' || b.workflow !== undefined, {
357
368
  message: 'deliver: "pr" requires a workflow — a free-text run has no def to append the deliver phase to',
358
369
  path: ['deliver'],
370
+ }).refine((b) => b.deliverGate === undefined || b.deliver !== 'none', {
371
+ // F-E2E-030: a deliver-gate posture on a launch that declines delivery is a contradiction the
372
+ // caller should hear about, not a silent no-op. (A launch that OMITS `deliver` may still
373
+ // resolve to no deliver unit — repo-less, free-text, read-only def; the field is then inert.)
374
+ message: 'deliverGate has no meaning with deliver: "none" — there is no deliver phase to gate',
375
+ path: ['deliverGate'],
359
376
  }).refine((b) => b.campaignId === undefined || b.groupLabel === undefined, {
360
377
  message: 'campaignId and groupLabel are mutually exclusive — a run files onto ONE grouping surface (an existing campaign, or a label group)',
361
378
  path: ['groupLabel'],
@@ -471,6 +488,7 @@ runtime = {}) {
471
488
  const runTimingIndex = runtime.runTimingIndex ?? new RunTimingIndex();
472
489
  const guidanceIndex = runtime.guidanceIndex ?? new GuidanceIndex();
473
490
  const chatScopes = runtime.chatScopes ?? new ChatScopeIndex();
491
+ const chatTurns = runtime.chatTurns ?? new ChatTurnIndex();
474
492
  const deliveryIndex = runtime.deliveryIndex ?? new DeliveryIndex();
475
493
  // Wave 6: the doc↔run binding and the registered test sets — `createServer` injects the real
476
494
  // ones; a directly-driven route set gets an empty index (every run `document_id: null`, no sets).
@@ -611,7 +629,32 @@ runtime = {}) {
611
629
  // null / [] for the rest ("where declared", never invented). See src/api/endpoint-manifest.ts.
612
630
  app.get(`${V}/health`, { config: { manifest: { statusCodes: [200] } } }, async () => {
613
631
  const ping = await adapter.ping();
614
- return { status: 'ok', version: PKG_VERSION, ping };
632
+ // F-E2E-030: what this deployment can keep. A composer reads `capabilities.deliverGate`
633
+ // before it promises "pauses at the deliver gate"; a stub-driven route set without the
634
+ // probe honestly reports no gate.
635
+ const capabilities = typeof adapter.engineCapabilities === 'function'
636
+ ? adapter.engineCapabilities()
637
+ : { deliverGate: false };
638
+ // wicked-core#411 / crew#497: the state-home blocker rides the health probe as a WARNING. The
639
+ // daemon still SERVES (status stays ok — studio must load and show the blocker) but refuses to
640
+ // launch while the state home holds an entry the worker Read fence cannot classify. Re-surveyed
641
+ // per probe (two readdirs) so an entry that appears after boot is reported without a restart;
642
+ // the field is ABSENT when there is nothing to say, and on a route set booted without the watch.
643
+ const stateHome = runtime.stateHome !== undefined ? await runtime.stateHome.refresh() : null;
644
+ const warnings = stateHome === null
645
+ ? []
646
+ : stateHome.findings.map((f) => ({ kind: f.kind, severity: f.severity, message: f.message }));
647
+ // crew#554: the BASE skill posture for the next launch — the composer's confirm line
648
+ // ("discipline skill: <name> gen N" / "MISSING — …"). Cached by the skills runtime: no I/O.
649
+ const baseSkill = runtime.skills?.baseSkill() ?? null;
650
+ return {
651
+ status: 'ok',
652
+ version: PKG_VERSION,
653
+ ping,
654
+ capabilities,
655
+ baseSkill,
656
+ ...(warnings.length > 0 ? { warnings } : {}),
657
+ };
615
658
  });
616
659
  // The daemon's self-knowledge surface (diagnostics): what is deployed, what it stores, what
617
660
  // has gone wrong recently, and whether ACP is really working across the CLIs — the answer
@@ -659,6 +702,11 @@ runtime = {}) {
659
702
  // Is the governance evidence LANDING (crew#495): the store, the records on it, the dead
660
703
  // letters — with a `governance.deadletter` finding the moment the outbox holds one.
661
704
  governance,
705
+ // The state-home classification (wicked-core#411 / crew#497): which state home the worker
706
+ // Read fence classifies, every entry it cannot classify there (top level and skills root),
707
+ // who classified (the engine, or crew's registry copy on an older addon) and whether that
708
+ // refuses launches — re-surveyed per read. `null` on a route set booted without the watch.
709
+ stateHome: runtime.stateHome !== undefined ? await runtime.stateHome.refresh() : null,
662
710
  };
663
711
  });
664
712
  // Who am I talking to the daemon as? (task #88). In local mode this is
@@ -702,41 +750,14 @@ runtime = {}) {
702
750
  return { port, host };
703
751
  });
704
752
  // The council seats for the launch form (static production roster), each carrying its RUNTIME
705
- // health (crew#274). The roster is declarative — every configured seat is listed — and `health`
706
- // is what the platform has observed: default active with no message; inactive + the error
707
- // excerpt after a seat-level failure, until an ok output or the recovery probe flips it back.
708
- // Existing fields ride through verbatim (the seat still round-trips into `clisJson` on launch)
709
- // — the spread is deliberately NOT a field whitelist, which is what lets the engine's
710
- // `login_invocation` (seat sign-in, wicked-core PR#278) pass through untouched. Each seat also
711
- // gains `signed_in`: the cheap file/env presence heuristic, computed against the LIVE
712
- // `WICKED_WORKER_HOME` env — the same value the engine reads at the next worker spawn, kept
713
- // current by `applyWorkerConfigRoot` at boot and on every settings change.
714
- //
715
- // Each seat ALSO carries its STANDING (F-2R2-009): `auth` re-reads `signed_in` for what it means
716
- // (a `signed_out` codex is benched on its first ballot; a `not_required` opencode answers on its
717
- // free tier), and `council_eligible` says what a council would do with the seat as far as the
718
- // daemon can tell. `POST /chats` seats its defaults through the SAME standing, so the roster and
719
- // the chat never disagree about a seat.
720
- const rosterWithStanding = () => {
721
- const workerRoot = process.env['WICKED_WORKER_HOME'];
722
- return CoreAdapter.roster().map((seat) => {
723
- const key = String(seat.key);
724
- const health = seatHealth.healthFor(key);
725
- const signed = signedIn(key, workerRoot === '' ? undefined : workerRoot);
726
- return {
727
- ...seat,
728
- health,
729
- signed_in: signed,
730
- // The bench is THIS daemon's council evidence (councilSeatFailed, bounded window) — the
731
- // prediction learns from what the engine actually did with the seat (#533 review, F-1).
732
- ...seatStanding(seat, signed, health, seatHealth.councilBenchFor(key),
733
- // F-A45-006: the seat's OWN "no credential" report (a ballot's "No API key found", a
734
- // worker's 401, an auth ACP fallback) overrides the file probe — `auth` flips, not only
735
- // `council_eligible`, and the evidence rides on the wire.
736
- seatHealth.authFailureFor(key)),
737
- };
738
- });
739
- };
753
+ // health (crew#274) and its STANDING (F-2R2-009) — `api/roster-standing.ts` is the ONE accessor
754
+ // every launch path shares (F-RECON-002/003: the interactive seams, the adapter's onboarding
755
+ // launch and `wicked-crew start` used to hand the engine the RAW registry roster, so signed-out
756
+ // seats were convened and even elected). `createServer` injects the daemon's single instance
757
+ // (built over the same `seatHealth`); a directly-driven route set builds an equivalent one here.
758
+ // `POST /chats` seats its defaults through the SAME standing, so the roster and the chat never
759
+ // disagree about a seat.
760
+ const rosterWithStanding = runtime.rosterWithStanding ?? rosterWithStandingFactory({ seatHealth, signedIn });
740
761
  app.get(`${V}/roster`, async () => ({ roster: rosterWithStanding() }));
741
762
  // Open a file/folder with the OS default application (crew#273) — the studio Files tab's
742
763
  // click-to-open. The open MUST happen daemon-side (the SPA cannot spawn a process), which is
@@ -1031,10 +1052,13 @@ runtime = {}) {
1031
1052
  requestType: 'LaunchRunBody',
1032
1053
  responseType: '{ runId: string }',
1033
1054
  // 404/409: unknown project / unknown campaignId (wicked-studio#27) /
1034
- // archived-or-synthesized project + busy engine (see the catch below); 400: zod
1035
- // reject or a retryOf naming no existing run; 501: campaignId attach on an engine
1036
- // addon without the campaign bindings ("upgrade the engine").
1037
- statusCodes: [201, 400, 404, 409, 501],
1055
+ // archived-or-synthesized project + busy engine + the state-home blocker
1056
+ // (`state_home_unregistered`) + a roster with no eligible seat (`no_eligible_seat`,
1057
+ // wicked-core#461 — see the catch below); 400: zod reject or a retryOf naming no
1058
+ // existing run; 422: the base-skill intake refusal (`base_skill_refused`, crew#554);
1059
+ // 501: campaignId attach on an engine addon without the campaign bindings ("upgrade
1060
+ // the engine").
1061
+ statusCodes: [201, 400, 404, 409, 422, 501],
1038
1062
  },
1039
1063
  },
1040
1064
  }, async (req, reply) => {
@@ -1043,6 +1067,18 @@ runtime = {}) {
1043
1067
  return reply.code(400).send(invalidBody(parsed.error, 'Invalid request body'));
1044
1068
  }
1045
1069
  const b = parsed.data;
1070
+ // wicked-core#411 / crew#497: the state-home blocker, judged BEFORE anything is resolved or
1071
+ // committed. While the handed skills snapshot derives a state home with an entry the worker
1072
+ // Read fence cannot classify, the engine refuses this launch at intake (and an engine before
1073
+ // that fix refused it at the run's first worker, after councils and the intake gate) — so the
1074
+ // daemon answers the typed 409 itself, naming every entry and the remedy, until they are gone.
1075
+ // Re-surveyed per launch: the entries named are the ones there NOW.
1076
+ if (runtime.stateHome !== undefined) {
1077
+ const stateHome = await runtime.stateHome.refresh();
1078
+ if (stateHome.refusesLaunches) {
1079
+ return reply.code(409).send(stateHomeBlockerBody(stateHome));
1080
+ }
1081
+ }
1046
1082
  const input = {
1047
1083
  problem: b.problem,
1048
1084
  sessionId: b.sessionId ?? randomUUID(),
@@ -1055,6 +1091,11 @@ runtime = {}) {
1055
1091
  input.entityMode = b.entityMode;
1056
1092
  if (b.humanConfirm !== undefined)
1057
1093
  input.humanConfirm = b.humanConfirm;
1094
+ // F-E2E-030: only the explicit `'auto'` opts out of the engine's deliver gate. `'human'` and
1095
+ // an omitted field both leave `autoDeliver` off the input — the gate is the engine's default,
1096
+ // so the wire never has to say "gate me" to be gated.
1097
+ if (b.deliverGate === 'auto')
1098
+ input.autoDeliver = true;
1058
1099
  if (b.repoRef !== undefined)
1059
1100
  input.repoRef = b.repoRef;
1060
1101
  if (b.workflow !== undefined)
@@ -1196,6 +1237,34 @@ runtime = {}) {
1196
1237
  }
1197
1238
  catch (err) {
1198
1239
  const msg = message(err);
1240
+ // The engine's own intake refusal (wicked-core#411 `StateHomeConfigError`) is a configuration
1241
+ // error, not a malformed request: the same typed 409 the pre-check above answers, over a fresh
1242
+ // survey — the pre-check saw a clean state home (or an addon that could not classify), the
1243
+ // engine did not.
1244
+ if (isEngineStateHomeRefusal(msg)) {
1245
+ const stateHome = runtime.stateHome !== undefined ? await runtime.stateHome.refresh() : null;
1246
+ return reply
1247
+ .code(409)
1248
+ .send(stateHome !== null && stateHome.unregistered.length > 0
1249
+ ? stateHomeBlockerBody(stateHome)
1250
+ : {
1251
+ error: msg,
1252
+ code: STATE_HOME_BLOCKER_CODE,
1253
+ stateHome: stateHome?.stateHome ?? null,
1254
+ unregistered: [],
1255
+ remedy: STATE_HOME_REMEDY,
1256
+ });
1257
+ }
1258
+ // wicked-core#461 / crew#556: the engine's typed `NoEligibleSeat` intake refusal — the plan
1259
+ // needs a seat and every seat of the roster was benched by the launcher (signed out / quota /
1260
+ // not installed). A 409 with a typed body, not the 400 the generic arm below answered: the
1261
+ // request is well-formed and succeeds unchanged once a seat is signed in — the conflict is
1262
+ // with the roster's standing (the `state_home_unregistered` rule) — and not a 5xx: the engine
1263
+ // is healthy and said no, naming every seat and its cause.
1264
+ const noSeat = parseNoEligibleSeat(msg);
1265
+ if (noSeat !== null) {
1266
+ return reply.code(409).send(noEligibleSeatBody(msg, noSeat));
1267
+ }
1199
1268
  // An unknown/archived project is a state conflict on a real resource, not a malformed
1200
1269
  // request: 404/409 per the projects error mapping; anything else keeps the launch 400/409.
1201
1270
  if (b.projectId !== undefined && /project.*not registered/i.test(msg)) {
@@ -1204,6 +1273,17 @@ runtime = {}) {
1204
1273
  if (b.projectId !== undefined && /archived|'default'|synthesized/i.test(msg)) {
1205
1274
  return reply.code(409).send({ error: msg });
1206
1275
  }
1276
+ // crew#554 / wicked-core#468: the engine refused the launch AT INTAKE because the handed
1277
+ // skills snapshot lacks the run's BASE skill (`SkillsError::BaseSkillRefused` — the message
1278
+ // names the skill and "refused at intake"). Nothing was planned or persisted. A typed 422 so a
1279
+ // composer renders a clear card (skill, policy, remedy) instead of a generic launch 400.
1280
+ if (/\bbase skill\b.*\brefused at intake\b/is.test(msg)) {
1281
+ const posture = runtime.skills?.baseSkill() ?? null;
1282
+ const remedy = posture !== null && posture.inCatalog
1283
+ ? `the skill is in the catalog — POST /skills/publish hands it to the next launch; or set baseSkillPolicy "warn" (PUT /settings) to run without the discipline directive`
1284
+ : `install a wicked-garden that ships the skill, POST /skills/refresh-baseline, then POST /skills/publish; or set baseSkillPolicy "warn" (PUT /settings) to run without the discipline directive`;
1285
+ return reply.code(422).send({ code: 'base_skill_refused', error: msg, baseSkill: posture, remedy });
1286
+ }
1207
1287
  const busy = /busy|in flight|already/i.test(msg);
1208
1288
  return reply.code(busy ? 409 : 400).send({ error: msg });
1209
1289
  }
@@ -1851,15 +1931,35 @@ runtime = {}) {
1851
1931
  text: z.string().min(1).max(65536),
1852
1932
  targets: z.array(z.string().min(1)).min(1).max(8).optional(),
1853
1933
  }).strict();
1934
+ // F-RECON-017: a message sent while a targeted seat is still answering the previous one is
1935
+ // REFUSED, not queued. The engine queues silently (the worker gets the text the instant the
1936
+ // previous turn's final block lands) and its reply frames carry no message correlation, so the
1937
+ // recon saw Q2's reply render under Q3's bubble. The daemon's own turn index (`chat-turns.ts`)
1938
+ // is the record: 409 `turn_in_flight` names the turn and the busy seats — a send that targets
1939
+ // only IDLE seats passes (a stalled seat never blocks a follow-up to the one that answered) —
1940
+ // and the 202 returns the `turnId` the reply frames are stamped with (`turn_id`) on `/ws`.
1854
1941
  app.post(`${V}/chats/:id/messages`, async (req, reply) => {
1855
1942
  const { id } = req.params;
1856
1943
  const parsed = ChatMessageSchema.safeParse(req.body);
1857
1944
  if (!parsed.success) {
1858
1945
  return reply.code(400).send(invalidBody(parsed.error, 'Invalid request body'));
1859
1946
  }
1947
+ const inFlight = chatTurns.inFlight(id, parsed.data.targets);
1948
+ if (inFlight !== null) {
1949
+ const ageS = Math.round(inFlight.ageMs / 1000);
1950
+ return reply.code(409).send({
1951
+ code: 'turn_in_flight',
1952
+ error: `${inFlight.busy.join(', ')} ${inFlight.busy.length === 1 ? 'is' : 'are'} still answering the previous ` +
1953
+ `message in this chat (turn ${inFlight.turnId}, ${ageS}s ago: “${inFlight.excerpt}”) — wait for the ` +
1954
+ `reply, or target only idle seats. Nothing was sent.`,
1955
+ chatId: id,
1956
+ turn: inFlight,
1957
+ });
1958
+ }
1860
1959
  try {
1861
1960
  const seats = await adapter.chatSend(id, parsed.data.text, parsed.data.targets);
1862
- return reply.code(202).send({ seats });
1961
+ const turn = chatTurns.begin(id, seats, parsed.data.text);
1962
+ return reply.code(202).send({ seats, ...(turn !== null ? { turnId: turn.turnId } : {}) });
1863
1963
  }
1864
1964
  catch (err) {
1865
1965
  const msg = message(err);
@@ -1924,6 +2024,7 @@ runtime = {}) {
1924
2024
  // (crew#502); the id stays parked until the engine's `chatClosed` is observed, so a delayed
1925
2025
  // close can never land on a chat that reused it (Copilot, #518).
1926
2026
  chatScopes.beginClose(id);
2027
+ chatTurns.closed(id);
1927
2028
  try {
1928
2029
  await adapter.chatClose(id);
1929
2030
  return { ok: true };
@@ -3289,6 +3390,26 @@ runtime = {}) {
3289
3390
  .send({ error: "deliverDefault must be 'pr' or 'none'" });
3290
3391
  }
3291
3392
  }
3393
+ // baseSkillRef / baseSkillPolicy (crew#554 / wicked-core#468): the discipline skill EVERY
3394
+ // governed unit is told to follow, and what a snapshot without it means. Both decide whether
3395
+ // launches carry the directive or are refused at intake, so a typo is a 400, never a silently
3396
+ // dropped key. The name is exported to the engine verbatim: a skill-name shape only.
3397
+ if (Object.hasOwn(patch, 'baseSkillRef')) {
3398
+ const r = patch.baseSkillRef;
3399
+ if (typeof r !== 'string' || !BASE_SKILL_REF_SHAPE.test(r.trim())) {
3400
+ return reply.code(400).send({
3401
+ error: 'baseSkillRef must be a skill name (lowercase letters, digits, "-", "_", ":", ".", up to 128 chars — e.g. "wicked-garden-governed-worker"), or "" to turn the base skill off',
3402
+ });
3403
+ }
3404
+ }
3405
+ if (Object.hasOwn(patch, 'baseSkillPolicy')) {
3406
+ const p = patch.baseSkillPolicy;
3407
+ if (typeof p !== 'string' || !BASE_SKILL_POLICIES.has(p)) {
3408
+ return reply.code(400).send({
3409
+ error: "baseSkillPolicy must be 'warn' (runs proceed without a missing base skill, with a diagnostics warning) or 'require' (the engine refuses launches at intake until the published snapshot holds it)",
3410
+ });
3411
+ }
3412
+ }
3292
3413
  // Skin-owned keys (crew#323): allowed through, but VALIDATED rather than trusted. The
3293
3414
  // daemon does not read these values, so the only two things it can check are the two that
3294
3415
  // can hurt it — a value it cannot persist, and a value big enough to bloat settings.json.
@@ -3329,6 +3450,8 @@ runtime = {}) {
3329
3450
  'workerStallEscalateAction',
3330
3451
  'workerStallMaxEscalations',
3331
3452
  'deliverDefault',
3453
+ 'baseSkillRef',
3454
+ 'baseSkillPolicy',
3332
3455
  ];
3333
3456
  const safe = {};
3334
3457
  for (const key of allowed) {
@@ -3354,6 +3477,12 @@ runtime = {}) {
3354
3477
  // WICKED_WORKER_HOME per worker spawn — never cached — so this alone makes the change live
3355
3478
  // at the next spawn: no daemon restart, no engine restart.
3356
3479
  applyWorkerConfigRoot(settings.worker_config_root);
3480
+ // Re-judge and re-export the BASE skill (crew#554) — the engine reads WICKED_BASE_SKILL_REF at
3481
+ // intake, per launch, so this alone makes a changed name or policy live at the next launch.
3482
+ // Only when the patch named either key: an unrelated settings write must not re-log the seam.
3483
+ if (Object.hasOwn(safe, 'baseSkillRef') || Object.hasOwn(safe, 'baseSkillPolicy')) {
3484
+ runtime.skills?.configureBaseSkill(settings);
3485
+ }
3357
3486
  // (No skills re-apply: the skills root is not a setting — skills/runtime.ts, codex round 5.)
3358
3487
  // `changed` names every persisted key, engine and `studio.*` alike; `ignored` (present only
3359
3488
  // when there is one) is where a dropped unknown key stops being invisible.