wicked-crew 0.4.0 → 0.6.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 (132) hide show
  1. package/dist/api/api-prefix.d.ts +13 -0
  2. package/dist/api/api-prefix.d.ts.map +1 -0
  3. package/dist/api/api-prefix.js +13 -0
  4. package/dist/api/api-prefix.js.map +1 -0
  5. package/dist/api/audit.d.ts +57 -0
  6. package/dist/api/audit.d.ts.map +1 -0
  7. package/dist/api/audit.js +115 -0
  8. package/dist/api/audit.js.map +1 -0
  9. package/dist/api/auth.d.ts +192 -0
  10. package/dist/api/auth.d.ts.map +1 -0
  11. package/dist/api/auth.js +515 -0
  12. package/dist/api/auth.js.map +1 -0
  13. package/dist/api/evidence.d.ts +11 -0
  14. package/dist/api/evidence.d.ts.map +1 -1
  15. package/dist/api/evidence.js +8 -3
  16. package/dist/api/evidence.js.map +1 -1
  17. package/dist/api/gate-cache.d.ts +8 -0
  18. package/dist/api/gate-cache.d.ts.map +1 -1
  19. package/dist/api/gate-cache.js +10 -0
  20. package/dist/api/gate-cache.js.map +1 -1
  21. package/dist/api/open-path.d.ts +18 -0
  22. package/dist/api/open-path.d.ts.map +1 -0
  23. package/dist/api/open-path.js +79 -0
  24. package/dist/api/open-path.js.map +1 -0
  25. package/dist/api/routes.d.ts +113 -1
  26. package/dist/api/routes.d.ts.map +1 -1
  27. package/dist/api/routes.js +516 -27
  28. package/dist/api/routes.js.map +1 -1
  29. package/dist/api/seat-health.d.ts +55 -0
  30. package/dist/api/seat-health.d.ts.map +1 -0
  31. package/dist/api/seat-health.js +273 -0
  32. package/dist/api/seat-health.js.map +1 -0
  33. package/dist/api/seat-signin.d.ts +27 -0
  34. package/dist/api/seat-signin.d.ts.map +1 -0
  35. package/dist/api/seat-signin.js +143 -0
  36. package/dist/api/seat-signin.js.map +1 -0
  37. package/dist/api/server.d.ts +111 -3
  38. package/dist/api/server.d.ts.map +1 -1
  39. package/dist/api/server.js +236 -11
  40. package/dist/api/server.js.map +1 -1
  41. package/dist/api/unit-output.d.ts +62 -0
  42. package/dist/api/unit-output.d.ts.map +1 -0
  43. package/dist/api/unit-output.js +108 -0
  44. package/dist/api/unit-output.js.map +1 -0
  45. package/dist/cli/index.js +97 -3
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/cli/mcp.d.ts +14 -0
  48. package/dist/cli/mcp.d.ts.map +1 -0
  49. package/dist/cli/mcp.js +119 -0
  50. package/dist/cli/mcp.js.map +1 -0
  51. package/dist/core/adapter.d.ts +106 -1
  52. package/dist/core/adapter.d.ts.map +1 -1
  53. package/dist/core/adapter.js +293 -12
  54. package/dist/core/adapter.js.map +1 -1
  55. package/dist/core/bridge-path.d.ts +17 -6
  56. package/dist/core/bridge-path.d.ts.map +1 -1
  57. package/dist/core/bridge-path.js +17 -6
  58. package/dist/core/bridge-path.js.map +1 -1
  59. package/dist/core/types.d.ts +23 -352
  60. package/dist/core/types.d.ts.map +1 -1
  61. package/dist/core/types.js +11 -4
  62. package/dist/core/types.js.map +1 -1
  63. package/dist/interactive/bridge-pool.d.ts +71 -0
  64. package/dist/interactive/bridge-pool.d.ts.map +1 -0
  65. package/dist/interactive/bridge-pool.js +187 -0
  66. package/dist/interactive/bridge-pool.js.map +1 -0
  67. package/dist/interactive/bridge-root.d.ts +36 -0
  68. package/dist/interactive/bridge-root.d.ts.map +1 -0
  69. package/dist/interactive/bridge-root.js +48 -0
  70. package/dist/interactive/bridge-root.js.map +1 -0
  71. package/dist/interactive/draft-events.d.ts +144 -0
  72. package/dist/interactive/draft-events.d.ts.map +1 -0
  73. package/dist/interactive/draft-events.js +490 -0
  74. package/dist/interactive/draft-events.js.map +1 -0
  75. package/dist/interactive/edit-events.d.ts +172 -0
  76. package/dist/interactive/edit-events.d.ts.map +1 -0
  77. package/dist/interactive/edit-events.js +530 -0
  78. package/dist/interactive/edit-events.js.map +1 -0
  79. package/dist/interactive/ledger.d.ts +39 -0
  80. package/dist/interactive/ledger.d.ts.map +1 -0
  81. package/dist/interactive/ledger.js +93 -0
  82. package/dist/interactive/ledger.js.map +1 -0
  83. package/dist/interactive/proxy-routes.d.ts +39 -0
  84. package/dist/interactive/proxy-routes.d.ts.map +1 -0
  85. package/dist/interactive/proxy-routes.js +189 -0
  86. package/dist/interactive/proxy-routes.js.map +1 -0
  87. package/dist/interactive/ws-relay.d.ts +85 -0
  88. package/dist/interactive/ws-relay.d.ts.map +1 -0
  89. package/dist/interactive/ws-relay.js +191 -0
  90. package/dist/interactive/ws-relay.js.map +1 -0
  91. package/dist/projects/activity.d.ts +29 -0
  92. package/dist/projects/activity.d.ts.map +1 -0
  93. package/dist/projects/activity.js +172 -0
  94. package/dist/projects/activity.js.map +1 -0
  95. package/dist/projects/charter.d.ts +28 -0
  96. package/dist/projects/charter.d.ts.map +1 -0
  97. package/dist/projects/charter.js +53 -0
  98. package/dist/projects/charter.js.map +1 -0
  99. package/dist/projects/events.d.ts +55 -0
  100. package/dist/projects/events.d.ts.map +1 -0
  101. package/dist/projects/events.js +141 -0
  102. package/dist/projects/events.js.map +1 -0
  103. package/dist/projects/membership-index.d.ts +25 -0
  104. package/dist/projects/membership-index.d.ts.map +1 -0
  105. package/dist/projects/membership-index.js +46 -0
  106. package/dist/projects/membership-index.js.map +1 -0
  107. package/dist/projects/routes.d.ts +90 -0
  108. package/dist/projects/routes.d.ts.map +1 -0
  109. package/dist/projects/routes.js +388 -0
  110. package/dist/projects/routes.js.map +1 -0
  111. package/dist/projects/settings.d.ts +32 -0
  112. package/dist/projects/settings.d.ts.map +1 -0
  113. package/dist/projects/settings.js +64 -0
  114. package/dist/projects/settings.js.map +1 -0
  115. package/dist/qe/acceptance.d.ts +137 -0
  116. package/dist/qe/acceptance.d.ts.map +1 -0
  117. package/dist/qe/acceptance.js +249 -0
  118. package/dist/qe/acceptance.js.map +1 -0
  119. package/dist/qe/gate-events.d.ts +111 -0
  120. package/dist/qe/gate-events.d.ts.map +1 -0
  121. package/dist/qe/gate-events.js +168 -0
  122. package/dist/qe/gate-events.js.map +1 -0
  123. package/dist/qe/ledger.d.ts +100 -0
  124. package/dist/qe/ledger.d.ts.map +1 -0
  125. package/dist/qe/ledger.js +154 -0
  126. package/dist/qe/ledger.js.map +1 -0
  127. package/dist/studio/assets/index-CCwXa1cn.js +428 -0
  128. package/dist/studio/assets/index-HWxo0h41.css +32 -0
  129. package/dist/studio/index.html +2 -2
  130. package/package.json +10 -4
  131. package/dist/studio/assets/index-Ci_R6ARr.js +0 -423
  132. package/dist/studio/assets/index-Dio_c1Q3.css +0 -32
