wicked-crew-api-types 0.34.0 → 0.36.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 (2) hide show
  1. package/index.d.ts +605 -19
  2. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -282,6 +282,34 @@ export interface AgentSession {
282
282
  * now; a joined `AgentSession.cost` is a follow-up once that store exists.
283
283
  */
284
284
  created_at?: number;
285
+ /**
286
+ * The interactive DOCUMENT this run answered (wave 6, F-4R2-006 root fix; api-types 0.36.0) —
287
+ * daemon-joined at DTO assembly on `GET /runs` and `GET /runs/:id` from the interactive seams'
288
+ * handoff ledgers (the system of record for "this handoff was answered by this run"), so a skin
289
+ * no longer parses `extra_write_roots` for it. `GET /runs?doc=<id>` filters on it. `null` =
290
+ * genuinely not a document run; ABSENT = a pre-0.36 daemon. A document chip renders only on
291
+ * `typeof === 'string'`.
292
+ */
293
+ document_id?: string | null;
294
+ /**
295
+ * Wave 6 (F-7R2-013): the run branch the worktree was minted on (`wicked/<run id>`, sanitized) —
296
+ * recorded by the engine when the worktree is ready and durable past its reap, so
297
+ * `GET /runs/:id/diff` serves the run's work from the branch (`source: 'branch'`) once the
298
+ * worktree is gone. Absent for an unbound run and on an engine predating wave 6 (the daemon then
299
+ * derives `wicked/<id>` itself).
300
+ */
301
+ run_branch?: string;
302
+ /** Wave 6 (F-7R2-013): the commit the worktree was minted FROM (`runBaseResolved.baseCommit`) —
303
+ * the branch diff's base. Absent for an unbound run, a reused worktree, or an older engine (the
304
+ * daemon then uses the branch's merge-base with the default branch). */
305
+ base_commit?: string;
306
+ /** Wave 6 (F-7R2-013): when the run reached a TERMINAL status, unix millis. A COMPLETED run's
307
+ * worktree is RETAINED from here until archive or the engine's retention window elapses
308
+ * (`WICKED_COMPLETED_WORKTREE_KEEP_DAYS`, default 14). Absent on a live run and on older engines. */
309
+ finished_at?: number;
310
+ /** Wave 6 (F-7R2-006): the seats BENCHED for this run — never convened, never a failover or
311
+ * judge target. Absent (never `[]`) when none / on an older engine. */
312
+ benched_seats?: BenchedSeat[];
285
313
  }
286
314
 
287
315
  /** An ordered unit of work within a run (`WorkUnit`). */
@@ -426,9 +454,72 @@ export interface RosterSeat {
426
454
  * unknown seat keys, or a daemon predating the field).
427
455
  */
428
456
  signed_in?: boolean | null;
457
+ /**
458
+ * What `signed_in` MEANS for this seat (api-types 0.35.0, F-2R2-009): `signed_in` — a credential
459
+ * artifact is observable; `signed_out` — none is and the seat needs one (a council benches it on
460
+ * its first ballot; a chat refuses it up front); `not_required` — none is, but the seat answers
461
+ * on a free tier with no account (`free_tier` names it); `unknown` — the probe cannot tell
462
+ * cheaply (keychain-backed seats, unknown seat keys). Absent on a daemon predating the field.
463
+ */
464
+ auth?: SeatAuth;
465
+ /**
466
+ * Where `auth` came from (api-types 0.36.0, F-A45-006): `seat-stderr` — the seat ITSELF reported
467
+ * no credential (a council ballot's "No API key found", a worker's 401, an authentication ACP
468
+ * fallback) within the last 30 minutes with no ok output since, which OVERRIDES the credential-
469
+ * file probe (`signed_in`) — the fresh rig's pi read `signed_in: true` off an empty `auth.json`
470
+ * while every ballot failed. Absent = the probe decided (and since 0.36.0 the probe itself needs
471
+ * a credential-SHAPED file, never mere presence).
472
+ */
473
+ auth_source?: 'seat-stderr';
474
+ /** Present with `auth_source: 'seat-stderr'`: the seat's own words, bounded. */
475
+ auth_evidence?: string;
476
+ /** Present when `auth` is `not_required`: the free tier the seat answers on. */
477
+ free_tier?: string;
478
+ /**
479
+ * Where the `not_required` reading came from: `registry` when the CLI's own record declared the
480
+ * credential requirement, `crew-heuristic` when the daemon's per-CLI table did (today's only
481
+ * source — the engine's `AgenticCli` declares none; a wicked-core follow-up). A reader can show
482
+ * the heuristic as such.
483
+ */
484
+ free_tier_source?: 'registry' | 'crew-heuristic';
485
+ /**
486
+ * Whether a council would seat AND keep this seat as far as the daemon can tell: enabled for
487
+ * council, runtime `health` active, `auth` not `signed_out`. The daemon's PREDICTION from its
488
+ * own records — the engine still convenes whatever roster it is handed and benches a seat only
489
+ * after it fails. Chat admission (`POST /chats` defaults) reads the same auth predicate.
490
+ */
491
+ council_eligible?: boolean;
492
+ /** Present when `council_eligible` is false: the one reason, in the operator's words. */
493
+ council_ineligible_reason?: string;
494
+ /**
495
+ * Present when THIS daemon's recent councils benched the seat: the engine's own
496
+ * `councilSeatFailed` evidence (`non_zero_exit` / `timed_out`, the derivative `benched` kind
497
+ * excluded), folded over a bounded window (`window_ms`) and cleared by the seat's next ok unit
498
+ * output. `council_eligible` is false while it is present.
499
+ */
500
+ council_bench?: RosterSeatCouncilBench;
429
501
  [k: string]: unknown;
430
502
  }
431
503
 
504
+ /** `RosterSeat.council_bench` (api-types 0.35.0). */
505
+ export interface RosterSeatCouncilBench {
506
+ /** Primary ballot failures inside the window. */
507
+ failures: number;
508
+ /** The last failure's `councilSeatFailed.kind`. */
509
+ last_kind: string;
510
+ /** ISO-8601 of the last failure. */
511
+ last_at: string;
512
+ /** The run the last failure happened in, when the frame named one. */
513
+ last_run?: string;
514
+ /** A bounded excerpt of the last failure's detail / stderr, when there was one. */
515
+ last_detail?: string;
516
+ /** The rolling window the failures were counted over, ms. */
517
+ window_ms: number;
518
+ }
519
+
520
+ /** A seat's auth reading (`RosterSeat.auth`; api-types 0.35.0). */
521
+ export type SeatAuth = 'signed_in' | 'signed_out' | 'not_required' | 'unknown';
522
+
432
523
  /** Body for `POST /open` — open a file/folder with the OS default application (crew#273). */