@@ -1,4 +1,16 @@
1
- import type { CoreEvent, LaunchRunInput, RepoEntry, RepoOnboardRef, SessionView, RecordedEvent, GovernancePolicy, ConformanceRule, GovernanceClaim, CoverageReport, WorkflowDef, SystemSettings } from './types.js';
1
+ import type { CoreEvent, LaunchRunInput, RepoEntry, RepoOnboardRef, SessionView, RecordedEvent, GovernancePolicy, ConformanceRule, GovernanceClaim, CoverageReport, GraphKind, WorkflowDef, SystemSettings, Project, ProjectMember, InteractionRequest } from './types.js';
2
+ /** Where the daemon persists system settings. Exported so tests write fixtures to the ONE
3
+ * spelling of this path (the tests/ tree is audited against spelling core paths itself). */
4
+ export declare function settingsFilePath(): string;
5
+ /** Read user-registered workflow overlays from `dir` (the same dir `registerWorkflow` writes to).
6
+ *
7
+ * Skips: files whose id matches a built-in (those are `_writeBuiltinOverlay` artifacts written FOR
8
+ * the Rust actor, not user workflows — including them would duplicate a built-in in `listWorkflows`),
9
+ * non-`.json` files, and any file that does not parse into a `{id, phases[]}` shape (the Rust actor
10
+ * skips an unreadable overlay too, so crew must not surface one it can't). A missing dir yields `[]`.
11
+ *
12
+ * Exported so the FINDING-002 restart-hydration path is unit-testable without spawning a Core. */
13
+ export declare function readOverlayWorkflows(dir: string, builtinIds: ReadonlySet<string>): WorkflowDef[];
2
14
  export declare const BUILTIN_WORKFLOWS: WorkflowDef[];
3
15
  /**
4
16
  * Chat is not available in this deployment at all — a capability gap, never a bad request.
@@ -15,6 +27,14 @@ export declare const BUILTIN_WORKFLOWS: WorkflowDef[];
15
27
  export declare class ChatUnsupportedError extends Error {
16
28
  constructor(message: string);
17
29
  }
30
+ /**
31
+ * The project surface (DES-PROJECT-001) is not available in this deployment — the installed
32
+ * wicked-core-ts predates 0.6.0. Typed for the same reason as `ChatUnsupportedError`: the routes
33
+ * answer 501 ("upgrade the engine") on this, never 400 ("fix your request").
34
+ */
35
+ export declare class ProjectsUnsupportedError extends Error {
36
+ constructor(what: string);
37
+ }
18
38
  /**
19
39
  * `resolveElicitation` is not available in this deployment — the NAPI binding has not
20
40
  * landed in the installed `wicked-core-ts` yet (DES-002 §4 P-1 stub).
@@ -63,6 +83,15 @@ export interface CoreAdapterOptions {
63
83
  * endpoint and the WS fan-out funnel through this stable API — so when the
64
84
  * in-flight core-ts subscribe/teardown signature lands, only this file changes.
65
85
  */