433
524
  export interface OpenPathBody {
434
525
  /**
@@ -471,6 +562,21 @@ export interface RunDiff {
471
562
  diff: string;
472
563
  /** The diff exceeded the 1 MB output cap and was cut. */
473
564
  truncated: boolean;
565
+ /**
566
+ * Where the diff was read from (wave 6, F-7R2-013; api-types 0.36.0): `worktree` — the live run
567
+ * worktree (staged + unstaged + untracked; the pre-0.36 answer); `branch` — the run's retained
568
+ * `wicked/<id>` branch in the REGISTERED repository, diffed against its base, served when the
569
+ * engine has reaped the worktree (a completed run) so the files view never goes dark at
570
+ * completion. A pre-0.36 daemon omits the field and answers 409 for a reaped worktree; 409 now
571
+ * means "no worktree AND no run branch" (nothing was ever committed for the run).
572
+ */
573
+ source?: 'worktree' | 'branch';
574
+ /** `source: 'branch'` — the run branch the diff was read from. */
575
+ branch?: string;
576
+ /** `source: 'branch'` — the base commit the branch was diffed against: the engine's recorded
577
+ * `AgentSession.base_commit` when it has one, else the branch's merge-base with the default
578
+ * branch; `?base=<ref>` overrides it with a plain in-repo ref. */
579
+ base?: string;
474
580
  }
475
581
 
476
582
  /** The daemon's cached open-gate record (`GET /runs/:id/gate`, DES-STUDIO-001 §3.3). */