86
+ /** The ids of a workflow's phases that carry a HUMAN gate (`human_confirm` unconditional, or the
87
+ * conditional `human_confirm_if`) — i.e. the phases that will PAUSE for a person.
88
+ *
89
+ * FINDING-023 (residual): core#208 made a workflow's phase gate deliberately WIN over a run-level
90
+ * `humanConfirm: none` (it pauses, with a self-disclosing note), but there was no way to learn a
91
+ * workflow's gates BEFORE launching it — an operator picking `none` for an unattended run only found
92
+ * out when it paused. Surfacing this on `GET /workflows/:id` is that missing launch-time signal.
93
+ * `'auto'` (the string form) is NOT a human gate. */
94
+ export declare function humanGatePhaseIds(wf: WorkflowDef): string[];
66
95
  export declare class CoreAdapter {
67
96
  private readonly core;
68
97
  private readonly subscription;
@@ -89,6 +118,13 @@ export declare class CoreAdapter {
89
118
  confirmGate(runId: string, approve: boolean, amend?: string): Promise<string>;
90
119
  /** Cancel a run → the status token. */
91
120
  cancelRun(runId: string): Promise<string>;
121
+ /**
122
+ * Archive (or unarchive) a TERMINAL run (crew#265) — write-off, not delete. Resolves `true`
123
+ * when the session existed, `false` for an unknown id (→ 404); REJECTS when the run is
124
+ * non-terminal (the route answers 409). Fail-closed on an old addon: method-presence guard,
125
+ * same doctrine as `injectWorkerMessage`.
126
+ */
127
+ archiveRun(runId: string, archived: boolean, note?: string): Promise<boolean>;
92
128
  /** Inject an operator message into a run's active PTY worker(s). target="all" or a CLI key. */
93
129
  injectWorkerMessage(runId: string, message: string, target: string): Promise<string>;
94
130
  /**
@@ -107,6 +143,51 @@ export declare class CoreAdapter {
107
143
  sessionsDetail(): Promise<SessionView[]>;
108
144
  /** A unit's captured transcript (string, or `null`). */
109
145
  workOutput(unitId: string): Promise<string | null>;
146
+ /** True when the installed addon carries the project surface (0.6.0+). */
147
+ projectsSupported(): boolean;
148
+ private requireProjects;
149
+ projectCreate(name: string, description?: string): Promise<Project>;
150
+ projectUpdate(id: string, patch: {
151
+ name?: string | undefined;
152
+ description?: string | undefined;
153
+ status?: string | undefined;
154
+ }): Promise<Project>;
155
+ /** Every stored project (all statuses, newest first). The `default` project is NOT here — the
156
+ * route layer synthesizes it (ADR §7: computed, never stored). */
157
+ projectList(): Promise<Project[]>;
158
+ projectGet(id: string): Promise<Project | null>;
159
+ projectMembers(projectId: string): Promise<ProjectMember[]>;
160
+ /** Attach a member. `created:false` = the idempotent duplicate hit (emit no event for it). */
161
+ projectMemberAttach(projectId: string, kind: string, ref: string, meta?: Record<string, unknown>, attachedBy?: string): Promise<{
162
+ member: ProjectMember;
163
+ created: boolean;
164
+ }>;
165
+ /** Detach (tombstone). `false` = no such live member on that project (the route's 404). */
166
+ projectMemberDetach(projectId: string, memberId: string): Promise<boolean>;
167
+ /** The projects holding a live `(kind, ref)` membership — the run→project reverse read. */
168
+ memberProjects(kind: string, ref: string): Promise<string[]>;
169
+ /**
170
+ * Durable interaction requests (ADR §5.3), newest first — or `null` when this addon predates
171
+ * the binding (the `runEvents` convention: a missing capability must stay distinguishable from
172
+ * "no open prompts", or the caches' fallback chain misreports upgrades as empty inboxes).
173
+ */
174
+ interactionRequests(sessionId?: string, status?: string): Promise<InteractionRequest[] | null>;
175
+ /** Capture a memory at a STRICT `kind:id[/…]` scope (the engine refuses malformed segments). */
176
+ captureMemory(content: string, scope: string): Promise<void>;
177
+ /** Memories within `scope`'s subtree, newest first — the ADR's memory.coverage probe. */
178
+ listMemories(scope: string, limit: number): Promise<{
179
+ content: string;
180
+ score: number;
181
+ tier: string;
182
+ }[]>;
183
+ /** Ingest a document (title + chunks) into the knowledge store. Returns the chunk count. */
184
+ ingestKnowledge(title: string, chunks: string[]): Promise<number>;
185
+ /** Recall up to `k` knowledge chunks relevant to `query`. */
186
+ recallKnowledge(query: string, k: number): Promise<{
187
+ content: string;
188
+ score: number;
189
+ source: string;
190
+ }[]>;
110
191
  chatOpen(chatId: string, clis: string[], cwd?: string): Promise<{
111
192
  cliKey: string;
112
193
  ok: boolean;
@@ -175,6 +256,19 @@ export declare class CoreAdapter {
175
256
  listConformanceClaims(): Promise<GovernanceClaim[]>;
176
257
  /** Front-half coverage gate report; null when the store has no graph nodes. */
177
258
  getCoverageReport(): Promise<CoverageReport | null>;
259
+ /**
260
+ * Coverage for ONE registered repo, computed over that repo's OWN code graph (FINDING-009). Unlike
261
+ * {@link getCoverageReport} — which reads the daemon store and reports a vacuous `coverage: 1.0` that
262
+ * names no repo — this resolves `repoRef` in the registry and recomputes over its `code_graph_db`.
263
+ * The core rejects an unknown repo (never a silent vacuous report), so this throws for a bad ref.
264
+ */
265
+ getCoverageReportForRepo(repoRef: string): Promise<CoverageReport | null>;
266
+ /**
267
+ * Node-count-by-kind summary of ONE registered repo's code graph, over that repo's OWN store
268
+ * (#122) — what the estate graph actually holds for the repo, so an operator can see it was
269
+ * populated. The core rejects an unknown repo, so this throws for a bad ref.
270
+ */
271
+ getGraphKindsForRepo(repoRef: string): Promise<GraphKind[]>;
178
272
  /** Upsert a governance policy via the single-writer actor. */
179
273
  upsertPolicy(policy: GovernancePolicy): Promise<void>;
180
274
  /** Upsert a conformance rule via the single-writer actor. */
@@ -192,6 +286,17 @@ export declare class CoreAdapter {
192
286
  /** Recall conformance rules matching a facet query (read-only, does not block actor). */
193
287
  recallRulesPreview(query: Record<string, string | string[] | undefined>): Promise<ConformanceRule[]>;
194
288
  private readonly userWorkflows;
289
+ /** Whether {@link hydrateFromOverlay} has run this process lifetime. */
290
+ private overlayHydrated;
291
+ /** Load user-registered workflows persisted to the overlay dir into `userWorkflows`, ONCE.
292
+ *
293
+ * FINDING-002 residual: `registerWorkflow` writes each def to the overlay dir AND to the in-memory
294
+ * `userWorkflows` Map, but the Map is process-local and empty on every daemon restart, and nothing
295
+ * read the dir back. So after a restart the Rust actor (which DOES load the overlay dir at startup)
296
+ * would launch a user workflow that `listWorkflows()`/`GET /workflows` no longer showed — it
297
+ * vanished from the registry while remaining runnable. Hydrating from the same dir the writer uses
298
+ * makes the two views agree again. */
299
+ private hydrateFromOverlay;
195
300
  listWorkflows(): WorkflowDef[];
196
301
  getWorkflow(id: string): WorkflowDef | null;
197
302
  /** Write a built-in workflow definition to the Rust overlay dir (and hot-register when possible).
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/core/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/core/adapter.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { mkdir, access, readFile, writeFile, chmod, rm } from 'node:fs/promises';
3
- import { existsSync, readFileSync, renameSync } from 'node:fs';
3
+ import { existsSync, readdirSync, readFileSync, renameSync } from 'node:fs';
4
4
  import { join, dirname, resolve, isAbsolute, relative, sep } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { homedir } from 'node:os';
@@ -12,6 +12,23 @@ function wickedDir(...parts) {
12
12
  const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
13
13
  return join(home, '.wicked', ...parts);
14
14
  }
15
+ /**
16
+ * Parse a JSON string a napi binding returned. The bindings type their return loosely (`unknown`
17
+ * until a `ts_return_type` regen), so guard BOTH a non-string return and invalid JSON with an error
18
+ * that names the method — a bare `JSON.parse` throw is an unactionable "Unexpected token" (crew#227
19
+ * review). At the current engine contract `raw` is always a valid JSON string.
20
+ */
21
+ function parseEngineJson(raw, method) {
22
+ if (typeof raw !== 'string') {
23
+ throw new Error(`${method}: expected a JSON string from the engine, got ${typeof raw}`);
24
+ }
25
+ try {
26
+ return JSON.parse(raw);
27
+ }
28
+ catch (e) {
29
+ throw new Error(`${method}: engine returned invalid JSON (${e instanceof Error ? e.message : String(e)})`);
30
+ }
31
+ }
15
32
  /**
16
33
  * Workflow overlay directory — mirrors the Rust `workflow_overlay_dir()` logic in
17
34
  * `pipeline.rs`. The Rust actor reads drop-in workflow JSONs from this path at startup
@@ -26,9 +43,45 @@ function workflowOverlayDir() {
26
43
  const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
27
44
  return join(home, '.config', 'wicked-core', 'workflows');
28
45
  }
29
- function settingsFilePath() {
46
+ /** Where the daemon persists system settings. Exported so tests write fixtures to the ONE
47
+ * spelling of this path (the tests/ tree is audited against spelling core paths itself). */
48
+ export function settingsFilePath() {
30
49
  return join(homedir(), '.config', 'wicked-core', 'settings.json');
31
50
  }
51
+ /** Read user-registered workflow overlays from `dir` (the same dir `registerWorkflow` writes to).
52
+ *
53
+ * Skips: files whose id matches a built-in (those are `_writeBuiltinOverlay` artifacts written FOR
54
+ * the Rust actor, not user workflows — including them would duplicate a built-in in `listWorkflows`),
55
+ * non-`.json` files, and any file that does not parse into a `{id, phases[]}` shape (the Rust actor
56
+ * skips an unreadable overlay too, so crew must not surface one it can't). A missing dir yields `[]`.
57
+ *
58
+ * Exported so the FINDING-002 restart-hydration path is unit-testable without spawning a Core. */
59
+ export function readOverlayWorkflows(dir, builtinIds) {
60
+ let files;
61
+ try {
62
+ files = readdirSync(dir);
63
+ }
64
+ catch {
65
+ return []; // no overlay dir yet → nothing registered
66
+ }
67
+ const out = [];
68
+ for (const file of files) {
69
+ if (!file.endsWith('.json'))
70
+ continue;
71
+ const id = file.slice(0, -'.json'.length);
72
+ if (builtinIds.has(id))
73
+ continue; // a built-in overlay, not a user workflow
74
+ try {
75
+ const def = JSON.parse(readFileSync(join(dir, file), 'utf8'));
76
+ if (def && typeof def.id === 'string' && Array.isArray(def.phases))
77
+ out.push(def);
78
+ }
79
+ catch {
80
+ // Unparseable overlay — core would skip it at load, so crew skips it too.
81
+ }
82
+ }
83
+ return out;
84
+ }
32
85
  /** Find the wicked-core standalone binary for the gate-hook command.
33
86
  * Checks common install locations so the Rust actor can build a correct
34
87
  * hook command even when loaded as a napi addon (where current_exe() = node).
@@ -58,6 +111,31 @@ function locateWickedCoreExe() {
58
111
  // `subscribe` seam has a blast radius of exactly one file.
59
112
  const require = createRequire(import.meta.url);
60
113
  const { Core } = require('wicked-core-ts');
114
+ /**
115
+ * Does the installed wicked-core-ts addon understand `LaunchOptions.extraWriteRoots` (≥ 0.6.1)?
116
+ *
117
+ * A napi object silently IGNORES fields the addon doesn't declare, so passing the option to an
118
+ * older addon would launch a run whose boundary never widened — the worker then gets a boundary
119
+ * deny on the very deliverable path the caller declared (the crew#263 failure, resurrected
120
+ * silently). Fail CLOSED on version instead: same doctrine as the `projectId` guard below.
121
+ */
122
+ function addonSupportsExtraWriteRoots() {
123
+ try {
124
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
125
+ const pkg = require('wicked-core-ts/package.json');
126
+ // Match only the numeric MAJOR.MINOR.PATCH prefix: `split('.').map(Number)` returns NaN on
127
+ // pre-release/build suffixes (`0.6.1-beta.1`), which would fail-closed a capable addon
128
+ // (Copilot). No match ⇒ unparseable ⇒ fail closed.
129
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec(pkg.version ?? '');
130
+ if (!m)
131
+ return false;
132
+ const [maj, min, pat] = [Number(m[1]), Number(m[2]), Number(m[3])];
133
+ return maj > 0 || min > 6 || (min === 6 && pat >= 1);
134
+ }
135
+ catch {
136
+ return false;
137
+ }
138
+ }
61
139
  // ── Built-in workflow definitions (crew#44) ──────────────────────────────────
62
140
  // Static mirrors of wicked-core workflow defs: feature, bug, migration, survey-repo,
63
141
  // domain-graph-slice, memories, collab, onboarding, chat, and domain-extraction.
@@ -98,7 +176,7 @@ const CORE_SEEDED_WORKFLOWS = new Set(['feature', 'bug', 'migration', 'onboardin
98
176
  * moves. This is display only: as of FINDING-049 these defs are never written to core's overlay dir
99
177
  * (see CORE_SEEDED_WORKFLOWS), so a stale value here cannot reach the engine.
100
178
  */
101
- const EVIDENCE_FLOOR_PIN = '2fcde907d57f3ee2';
179
+ const EVIDENCE_FLOOR_PIN = 'e2e7af1db9e48454';
102
180
  export const BUILTIN_WORKFLOWS = [
103
181
  {
104
182
  id: 'chat',
@@ -133,7 +211,7 @@ export const BUILTIN_WORKFLOWS = [
133
211
  { id: 'design', kind: 'recon', gate_type: 'strategy', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['clarify'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
134
212
  { id: 'build', kind: 'build', gate_type: 'execution', gate: 'auto', executes_code: true, verified_evidence: false, required_deliverables: [], depends_on: ['design'], role: 'creator', skill_ref: null, allowed_skills: [], validator_pin: null },
135
213
  { id: 'adversarial-review', kind: 'review', gate_type: 'execution', gate: { human_confirm: { unconditional: false } }, executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['build'], role: 'evaluator', skill_ref: null, allowed_skills: [], validator_pin: EVIDENCE_FLOOR_PIN },
136
- { id: 'test', kind: 'test', gate_type: 'execution', gate: { human_confirm_if: 'verdict_not_pass' }, executes_code: false, verified_evidence: true, required_deliverables: [], depends_on: ['build'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
214
+ { id: 'test', kind: 'test', gate_type: 'execution', gate: { human_confirm_if: 'verdict_not_pass' }, executes_code: false, verified_evidence: true, required_deliverables: [], depends_on: ['build'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: EVIDENCE_FLOOR_PIN },
137
215
  { id: 'review', kind: 'review', gate_type: 'execution', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['test'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
138
216
  ],
139
217
  },
@@ -157,12 +235,19 @@ export const BUILTIN_WORKFLOWS = [
157
235
  ],
158
236
  },
159
237
  {
238
+ // MUST stay byte-identical to wicked-core/workflows/survey-repo.json — crew's overlay write is the
239
+ // ONLY def the engine resolves at runtime (core does not seed survey-repo), so a stale mirror here
240
+ // silently runs the OLD def. The pre-fix mirror carried 3 phases with no `instructions` and no
241
+ // `synthesize`, so survey-repo ran 3 near-identical prompts and produced no run-level synthesis —
242
+ // exactly FINDING-011, still live because the fix only landed in the core JSON the runtime ignores.
243
+ // Guarded by builtin-overlay-shadow.test.ts (survey-repo is now in MIRRORED_IDS).
160
244
  id: 'survey-repo',
161
245
  is_system: true,
162
246
  phases: [
163
- { id: 'structure', kind: 'recon', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: [], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
164
- { id: 'stack', kind: 'recon', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['structure'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
165
- { id: 'conventions', kind: 'recon', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['stack'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
247
+ { id: 'structure', kind: 'recon', instructions: 'Map the repository layout only: top-level directories, entry points, and where source, tests, config, and docs live. Do not analyze languages, dependencies, or conventions — later phases cover those.', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: [], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
248
+ { id: 'stack', kind: 'recon', instructions: 'Identify the technology stack from the manifests (package.json, Cargo.toml, pyproject.toml, ...): languages, frameworks, build tools, key dependencies. Build on the structure summary provided as prior context; do not re-map the layout.', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['structure'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
249
+ { id: 'conventions', kind: 'recon', instructions: 'Identify the working conventions: naming, module boundaries, test placement and style, lint/format configuration, CI expectations. Build on the prior phases\' outputs provided as context; do not re-survey structure or stack.', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['stack'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
250
+ { id: 'synthesize', kind: 'recon', instructions: 'Do not re-survey the repository. Merge the three prior phase outputs provided as context into one coherent survey — structure, then stack, then conventions — resolving overlaps and flagging any contradictions between them.', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['structure', 'stack', 'conventions'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
166
251
  ],
167
252
  },
168
253
  {
@@ -211,11 +296,20 @@ export const BUILTIN_WORKFLOWS = [
211
296
  {
212
297
  id: 'domain-extraction',
213
298
  phases: [
214
- { id: 'survey', kind: 'recon', gate_type: null, gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: ['legacy-graph.digest.txt'], depends_on: [], role: 'neutral', skill_ref: 'wicked-garden-domain', allowed_skills: [], validator_pin: null },
215
- { id: 'analyze', kind: 'recon', gate_type: null, gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: ['analysis-report.json'], depends_on: ['survey'], role: 'neutral', skill_ref: 'wicked-garden-domain', allowed_skills: [], validator_pin: null },
216
- { id: 'extract', kind: 'recon', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: ['annotations.jsonl'], depends_on: ['analyze'], role: 'creator', skill_ref: 'wicked-garden-domain-extractor', allowed_skills: [], validator_pin: null },
217
- { id: 'coverage', kind: 'test', gate_type: 'execution', gate: { human_confirm_if: 'verdict_not_pass' }, executes_code: false, verified_evidence: true, required_deliverables: ['coverage-report.json'], depends_on: ['extract'], role: 'evaluator', skill_ref: 'wicked-garden-domain-coverage', allowed_skills: [], validator_pin: 'adaf3e9b6d088f1a' },
218
- { id: 'domain-graph', kind: 'build', gate_type: 'strategy', gate: { human_confirm: { unconditional: false } }, executes_code: false, verified_evidence: false, required_deliverables: ['requirements_graph.json'], depends_on: ['coverage'], role: 'neutral', skill_ref: 'wicked-garden-domain-modeler', allowed_skills: [], validator_pin: null },
299
+ // required_deliverables reconciled with core (wicked-core/workflows/domain-extraction.json):
300
+ // survey/analyze/extract annotate the estate STORE and domain-graph now PERSISTS the graph
301
+ // into the store (not a JSON file), so their evidence is DB state — verified by the coverage
302
+ // gate (reads the store) and domain-graph's fail-closed-on-coverage<1.0 — not a worktree file.
303
+ // Only coverage emits a genuine standalone report the deterministic floor reads. Declaring
304
+ // phantom files failed every phase under core's FINDING-101 deliverable gate.
305
+ { id: 'survey', kind: 'recon', gate_type: null, gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: [], role: 'neutral', skill_ref: 'wicked-garden-domain', allowed_skills: [], validator_pin: null },
306
+ { id: 'analyze', kind: 'recon', gate_type: null, gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['survey'], role: 'neutral', skill_ref: 'wicked-garden-domain', allowed_skills: [], validator_pin: null },
307
+ { id: 'extract', kind: 'recon', gate_type: 'value', gate: 'auto', executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['analyze'], role: 'creator', skill_ref: 'wicked-garden-domain-extractor', allowed_skills: [], validator_pin: null },
308
+ { id: 'coverage', kind: 'test', gate_type: 'execution', gate: { human_confirm_if: 'verdict_not_pass' }, executes_code: false, verified_evidence: true, required_deliverables: ['coverage-report.json'], depends_on: ['extract'], role: 'evaluator', skill_ref: 'wicked-garden-domain-coverage', allowed_skills: [], validator_pin: 'bfe4020a365c598b' },
309
+ // domain-graph is a DETERMINISTIC Tool that runs `wicked-core domain-graph`, which PERSISTS the
310
+ // domain/requirement/rule graph into the repo store (core#237) — not an LLM skill that could hit
311
+ // a non-persisting hermetic fallback. Mirrors wicked-core/workflows/domain-extraction.json.
312
+ { id: 'domain-graph', executor: { type: 'tool', cmd: ['wicked-core', 'domain-graph', '--db', '{code_graph_db}', '--out', 'requirements_graph.json'] }, kind: 'build', gate_type: 'strategy', gate: { human_confirm: { unconditional: false } }, executes_code: false, verified_evidence: false, required_deliverables: [], depends_on: ['coverage'], role: 'neutral', skill_ref: null, allowed_skills: [], validator_pin: null },
219
313
  ],
220
314
  },
221
315
  ];
@@ -237,6 +331,17 @@ export class ChatUnsupportedError extends Error {
237
331
  this.name = 'ChatUnsupportedError';
238
332
  }
239
333
  }
334
+ /**
335
+ * The project surface (DES-PROJECT-001) is not available in this deployment — the installed
336
+ * wicked-core-ts predates 0.6.0. Typed for the same reason as `ChatUnsupportedError`: the routes
337
+ * answer 501 ("upgrade the engine") on this, never 400 ("fix your request").
338
+ */
339
+ export class ProjectsUnsupportedError extends Error {
340
+ constructor(what) {
341
+ super(`${what} is not supported by this wicked-core build (needs wicked-core-ts >= 0.6.0)`);
342
+ this.name = 'ProjectsUnsupportedError';
343
+ }
344
+ }
240
345
  /**
241
346
  * `resolveElicitation` is not available in this deployment — the NAPI binding has not
242
347
  * landed in the installed `wicked-core-ts` yet (DES-002 §4 P-1 stub).
@@ -316,6 +421,21 @@ function quarantineStaleOnboardingOverlay() {
316
421
  * endpoint and the WS fan-out funnel through this stable API — so when the
317
422
  * in-flight core-ts subscribe/teardown signature lands, only this file changes.
318
423
  */
424
+ /** The ids of a workflow's phases that carry a HUMAN gate (`human_confirm` unconditional, or the
425
+ * conditional `human_confirm_if`) — i.e. the phases that will PAUSE for a person.
426
+ *
427
+ * FINDING-023 (residual): core#208 made a workflow's phase gate deliberately WIN over a run-level
428
+ * `humanConfirm: none` (it pauses, with a self-disclosing note), but there was no way to learn a
429
+ * workflow's gates BEFORE launching it — an operator picking `none` for an unattended run only found
430
+ * out when it paused. Surfacing this on `GET /workflows/:id` is that missing launch-time signal.
431
+ * `'auto'` (the string form) is NOT a human gate. */
432
+ export function humanGatePhaseIds(wf) {
433
+ return wf.phases
434
+ .filter((p) => typeof p.gate === 'object' &&
435
+ p.gate !== null &&
436
+ ('human_confirm' in p.gate || 'human_confirm_if' in p.gate))
437
+ .map((p) => p.id);
438
+ }
319
439
  export class CoreAdapter {
320
440
  core;
321
441
  subscription;
@@ -408,6 +528,23 @@ export class CoreAdapter {
408
528
  opts.humanConfirm = input.humanConfirm;
409
529
  if (input.repoRef !== undefined)
410
530
  opts.repoRef = input.repoRef;
531
+ if (input.projectId !== undefined) {
532
+ // Fail CLOSED on an old addon: silently dropping projectId would launch an unfiled run the
533
+ // caller believed was filed — the exact failure §2.2 exists to prevent.
534
+ if (typeof this.core.projectCreate !== 'function') {
535
+ throw new ProjectsUnsupportedError('Filing a run into a project');
536
+ }
537
+ opts.projectId = input.projectId;
538
+ }
539
+ if (input.extraWriteRoots !== undefined && input.extraWriteRoots.length > 0) {
540
+ // Fail CLOSED on an old addon (napi ignores undeclared fields): a silently-dropped
541
+ // widening resurrects the crew#263 boundary deny on the declared deliverable path.
542
+ if (!addonSupportsExtraWriteRoots()) {
543
+ throw new Error('extraWriteRoots needs wicked-core-ts >= 0.6.1; the installed addon would silently ' +
544
+ 'ignore it and the run would be denied writing its own deliverable');
545
+ }
546
+ opts.extraWriteRoots = input.extraWriteRoots;
547
+ }
411
548
  if (input.workflow !== undefined) {
412
549
  opts.workflow = input.workflow;
413
550
  // Ensure DROP-IN workflow definitions are present in the Rust overlay dir on first use.
@@ -442,6 +579,20 @@ export class CoreAdapter {
442
579
  cancelRun(runId) {
443
580
  return this.core.cancelRun(runId);
444
581
  }
582
+ /**
583
+ * Archive (or unarchive) a TERMINAL run (crew#265) — write-off, not delete. Resolves `true`
584
+ * when the session existed, `false` for an unknown id (→ 404); REJECTS when the run is
585
+ * non-terminal (the route answers 409). Fail-closed on an old addon: method-presence guard,
586
+ * same doctrine as `injectWorkerMessage`.
587
+ */
588
+ async archiveRun(runId, archived, note) {
589
+ const archive = this.core.archiveRun;
590
+ if (typeof archive !== 'function') {
591
+ throw new Error('Run archival needs wicked-core-ts >= 0.6.2; this build does not support it');
592
+ }
593
+ // JSON-encoded bool by contract (parse, never truthiness-test — 'false' is truthy).
594
+ return JSON.parse(await archive.call(this.core, runId, archived, note ?? null));
595
+ }
445
596
  /** Inject an operator message into a run's active PTY worker(s). target="all" or a CLI key. */
446
597
  injectWorkerMessage(runId, message, target) {
447
598
  if (typeof this.core.injectWorkerMessage !== 'function') {
@@ -506,6 +657,87 @@ export class CoreAdapter {
506
657
  async workOutput(unitId) {
507
658
  return JSON.parse(await this.core.workOutput(unitId));
508
659
  }
660
+ // ── Projects (DES-PROJECT-001) ──────────────────────────────────────────────
661
+ // 1:1 maps of the 0.6.0 engine surface. Writes ride the single-writer actor;
662
+ // reads are read-only store opens inside the addon. Every method throws
663
+ // ProjectsUnsupportedError on a pre-0.6.0 addon (routes answer 501).
664
+ /** True when the installed addon carries the project surface (0.6.0+). */
665
+ projectsSupported() {
666
+ return typeof this.core.projectCreate === 'function';
667
+ }
668
+ requireProjects(fn, what) {
669
+ if (typeof fn !== 'function')
670
+ throw new ProjectsUnsupportedError(what);
671
+ return fn;
672
+ }
673
+ async projectCreate(name, description) {
674
+ const fn = this.requireProjects(this.core.projectCreate, 'Creating a project');
675
+ return JSON.parse(await fn.call(this.core, name, description ?? null));
676
+ }
677
+ async projectUpdate(id, patch) {
678
+ const fn = this.requireProjects(this.core.projectUpdate, 'Updating a project');
679
+ return JSON.parse(await fn.call(this.core, id, patch.name ?? null, patch.description ?? null, patch.status ?? null));
680
+ }
681
+ /** Every stored project (all statuses, newest first). The `default` project is NOT here — the
682
+ * route layer synthesizes it (ADR §7: computed, never stored). */
683
+ async projectList() {
684
+ const fn = this.requireProjects(this.core.projectList, 'Listing projects');
685
+ return JSON.parse(await fn.call(this.core));
686
+ }
687
+ async projectGet(id) {
688
+ const fn = this.requireProjects(this.core.projectGet, 'Reading a project');
689
+ return JSON.parse(await fn.call(this.core, id));
690
+ }
691
+ async projectMembers(projectId) {
692
+ const fn = this.requireProjects(this.core.projectMembers, 'Listing project members');
693
+ return JSON.parse(await fn.call(this.core, projectId));
694
+ }
695
+ /** Attach a member. `created:false` = the idempotent duplicate hit (emit no event for it). */
696
+ async projectMemberAttach(projectId, kind, ref, meta, attachedBy) {
697
+ const fn = this.requireProjects(this.core.projectMemberAttach, 'Attaching a project member');
698
+ return JSON.parse(await fn.call(this.core, projectId, kind, ref, meta !== undefined ? JSON.stringify(meta) : null, attachedBy ?? null));
699
+ }
700
+ /** Detach (tombstone). `false` = no such live member on that project (the route's 404). */
701
+ async projectMemberDetach(projectId, memberId) {
702
+ const fn = this.requireProjects(this.core.projectMemberDetach, 'Detaching a project member');
703
+ return JSON.parse(await fn.call(this.core, projectId, memberId));
704
+ }
705
+ /** The projects holding a live `(kind, ref)` membership — the run→project reverse read. */
706
+ async memberProjects(kind, ref) {
707
+ const fn = this.requireProjects(this.core.memberProjects, 'Resolving a member’s projects');
708
+ return JSON.parse(await fn.call(this.core, kind, ref));
709
+ }
710
+ /**
711
+ * Durable interaction requests (ADR §5.3), newest first — or `null` when this addon predates
712
+ * the binding (the `runEvents` convention: a missing capability must stay distinguishable from
713
+ * "no open prompts", or the caches' fallback chain misreports upgrades as empty inboxes).
714
+ */
715
+ async interactionRequests(sessionId, status) {
716
+ if (typeof this.core.interactionRequests !== 'function')
717
+ return null;
718
+ return JSON.parse(await this.core.interactionRequests(sessionId ?? null, status ?? null));
719
+ }
720
+ // ── The foundation record (ADR §3.2): charter writes + record probes ────────
721
+ /** Capture a memory at a STRICT `kind:id[/…]` scope (the engine refuses malformed segments). */
722
+ async captureMemory(content, scope) {
723
+ const fn = this.requireProjects(this.core.captureMemory, 'Capturing a memory');
724
+ await fn.call(this.core, content, scope);
725
+ }
726
+ /** Memories within `scope`'s subtree, newest first — the ADR's memory.coverage probe. */
727
+ async listMemories(scope, limit) {
728
+ const fn = this.requireProjects(this.core.listMemories, 'Listing memories');
729
+ return JSON.parse(await fn.call(this.core, scope, limit));
730
+ }
731
+ /** Ingest a document (title + chunks) into the knowledge store. Returns the chunk count. */
732
+ async ingestKnowledge(title, chunks) {
733
+ const fn = this.requireProjects(this.core.ingestKnowledge, 'Ingesting knowledge');
734
+ return JSON.parse(await fn.call(this.core, title, JSON.stringify(chunks)));
735
+ }
736
+ /** Recall up to `k` knowledge chunks relevant to `query`. */
737
+ async recallKnowledge(query, k) {
738
+ const fn = this.requireProjects(this.core.recallKnowledge, 'Recalling knowledge');
739
+ return JSON.parse(await fn.call(this.core, query, k));
740
+ }
509
741
  // ── Chat sessions (core#134 / crew#165) ────────────────────────────────────
510
742
  async chatOpen(chatId, clis, cwd) {
511
743
  const raw = await this.core.chatOpen(chatId, JSON.stringify(clis), cwd ?? null);
@@ -743,6 +975,25 @@ export class CoreAdapter {
743
975
  async getCoverageReport() {
744
976
  return JSON.parse(await this.core.getCoverageReport());
745
977
  }
978
+ /**
979
+ * Coverage for ONE registered repo, computed over that repo's OWN code graph (FINDING-009). Unlike
980
+ * {@link getCoverageReport} — which reads the daemon store and reports a vacuous `coverage: 1.0` that
981
+ * names no repo — this resolves `repoRef` in the registry and recomputes over its `code_graph_db`.
982
+ * The core rejects an unknown repo (never a silent vacuous report), so this throws for a bad ref.
983
+ */
984
+ async getCoverageReportForRepo(repoRef) {
985
+ // The napi binding returns a JSON string (`serde_json::to_string`); parse it with a guard that
986
+ // names the method on either a non-string return or invalid JSON (Copilot #227).
987
+ return parseEngineJson(await this.core.getCoverageReportForRepo(repoRef), 'getCoverageReportForRepo');
988
+ }
989
+ /**
990
+ * Node-count-by-kind summary of ONE registered repo's code graph, over that repo's OWN store
991
+ * (#122) — what the estate graph actually holds for the repo, so an operator can see it was
992
+ * populated. The core rejects an unknown repo, so this throws for a bad ref.
993
+ */
994
+ async getGraphKindsForRepo(repoRef) {
995
+ return parseEngineJson(await this.core.getGraphKindsForRepo(repoRef), 'getGraphKindsForRepo');
996
+ }
746
997
  // ── Governance writes (crew#42) ────────────────────────────────────────────
747
998
  /** Upsert a governance policy via the single-writer actor. */
748
999
  async upsertPolicy(policy) {
@@ -791,7 +1042,28 @@ export class CoreAdapter {
791
1042
  // workflows are added to `userWorkflows` and persisted to disk; the Rust actor
792
1043
  // picks them up via `register_workflow` NAPI (when available) for immediate use.
793
1044
  userWorkflows = new Map();
1045
+ /** Whether {@link hydrateFromOverlay} has run this process lifetime. */
1046
+ overlayHydrated = false;
1047
+ /** Load user-registered workflows persisted to the overlay dir into `userWorkflows`, ONCE.
1048
+ *
1049
+ * FINDING-002 residual: `registerWorkflow` writes each def to the overlay dir AND to the in-memory
1050
+ * `userWorkflows` Map, but the Map is process-local and empty on every daemon restart, and nothing
1051
+ * read the dir back. So after a restart the Rust actor (which DOES load the overlay dir at startup)
1052
+ * would launch a user workflow that `listWorkflows()`/`GET /workflows` no longer showed — it
1053
+ * vanished from the registry while remaining runnable. Hydrating from the same dir the writer uses
1054
+ * makes the two views agree again. */
1055
+ hydrateFromOverlay() {
1056
+ if (this.overlayHydrated)
1057
+ return;
1058
+ this.overlayHydrated = true;
1059
+ const builtinIds = new Set(BUILTIN_WORKFLOWS.map((w) => w.id));
1060
+ for (const def of readOverlayWorkflows(workflowOverlayDir(), builtinIds)) {
1061
+ if (!this.userWorkflows.has(def.id))
1062
+ this.userWorkflows.set(def.id, def);
1063
+ }
1064
+ }
794
1065
  listWorkflows() {
1066
+ this.hydrateFromOverlay();
795
1067
  // Builtins first (stable ordering), but user-registered workflows take precedence when
796
1068
  // ids conflict — consistent with getWorkflow() which prefers userWorkflows.get().
797
1069
  const seen = new Set();
@@ -812,6 +1084,7 @@ export class CoreAdapter {
812
1084
  return result;
813
1085
  }
814
1086
  getWorkflow(id) {
1087
+ this.hydrateFromOverlay();
815
1088
  return this.userWorkflows.get(id) ?? BUILTIN_WORKFLOWS.find((w) => w.id === id) ?? null;
816
1089
  }
817
1090
  /** Write a built-in workflow definition to the Rust overlay dir (and hot-register when possible).
@@ -947,6 +1220,14 @@ export class CoreAdapter {
947
1220
  if (typeof v !== 'number' || !Number.isInteger(v) || v < 1 || v > 10000)
948
1221
  delete parsed.graphNodeLimit;
949
1222
  }
1223
+ // worker_config_root (seat sign-in): absolute path or "" (= engine default). A hand-edited
1224
+ // relative path is dropped rather than exported as WICKED_WORKER_HOME, where the engine
1225
+ // would resolve it against an arbitrary spawn cwd.
1226
+ if ('worker_config_root' in parsed) {
1227
+ const r = parsed.worker_config_root;
1228
+ if (typeof r !== 'string' || (r !== '' && !isAbsolute(r)))
1229
+ delete parsed.worker_config_root;
1230
+ }
950
1231
  return { ...DEFAULT_SETTINGS, ...parsed };
951
1232
  }
952
1233
  catch {