@@ -629,17 +735,40 @@ export interface CoreEvent {
629
735
  hasValidatorPin?: boolean;
630
736
  executor_type?: string;
631
737
  executorType?: string;
632
- // unitDistributed enrichment fields
738
+ // unitDistributed enrichment fields — the wire spelling is camelCase (event_to_json; see
739
+ // UnitDistributedEvent, api-types 0.36.0); the snake_case variants are kept for older relays.
740
+ routingMethod?: string;
633
741
  routing_method?: string;
634
742
  agreement_pct?: number | null;
635
743
  returned?: number | null;
744
+ seated?: number | null;
636
745
  dissent?: number | null;
746
+ degradedReason?: string | null;
637
747
  degraded_reason?: string | null;
748
+ seatConstraint?: string | null;
749
+ /** `gateEvaluated` (wave 6, F-7R2-005): `true` when NOTHING gated the unit — render as UNGATED. */
750
+ ungated?: boolean;
751
+ /** `gateEvaluated` (wave 6): why, when `ungated`. */
752
+ ungatedReason?: string | null;
753
+ /** `gateEvaluated` (wave 6, #449 @ 9e11685): why the deterministic layer is absent / why no judge ran. */
754
+ floorNote?: string | null;
755
+ judgeSkippedReason?: string | null;
756
+ /** `repoChecksEvaluated` (wave 6): the sandbox level, and why the sandbox / check detection failed. */
757
+ sandboxLevel?: string;
758
+ sandboxError?: string | null;
759
+ detectError?: string | null;
760
+ /** `workerToolCallDenied` (wave 6, F-7R2-012): the refused command / the remedy / the unit's role. */
761
+ command?: string;
762
+ remedy?: string;
763
+ role?: string;
764
+ /** `runBaseResolved` (wave 6, F-7R2-013): the run branch the worktree was minted on. */
765
+ runBranch?: string;
638
766
  // councilConvened / councilDeliberated / councilVoted (live deliberation) — camelCase
639
767
  // per event_to_json
640
768
  clis?: string[];
641
769
  consensus?: boolean;
642
- agreementPct?: number;
770
+ /** councilConvened/Deliberated/Voted: a number; `unitDistributed` (api-types 0.36.0): `number | null`. */
771
+ agreementPct?: number | null;
643
772
  votes?: number;
644
773
  /** councilDeliberated: the completed ballot number (1-based). */
645
774
  round?: number;
@@ -905,6 +1034,28 @@ export interface GateEvaluatedEvent {
905
1034
  * pick, `false` when the judge fell back to the single default runner (prompt-only independence).
906
1035
  * `null` when no judge ran or the seat is unknown. */
907
1036
  judgeDistinct: boolean | null;
1037
+ /**
1038
+ * Wave 6 (F-7R2-005, api-types 0.36.0): `true` when NOTHING gated this unit — no deterministic
1039
+ * floor (no pinned validator, and the repo-checks floor did not apply), no agent judge, and an
1040
+ * EMPTY evaluator-policy selection — the exact default-allow shape run b86c14c1 passed seven times.
1041
+ * A consumer MUST render it as UNGATED, never as "pass", and a narrator must never say "checks
1042
+ * ran" without a `repoChecksEvaluated` for the same unit. Absent on an engine predating wave 6
1043
+ * (read `=== true`); `false` on a gated unit.
1044
+ */
1045
+ ungated?: boolean;
1046
+ /** WHY, when `ungated` — each absent layer and its cause (`"no judge: no eligible judge seat
1047
+ * distinct from creator \`claude\` (roster: claude; benched: codex (signed out))"`). `null` when
1048
+ * gated; absent on an older engine. */
1049
+ ungatedReason?: string | null;
1050
+ /** Wave 6 (wicked-core#449 @ 9e11685, api-types 0.36.0): WHY the deterministic layer is absent —
1051
+ * set whenever `hasDeterministicFloor` is `false` on an agent unit, judge or not (`"no pinned
1052
+ * validator; the repo-checks floor did not apply: the tree was not changed"`). `null` when a
1053
+ * floor ran; absent on an older engine. */
1054
+ floorNote?: string | null;
1055
+ /** Wave 6: WHY no judge was convened for a unit that WANTED one (its tree changed and no pinned
1056
+ * validator gated it) — `"no eligible judge seat distinct from creator \`claude\` (roster: …;
1057
+ * benched: …)"`. `null` when a judge ran or none was wanted; absent on an older engine. */
1058
+ judgeSkippedReason?: string | null;
908
1059
  }
909
1060
 
910
1061
  /** Foundation wave: session started with enriched context. */
@@ -972,6 +1123,12 @@ export type AcpFallbackKind =
972
1123
  | 'binary_unavailable'
973
1124
  | 'session_died'
974
1125
  | 'auth_required'
1126
+ /** Wave 6 (F-7R2-019, api-types 0.36.0): the ACP handshake was REFUSED for authentication — the
1127
+ * seat's account, not its binary (`pi-acp` existed; pi answered 401). The engine benches the seat
1128
+ * for the run and does NOT attempt the single-shot wrapped fallback (it fails the same way). */
1129
+ | 'auth_failed'
1130
+ /** Wave 6 — the seat reported no credential at all (`unauthenticated`); same bench, same no-retry. */
1131
+ | 'unauthenticated'
975
1132
  | 'governance_requires_wrapped'
976
1133
  | 'read_only_requires_wrapped'
977
1134
  | (string & {});
@@ -1212,8 +1369,19 @@ export type RepoChecksEvaluatedEvent = {
1212
1369
  attempt: number;
1213
1370
  passed: boolean;
1214
1371
  criterion: string;
1372
+ /** The checks that RAN. EMPTY means "0 checks detected" — a consumer must say so, never "checks
1373
+ * ran"; since wave 6 the frame says WHY (`detectError`, `sandboxError`). */
1215
1374
  checks: RepoCheckRun[];
1216
1375
  skipped: string[];
1376
+ /** Wave 6 (wicked-core#449 @ 9e11685, api-types 0.36.0): the sandbox level the checks ran under
1377
+ * (`bwrap`, `sandbox-exec`, `none`, …). Absent on an older engine. */
1378
+ sandboxLevel?: string;
1379
+ /** Wave 6: why the sandbox could not be armed, when it could not (`null` when it was). */
1380
+ sandboxError?: string | null;
1381
+ /** Wave 6: why check DETECTION failed (an unreadable `package.json`, a manifest with no
1382
+ * `typecheck`/`lint`/`test` script, …) — the reason an empty `checks` is empty; `null` when
1383
+ * detection succeeded. */
1384
+ detectError?: string | null;
1217
1385
  };
1218
1386
 
1219
1387
  // ── wicked-core#431 gate / deliver evidence (F-3R2-013 / -010 / -009; api-types 0.33.0) ──────────
@@ -1352,6 +1520,11 @@ export type RunBaseResolvedEvent = {
1352
1520
  fetched: boolean;
1353
1521
  lifted: boolean;
1354
1522
  note: string | null;
1523
+ /** Wave 6 (F-7R2-013, api-types 0.36.0): the run branch the worktree was minted on
1524
+ * (`wicked/<run id>`, sanitized), recorded on the session as `run_branch` beside `base_commit`
1525
+ * so the run's diff is servable from the branch once the worktree is reaped. Absent on an
1526
+ * engine predating wave 6. */
1527
+ runBranch?: string;
1355
1528
  };
1356
1529
 
1357
1530
  /** The gate-evidence events (wicked-core F-036/F-039, extended by wicked-core#431 — api-types 0.33.0
@@ -1359,12 +1532,45 @@ export type RunBaseResolvedEvent = {
1359
1532
  * {@link EvaluatorToolCallDeniedEvent}) as a discriminated union for consumers that narrow on `type`;
1360
1533
  * they also flow through the permissive {@link CoreEvent}. {@link RunBaseResolvedEvent} is
1361
1534
  * session-level evidence and stands on its own. */
1535
+ /**
1536
+ * Wave 6 (F-7R2-012, api-types 0.36.0) — a WORKER seat asked to run a REMOTE-WRITING command
1537
+ * (`git push`, `gh pr create|merge|edit|comment`, a `gh api` mutation, `gh release`, …) and the
1538
+ * engine REFUSED it: delivery is performed by the run's deliver phase (which lifts, re-verifies,
1539
+ * pushes and opens the PR so the ledger records it), never by a creator or evaluator seat. Emitted
1540
+ * on both carriers: the ACP permission bridge (`carrier: 'acp'`) answers the seat's permission
1541
+ * request with its reject option; the wrapped carrier's PreToolUse gate hook (`carrier:
1542
+ * 'wrapped_cli'`) blocks the call and the fold replays the record at the gate. A refusal costs the
1543
+ * seat one tool call, never the unit — the seat continues with `remedy`. Spelled exactly as
1544
+ * wicked-core's `CoreEvent::to_json` emits it. `type` alias on purpose (relays through the
1545
+ * `CoreEvent`-typed seams unchanged).
1546
+ */
1547
+ export type WorkerToolCallDeniedEvent = {
1548
+ type: 'workerToolCallDenied';
1549
+ session: string;
1550
+ ord: number;
1551
+ attempt: number;
1552
+ /** The registry seat key. */
1553
+ cli: string;
1554
+ /** `'acp'` | `'wrapped_cli'`; open-ended for a future carrier. */
1555
+ carrier: 'acp' | 'wrapped_cli' | (string & {});
1556
+ /** The unit's role (`creator` | `evaluator` | `neutral`). */
1557
+ role: 'creator' | 'evaluator' | 'neutral' | (string & {});
1558
+ /** The tool the seat invoked (`Bash`, `bash`, `shell`, …). */
1559
+ tool: string;
1560
+ /** The command text the seat sent. */
1561
+ command: string;
1562
+ reason: string;
1563
+ /** What the seat was told to do instead ("delivery is performed by the run's deliver phase"). */
1564
+ remedy: string;
1565
+ };
1566
+
1362
1567
  export type GateEvidenceEvent =
1363
1568
  | EvaluatorMutatedWorktreeEvent
1364
1569
  | RepoChecksEvaluatedEvent
1365
1570
  | WorktreeRestoredEvent
1366
1571
  | DeliverLiftEvaluatedEvent
1367
- | EvaluatorToolCallDeniedEvent;
1572
+ | EvaluatorToolCallDeniedEvent
1573
+ | WorkerToolCallDeniedEvent;
1368
1574
 
1369
1575
  // ── P2 decisions-full observability events (wicked-core EVT-001/012/013) ────
1370
1576
 
@@ -1436,17 +1642,64 @@ export interface UnitPlannedEvent {
1436
1642
  executor_type: 'agent' | 'tool';
1437
1643
  }
1438
1644
 
1439
- /** Foundation wave: a unit was distributed with full routing detail. */
1645
+ /**
1646
+ * Foundation wave: a unit was distributed with full routing detail.
1647
+ *
1648
+ * WIRE SPELLING (api-types 0.36.0, crew#533 follow-through): the engine's `CoreEvent::to_json` and
1649
+ * the napi frames spell these fields camelCase — `routingMethod`, `agreementPct`, `returned`,
1650
+ * `seated`, `dissent`, `degradedReason`, `seatConstraint` — exactly as `wicked-core-ts`'s own
1651
+ * `UnitDistributedEventJson` declares them; `packages/crew/tests/wire-contract.test.ts` pins this
1652
+ * interface against that type AND against a recorded frame. Pre-0.36 this interface declared the
1653
+ * snake_case spellings, which the engine never emitted — a consumer reading `degraded_reason` got
1654
+ * `undefined` and rendered a benched council as whole. The snake_case names stay for ONE minor as
1655
+ * `@deprecated` optional aliases (a normalising relay may still produce them); consumers read the
1656
+ * camelCase names. Every camelCase `Option` field is emitted unconditionally: `null`, never absent.
1657
+ */
1440
1658
  export interface UnitDistributedEvent {
1441
1659
  type: 'unitDistributed';
1442
1660
  session: string;
1443
1661
  ord: number;
1662
+ /** The roster key of the assigned seat. */
1444
1663
  cli: string;
1445
- routing_method: 'council' | 'degraded' | 'evaluator_distinct' | 'tool';
1446
- agreement_pct: number | null;
1664
+ /** How the seat was chosen: the council verdict, a degrade to the first candidate, an
1665
+ * evaluator ≠ creator reassignment, or a deterministic tool execution. */
1666
+ routingMethod: 'council' | 'degraded' | 'evaluator_distinct' | 'tool';
1667
+ agreementPct: number | null;
1668
+ /** Ballots that came back. Read against `seated`. */
1447
1669
  returned: number | null;
1670
+ /** Seats CONVENED for the council that produced this assignment (`null` = unknown / not a council). */
1671
+ seated: number | null;
1448
1672
  dissent: number | null;
1449
- degraded_reason: string | null;
1673
+ /**
1674
+ * WHY the routing was degraded. Since wave 6 (F-7R2-006) the engine sets it on EVERY routing arm
1675
+ * whenever the eligible seat set is smaller than the configured roster — `"4 of 5 seats benched:
1676
+ * codex (signed out — launcher), pi (unauthenticated — ballot), …"` — not only for its `degraded`
1677
+ * arm, so a council that held on a fraction of its seats reads as degraded. `null` when whole.
1678
+ */
1679
+ degradedReason: string | null;
1680
+ /** WHY the candidate seats were narrowed BEFORE the council voted (core#401): a non-portable
1681
+ * skill constrained the unit to a claude seat. `null` when every roster seat was a candidate. */
1682
+ seatConstraint: string | null;
1683
+ /** @deprecated api-types 0.36.0 — the engine emits `routingMethod`; removed in 0.37. */
1684
+ routing_method?: 'council' | 'degraded' | 'evaluator_distinct' | 'tool';
1685
+ /** @deprecated api-types 0.36.0 — the engine emits `agreementPct`; removed in 0.37. */
1686
+ agreement_pct?: number | null;
1687
+ /** @deprecated api-types 0.36.0 — the engine emits `degradedReason`; removed in 0.37. */
1688
+ degraded_reason?: string | null;
1689
+ }
1690
+
1691
+ /** One seat the wave-6 engine BENCHED for a run (`AgentSession.benched_seats`, F-7R2-006): never
1692
+ * convened, never a failover or judge target, named in `unitDistributed.degradedReason`. */
1693
+ export interface BenchedSeat {
1694
+ /** The roster key. */
1695
+ cli: string;
1696
+ /** Why — the launcher's words (`signed out`) or the engine's classification (`not_logged_in`,
1697
+ * `unauthenticated`). */
1698
+ reason: string;
1699
+ /** Who benched it: `launcher` (the roster's health probe — crew's `council_eligible`), `ballot`
1700
+ * (a council seat failure), `worker` (a unit's worker failed with an auth refusal), `judge`
1701
+ * (the seat failed while judging another unit — wicked-core#449 @ 9e11685). */
1702
+ source: 'launcher' | 'ballot' | 'worker' | 'judge' | (string & {});
1450
1703
  }
1451
1704
 
1452
1705
  // ── Worker injection + reassignment events (core#93) ──────────────────────────
@@ -1910,8 +2163,40 @@ export interface SkillsManifestResponse {
1910
2163
  root: string;
1911
2164
  /** The VERIFIED published snapshot `current` resolves to, or `null` before the first publish.
1912
2165
  * `path` is the absolute REAL path of `snapshots/<gen>` — byte-identical to the one input the
1913
- * engine is handed (`WICKED_SKILLS_SNAPSHOT`; design v3.1 §2). */
1914
- current: { gen: number; path: string } | null;
2166
+ * engine is handed (`WICKED_SKILLS_SNAPSHOT`; design v3.1 §2). `rules` / `drift` (api-types
2167
+ * 0.36.0, F-083 / crew#535) ride beside it on a daemon ≥ 0.7.30 — absent on an older one. */
2168
+ current: {
2169
+ gen: number;
2170
+ path: string;
2171
+ /** The portability rules the generation was published under vs the rules this daemon runs:
2172
+ * `recorded` is `null` for a generation predating the identity (0.7.29 and earlier);
2173
+ * `stale: true` = they differ and the daemon raised `skills.stale-rules`. */
2174
+ rules?: {
2175
+ recorded: PortabilityRulesIdentity | null;
2176
+ running: PortabilityRulesIdentity;
2177
+ stale: boolean;
2178
+ };
2179
+ /** The rows whose portability derives differently under the running rules than the snapshot
2180
+ * recorded — the `skills.stale-rules` warning names up to five; the list carries the rest.
2181
+ * `recorded.reasons` is `null` for a row written before per-reason portability (0.34.0). */
2182
+ drift?: SnapshotRowDrift[];
2183
+ } | null;
2184
+ }
2185
+
2186
+ /** The identity of a portability-rule table (api-types 0.36.0, F-083): its version and the
2187
+ * sha256 of the committed parity fixture (`tests/fixtures/portability_rules.json`). */
2188
+ export interface PortabilityRulesIdentity {
2189
+ version: number;
2190
+ sha256: string;
2191
+ }
2192
+
2193
+ /** One snapshot row whose portability the RUNNING rules derive differently than the snapshot
2194
+ * recorded (api-types 0.36.0, F-083) — see `SkillsManifestResponse.current.drift`. */
2195
+ export interface SnapshotRowDrift {
2196
+ name: string;
2197
+ recorded: { portable: boolean; reasons: SkillPortabilityReason[] | null };
2198
+ /** What the RUNNING rules derive for the row — the same per-reason shape `SkillEntry.portability` carries. */
2199
+ derived: SkillPortability;
1915
2200
  }
1916
2201
 
1917
2202
  export interface SkillFileEntry {
@@ -2513,6 +2798,148 @@ export interface TestingReconResponse {
2513
2798
  projectAttachError?: string;
2514
2799
  }
2515
2800
 
2801
+ // ── The governed test-authoring launch (wave 6; api-types 0.36.0) ──────────────────────────────
2802
+
2803
+ /**
2804
+ * The id of the governed test-authoring workflow the daemon serves (`GET /workflows` lists it, not
2805
+ * `is_system` — an operator-selectable work mode): recon (`wicked-garden-qe` plan) → author
2806
+ * (creator, evidence-floor pinned, `wicked-garden-qe` author) → verify (a TOOL phase that RUNS every
2807
+ * produced test under the repository's own harness — vitest/jest/pytest/Playwright — and FAILS the
2808
+ * unit when one fails or was never executed) → review (evaluator ≠ creator, `wicked-garden-qe`
2809
+ * review) → the ENGINE's deliver phase (appended per run; never a worker's `gh pr create`).
2810
+ * F-7R2-003/004/005/012/014/015 + acceptance R4-r2.
2811
+ */
2812
+ export type QeAuthorTestsWorkflowId = 'qe-author-tests';
2813
+
2814
+ /**
2815
+ * Body of `POST /testing/author` — the Testing page's "New test". Same scope wire as
2816
+ * {@link TestingReconBody} (`repoRefs` and/or `projectId`, resolved server-side), but the scope
2817
+ * MUST resolve to ≥ 1 repo (a test-authoring run writes into a repository; an unscoped author is a
2818
+ * 400). One governed `qe-author-tests` run per resolved repo, each filed under the repo's
2819
+ * `qe-tests-<repo name>` label group (`RunGroup` on `GET /campaigns`).
2820
+ */
2821
+ export interface TestingAuthorBody {
2822
+ /** The operator's intent — what to test — verbatim; the run's problem statement. */
2823
+ problem: string;
2824
+ /** The project to FILE the runs into. Alone: the scope is the project's `crew.repo` members. */
2825
+ projectId?: string;
2826
+ /**
2827
+ * The repos to author into (registry id or name). With `projectId`, THIS is the scope and the
2828
+ * project is the filing only (a NARROWED project scope — studio #263 F-4): the runs are filed
2829
+ * into the project without inheriting its other repo members, so a skin with repo chips names
2830
+ * the repos and the project once instead of fanning one `POST /runs` per repo (one deliver/PR
2831
+ * each). Unlike {@link TestingReconBody}, where both fields union. The 201 says which was used
2832
+ * (`scope`).
2833
+ */
2834
+ repoRefs?: string[];
2835
+ /** EXPLICITLY launch unattended (no intake gate). Default `false`: each run pauses at its intake
2836
+ * gate (`before:1`) with the plan on the table before any unit runs. */
2837
+ ungated?: boolean;
2838
+ /** `pr` (default) — the ENGINE's deliver phase opens the PR; `none` — the tests stay on the run
2839
+ * branch (`delivery: 'stranded'` on the wire, liftable via `POST /runs/:id/deliver`). */
2840
+ deliver?: 'pr' | 'none';
2841
+ }
2842
+
2843
+ /** One planned phase as the intake gate shows it — derived from the workflow DEFINITION. */
2844
+ export interface WorkflowPlanPhase {
2845
+ id: string;
2846
+ kind: StageKindPhase;
2847
+ role: PhaseRole;
2848
+ executor: 'agent' | 'tool';
2849
+ /** The garden skill the phase routes through (`wicked-garden-qe`), `null` for a tool phase. */
2850
+ skillRef: string | null;
2851
+ /** `auto` | `human` (unconditional confirm) | `human_if_not_pass` (a not-pass verdict escalates). */
2852
+ gate: 'auto' | 'human' | 'human_if_not_pass';
2853
+ executesCode: boolean;
2854
+ /** `true` for the phase the ENGINE appends and owns (the deliver phase) — not in the def. */
2855
+ engine?: boolean;
2856
+ }
2857
+
2858
+ /** The plan the intake gate shows (F-7R2-008): the phases in order + the seats a council may
2859
+ * pick from (the launcher's eligible roster — benched seats excluded). */
2860
+ export interface WorkflowPlan {
2861
+ workflow: string;
2862
+ phases: WorkflowPlanPhase[];
2863
+ seats: string[];
2864
+ }
2865
+
2866
+ /** One launched authoring run and the label group it was filed under. */
2867
+ export interface TestingAuthorRun {
2868
+ runId: string;
2869
+ repoRef: string;
2870
+ /** `qe-tests-<repo name>` — the `RunGroup.label` on `GET /campaigns`. */
2871
+ label: string;
2872
+ }
2873
+
2874
+ /**
2875
+ * The `POST /testing/author` 201 body. `runIds` is the source of truth (one per resolved repo in
2876
+ * the caller's order); `runId` is its first entry. `plan` is what the intake card renders before
2877
+ * approval; `gate` says whether the runs paused there. `campaignRegistered` is always `false`: an
2878
+ * author launch is filed as label groups, never an engine campaign (campaign nodes are
2879
+ * engine-launched and would not receive the daemon's deliver composition).
2880
+ */
2881
+ export interface TestingAuthorResponse {
2882
+ runId: string;
2883
+ runIds: string[];
2884
+ workflow: QeAuthorTestsWorkflowId;
2885
+ runs: TestingAuthorRun[];
2886
+ gate: 'before:1' | 'none';
2887
+ deliver: 'pr' | 'none';
2888
+ plan: WorkflowPlan;
2889
+ /** How the repos were chosen: `repoRefs` — the named repos (a `projectId`, when given, was the
2890
+ * filing only); `project` — the project's `crew.repo` members. Absent on a daemon predating it. */
2891
+ scope?: 'repoRefs' | 'project';
2892
+ campaignRegistered: false;
2893
+ }
2894
+
2895
+ /** One produced test file as the verify phase judged it. */
2896
+ export interface TestSetFile {
2897
+ path: string;
2898
+ /** `playwright` | `playwright-python` | `vitest` | `jest` | `pytest` | `python` | `unknown`. */
2899
+ harness: string;
2900
+ status: 'passed' | 'failed' | 'not-executed';
2901
+ }
2902
+
2903
+ /**
2904
+ * A registered TEST SET (wave 6, F-7R2-014; api-types 0.36.0) — the produced tests of a terminal
2905
+ * `qe-author-tests` run, read back from its verify phase's `QE-VERIFY` report and recorded as a
2906
+ * durable `testing.testset.registered` audit entry the daemon hydrates at boot. Served as
2907
+ * `CampaignsListResponse.test_sets` — what the Test landing counts. Deny-dominates: a run whose
2908
+ * verify phase FAILED registers too (`verified: false`, counts intact) so a red set is shown, never
2909
+ * hidden; a run that never reached its verdict registers with zero counts and `plan: null`.
2910
+ */
2911
+ export interface TestSet {
2912
+ /** `testset-<run id>` — one set per producing run. */
2913
+ id: string;
2914
+ run_id: string;
2915
+ workflow_id: QeAuthorTestsWorkflowId;
2916
+ /** The `RunGroup.label` the launch filed the run under, when it did. */
2917
+ label?: string;
2918
+ repo_ref: string | null;
2919
+ repo_name?: string;
2920
+ /** Unix millis. */
2921
+ registered_at: number;
2922
+ /** The producing run's terminal status. */
2923
+ run_status: SessionStatus | (string & {});
2924
+ /** The verify unit's status (`done` | `rejected` | …); `null` when the run never planned one. */
2925
+ verify_status: string | null;
2926
+ /** `true` ONLY when the verify phase passed: ≥ 1 produced test, every one executed and green,
2927
+ * the repository checks green, the PLAN present. */
2928
+ verified: boolean;
2929
+ files: TestSetFile[];
2930
+ produced: number;
2931
+ executed: number;
2932
+ passed: number;
2933
+ failed: number;
2934
+ not_executed: number;
2935
+ /** The produced `tests/PLAN-*.md`, `null` when none was produced. */
2936
+ plan: string | null;
2937
+ /** The harnesses the produced tests ran under (unique, first-use order). */
2938
+ harnesses: string[];
2939
+ /** The delivered PR, when the engine's deliver phase opened one. */
2940
+ deliverUrl?: string;
2941
+ }
2942
+
2516
2943
  // ── Governance claims (crew#40/43) ─────────────────────────────────────────────
2517
2944
 
2518
2945
  /**
@@ -2825,6 +3252,20 @@ export interface CodeGraphData {
2825
3252
  stats: { nodeCount: number; edgeCount: number; fileCount: number };
2826
3253
  }
2827
3254
 
3255
+ /**
3256
+ * `GET /repos/:id/graph` → 200 (api-types 0.35.0, F-2R2-005). `graph: null` = the repo's code
3257
+ * graph has not been built (nothing at its registered graph path) — and `reason` says WHY, so a
3258
+ * consumer of this route alone can tell "not indexed" from "empty": the same finding text the
3259
+ * repos wire carries when the engine has one (`finding` — e.g. `in_tree_code_graph_ignored`: a
3260
+ * checkout's in-tree graph is ignored and no live graph exists yet; re-run onboarding), else the
3261
+ * daemon's own sentence. Both absent when `graph` is present.
3262
+ */
3263
+ export interface RepoGraphResponse {
3264
+ graph: CodeGraphData | null;
3265
+ reason?: string;
3266
+ finding?: RepoFinding;
3267
+ }
3268
+
2828
3269
  export interface GitCommit {
2829
3270
  sha: string;
2830
3271
  shortSha: string;
@@ -3263,6 +3704,12 @@ export interface InteractiveStatusPosted {
3263
3704
  message?: string;
3264
3705
  /** The edit seam stamps the handoff version it is answering. */
3265
3706
  version?: number;
3707
+ /** Wave 6 (F-4R2-005 root fix; api-types 0.36.0): the governed run this narration is about, so a
3708
+ * skin keys the thread per run. Absent on a pre-launch status and on a pre-0.36 daemon. */
3709
+ run_id?: string;
3710
+ /** Wave 6: the unit (ord) the line narrates — the latest unit-scoped engine frame's `ord`, so a
3711
+ * skin renders one council at a time. Absent before the first unit-scoped frame. */
3712
+ unit_ord?: number;
3266
3713
  }
3267
3714
 
3268
3715
  /**
@@ -3291,6 +3738,62 @@ export interface InteractiveDocSummary {
3291
3738
  projectId: string;
3292
3739
  }
3293
3740
 
3741
+ /**
3742
+ * `GET /api/v1/interactive/docs` (api-types 0.36.0, studio #263) — every interactive document across
3743
+ * projects, listed by the DAEMON from disk WITHOUT spawning a bridge: each project's docs root
3744
+ * (the per-project `interactiveRoot` binding, else `WICKED_INTERACTIVE_ROOT`, else the default
3745
+ * root / its `projects/<id>` partition), each slug-named child carrying a `versions.json`, read by
3746
+ * the bridge's own `listDocs` rules — plus what only the daemon knows: the seams that answered
3747
+ * the document and the governed runs they launched (the handoff ledgers). The per-project
3748
+ * `GET /projects/:id/interactive/api/docs` spawns one bridge per project (≈60 s cold start) — a
3749
+ * skin must never fan that out on mount; this is the listing it mounts with. `?includeRetired=1`
3750
+ * lists tombstoned rows too.
3751
+ */
3752
+ export interface InteractiveDocsListing {
3753
+ /** Newest first (by `updatedAt`), then by name. */
3754
+ docs: InteractiveDocIndexRow[];
3755
+ /** Project docs roots the daemon could not read (or a partition its containment walk refused),
3756
+ * with the reason — never silently dropped. `[]` when every root was readable. */
3757
+ unreachable: InteractiveDocsUnreachable[];
3758
+ }
3759
+
3760
+ /** The seams that can answer a document (the handoff ledgers). */
3761
+ export type InteractiveSeamKind = 'draft' | 'edit' | 'chat' | 'demo' | (string & {});
3762
+
3763
+ /** One document on the daemon-wide listing (api-types 0.36.0). */
3764
+ export interface InteractiveDocIndexRow {
3765
+ /** The project whose docs root holds the document. A root SHARED by several projects (an
3766
+ * explicit `interactiveRoot` binding, or `WICKED_INTERACTIVE_ROOT` applying to all) lists once,
3767
+ * under the first project in listing order (the synthesized `default` first). */
3768
+ projectId: string;
3769
+ /** The doc name (slug). */
3770
+ name: string;
3771
+ /** Manifest `kind`; a manifest without one lists as `doc`. */
3772
+ kind: 'doc' | 'html' | 'source' | 'demo' | (string & {});
3773
+ /** Head version; `null` when the manifest carries none. */
3774
+ head: number | null;
3775
+ /** Lineage size. */
3776
+ versions: number;
3777
+ /** ISO-8601 of the head (last) version, or `null` when the lineage is empty. */
3778
+ updatedAt: string | null;
3779
+ /** Present only on a retired (tombstoned) row, listed only with `?includeRetired=1`. */
3780
+ retired?: true;
3781
+ /** ISO-8601 retirement timestamp; present with `retired`. */
3782
+ retiredAt?: string;
3783
+ /** The seams that answered this document, from the handoff ledgers (`[]` = none yet). */
3784
+ kinds: InteractiveSeamKind[];
3785
+ /** The governed runs those seams launched for it (ledger order) — the doc ↔ run binding. */
3786
+ runs: string[];
3787
+ }
3788
+
3789
+ /** A project docs root the listing could not read (api-types 0.36.0). */
3790
+ export interface InteractiveDocsUnreachable {
3791
+ projectId: string;
3792
+ /** The resolved root, or `null` when the containment walk refused the partition before resolving one. */
3793
+ root: string | null;
3794
+ error: string;
3795
+ }
3796
+
3294
3797
  /**
3295
3798
  * One normalized entry of the merged project activity feed
3296
3799
  * (`GET /projects/:id/activity`, ADR §5.2): core events of member runs/chats
@@ -3403,8 +3906,10 @@ export interface ChatScope {
3403
3906
  cwd: string;
3404
3907
  /** Whether a READ-ONLY estate MCP over a code graph is attached, and why / why not. `repoLabel`
3405
3908
  * is the estate label the project graph indexes the (single) scoped repo under, when the
3406
- * binding was repo-bound — informational; the engine binds by graph path. */
3407
- graph: { bound: boolean; reason: string; repoLabel?: string };
3909
+ * binding was repo-bound — informational; the engine binds by graph path. `action` (api-types
3910
+ * 0.35.0, F-2R2-008) names the UI action that would change a `bound: false` outcome, when one
3911
+ * would: `projects.graph.refresh` = build the project graph from the project page. */
3912
+ graph: { bound: boolean; reason: string; repoLabel?: string; action?: ProjectGraphAction };
3408
3913
  /** `crew.repo` members of the project whose registry record is gone — named, not readable. */
3409
3914
  dangling: string[];
3410
3915
  }
@@ -3416,6 +3921,31 @@ export interface ChatSeatOutcome {
3416
3921
  error?: string;
3417
3922
  }
3418
3923
 
3924
+ /**
3925
+ * One seat a `POST /chats` did NOT seat, and why (api-types 0.35.0, F-2R2-007): a DEFAULT seat the
3926
+ * daemon's admission dropped before the engine saw it (signed out; not admissible to a scoped chat
3927
+ * — its ACP adapter asks no permissions and arms no sandbox, or it has no ACP adapter), or a
3928
+ * REQUESTED seat (`clis`) the engine refused (`ChatSeatOutcome.ok: false`, its `error` repeated
3929
+ * here as `reason`). One list for the scope card and the thread to read.
3930
+ */
3931
+ /**
3932
+ * Why a seat was not seated (api-types 0.36.0, F-A45-011): `auth` — signed out (the probe, or the
3933
+ * seat's own "no credential" report); `scope` — the scoped-chat admission rule; `bench` — benched by
3934
+ * this daemon's recent councils; `budget` — the engine did not warm it within its dispatch budget
3935
+ * (the seat timed out or was dropped at dispatch); `engine` — the engine refused it with its own
3936
+ * reason. Open-ended so a newer daemon's source parses in an older skin.
3937
+ */
3938
+ export type ChatRefusalSource = 'auth' | 'scope' | 'bench' | 'budget' | 'engine' | (string & {});
3939
+
3940
+ export interface ChatSeatRefusal {
3941
+ cliKey: string;
3942
+ reason: string;
3943
+ /** api-types 0.36.0 (F-A45-011): the cause class. Absent on a daemon predating the field. Since
3944
+ * 0.36.0 EVERY requested-or-defaulted seat that is not in `seats` as warm has an entry — a seat
3945
+ * the engine DROPPED at dispatch (absent from `seats` altogether) included, never a silent gap. */
3946
+ source?: ChatRefusalSource;
3947
+ }
3948
+
3419
3949
  /** `POST /chats` → 201. */
3420
3950
  export interface ChatOpenResponse {
3421
3951
  chatId: string;
@@ -3424,8 +3954,29 @@ export interface ChatOpenResponse {
3424
3954
  scope: ChatScope;
3425
3955
  /** Present when the chat opened but its `crew.chat` filing into `projectId` failed. */
3426
3956
  projectAttachError?: string;
3957
+ /** Every seat that was asked for or defaulted and is NOT in `seats` as warm, with its reason
3958
+ * (api-types 0.35.0). Empty when every seat warmed; absent on a daemon predating the field. */
3959
+ refused?: ChatSeatRefusal[];
3427
3960
  }
3428
3961
 
3962
+ /**
3963
+ * A seat refused at `POST /chats` (api-types 0.35.0, F-2R2-007). DAEMON-SYNTHETIC: broadcast
3964
+ * straight to `/ws` once per refused seat, right after the open, so the chat thread can say why a
3965
+ * seat is missing (the studio's `chat-scope-admission` copy) — the engine emits nothing for a seat
3966
+ * it never saw. `chat` names the chat the way the engine's `chatClosed` does; `project_id` rides
3967
+ * along when the chat was filed. An anonymous object type on purpose, like the stall frames, so it
3968
+ * flows through `CoreEvent`-typed seams.
3969
+ */
3970
+ export type ChatSeatRefusedFrame = {
3971
+ type: 'chatSeatRefused';
3972
+ chat: string;
3973
+ cliKey: string;
3974
+ reason: string;
3975
+ /** api-types 0.36.0 (F-A45-011): the cause class — see {@link ChatRefusalSource}. */
3976
+ source?: ChatRefusalSource;
3977
+ project_id?: string;
3978
+ };
3979
+
3429
3980
  /** One live chat on `GET /chats` (FINDING-027 gap 4; scope fields with crew#502). */
3430
3981
  export interface ChatSummary {
3431
3982
  chatId: string;
@@ -3452,6 +4003,10 @@ export interface ChatDetailResponse {
3452
4003
  seats: string[];
3453
4004
  /** The scope recorded at open; `null` for a chat this daemon did not open (or after a restart). */
3454
4005
  scope: ChatScope | null;
4006
+ /** The seats refused at open (api-types 0.36.0, F-A45-011) — the same list the 201 carried, so
4007
+ * the admission copy survives a reload; `null` for a chat this daemon did not open; absent on a
4008
+ * daemon predating the field. */
4009
+ refused?: ChatSeatRefusal[] | null;
3455
4010
  }
3456
4011
 
3457
4012
  // ── Project code graph (DES-PROJECT-001; the co-located multi-repo graph) ──────
@@ -3522,11 +4077,21 @@ export interface ProjectGraphRepo {
3522
4077
  }
3523
4078
 
3524
4079
  /** `GET /projects/:id/graph` — what the project graph holds and what it cannot answer. */
4080
+ /** The UI action that resolves a project-graph state or a declined binding (api-types 0.35.0):
4081
+ * `projects.graph.refresh` = `POST /projects/:id/graph/refresh`, surfaced as the project page's
4082
+ * build/refresh control. */
4083
+ export type ProjectGraphAction = 'projects.graph.refresh';
4084
+
3525
4085
  export interface ProjectGraphStatus {
3526
4086
  projectId: string;
3527
4087
  state: ProjectGraphState;
3528
- /** One sentence naming the state's cause and its remedy. Always present, never empty. */
4088
+ /** One sentence naming the state's cause and its remedy, in the operator's words — never a raw
4089
+ * route (F-2R2-008); the route rides on `action`. Always present, never empty. */
3529
4090
  detail: string;
4091
+ /** Present when one UI action resolves the state (today: `not-indexed` with registered members
4092
+ * → `projects.graph.refresh`). Absent when no single action does (dangling-only members, no
4093
+ * members, or a graph that is already built). api-types 0.35.0. */
4094
+ action?: ProjectGraphAction;
3530
4095
  /** Absolute path of the co-located database; `null` when there is none to point at. */
3531
4096
  dbPath: string | null;
3532
4097
  repos: ProjectGraphRepo[];
@@ -3836,6 +4401,9 @@ export interface RunGroup {
3836
4401
  export interface CampaignsListResponse {
3837
4402
  campaigns: Campaign[];
3838
4403
  groups: RunGroup[];
4404
+ /** The registered test sets (wave 6, F-7R2-014; api-types 0.36.0 — ADDITIVE: a pre-0.36 daemon
4405
+ * omits it). Newest first. See {@link TestSet}. */
4406
+ test_sets?: TestSet[];
3839
4407
  }
3840
4408
 
3841
4409
  /**
@@ -4082,16 +4650,24 @@ export interface DiagnosticsGovernanceDeadletters {
4082
4650
  /** `true` when the fold stopped at its byte cap — `count` is then a floor. */
4083
4651
  truncated: boolean;
4084
4652
  /** The pre-fix outbox under HOME (`~/.something-wicked/wicked-apps/emit-outbox.ndjson`) when it exists and is
4085
- * non-empty — events earlier daemons on this host spooled there; `null` otherwise. Reported, never written. */
4086
- legacyOutbox: { path: string; bytes: number } | null;
4653
+ * non-empty — events earlier daemons on this host spooled there; `null` otherwise. Reported, never written.
4654
+ * `scope` (api-types 0.35.0, F-2R2-006): `own` = this daemon runs in that HOME's default state home, so the
4655
+ * file is its own earlier versions' outbox (the finding is a warning with the replay recipe); `host` = this
4656
+ * daemon's state home is isolated, so the file — shared by every daemon on the host — cannot be attributed
4657
+ * to it (an `info` finding with a read-only `--dry-run` inspect recipe and no replay). REQUIRED, deliberately:
4658
+ * the daemon always attributes the file it reports, so a `legacyOutbox` object without `scope` never leaves a
4659
+ * daemon shipping this contract; a skin that talks to an OLDER daemon must treat the whole `legacyOutbox`
4660
+ * object as best-effort (it predates the attribution), not the field alone. */
4661
+ legacyOutbox: { path: string; bytes: number; scope: 'own' | 'host' } | null;
4087
4662
  }
4088
4663
 
4089
4664
  /** One governance finding. `governance.store` (error) = no store resolved, every emit dead-letters;
4090
- * `governance.deadletter` (error) = the outbox holds entries; `governance.legacy-outbox` (warning) =
4091
- * the pre-fix HOME outbox exists. Every message names the replay command. */
4665
+ * `governance.deadletter` (error) = the outbox holds entries; `governance.legacy-outbox` = the pre-fix
4666
+ * HOME outbox exists — `warning` with the replay command when it is this daemon's own, `info` with none
4667
+ * when it is shared across the host's daemons and not this one's (api-types 0.35.0). */
4092
4668
  export interface DiagnosticsGovernanceFinding {
4093
4669
  kind: 'governance.store' | 'governance.deadletter' | 'governance.legacy-outbox';
4094
- severity: 'warning' | 'error';
4670
+ severity: 'info' | 'warning' | 'error';
4095
4671
  message: string;
4096
4672
  }
4097
4673
 
@@ -4122,8 +4698,18 @@ export interface DiagnosticsSkillsFinding {
4122
4698
  * (`error`, api-types 0.29.0) = `manifest.json` could not be read when diagnostics were taken
4123
4699
  * (corrupt, unreadable, or the root no longer the one the store bound) — reported as
4124
4700
  * `config-error` with the cause instead of the stale boot outcome; the exported engine input is
4125
- * unchanged until the daemon restarts. */
4126
- kind: 'skills.fallback' | 'skills.blocked' | 'skills.config' | 'skills.source' | 'skills.manifest';
4701
+ * unchanged until the daemon restarts; `skills.stale-rules` (`warning`, api-types 0.36.0, F-083 /
4702
+ * crew#535) = the CURRENT generation was published under OTHER portability rules than the daemon
4703
+ * now runs — accepted (the snapshot is never rewritten; the runtime stays `published`), the rows
4704
+ * that now derive differently are named (`SkillsManifestResponse.current.drift`), and a publish
4705
+ * records the running rules and clears it. */
4706
+ kind:
4707
+ | 'skills.fallback'
4708
+ | 'skills.blocked'
4709
+ | 'skills.config'
4710
+ | 'skills.source'
4711
+ | 'skills.manifest'
4712
+ | 'skills.stale-rules';
4127
4713
  severity: 'warning' | 'error';
4128
4714
  message: string;
4129
4715
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wicked-crew-api-types",
3
- "version": "0.34.0",
4
- "description": "The wire contract of the wicked-crew daemon's /api/v1 REST surface and /ws CoreEvent frames — types only, zero runtime",
3
+ "version": "0.36.0",
4
+ "description": "The wire contract of the wicked-crew daemon's /api/v1 REST surface and /ws CoreEvent frames \u2014 types only, zero runtime",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",
7
7
  "exports": {