wicked-crew 0.7.1 → 0.7.3

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 (56) hide show
  1. package/dist/api/delivery-index.d.ts +61 -0
  2. package/dist/api/delivery-index.d.ts.map +1 -0
  3. package/dist/api/delivery-index.js +87 -0
  4. package/dist/api/delivery-index.js.map +1 -0
  5. package/dist/api/governance-steering.d.ts +139 -0
  6. package/dist/api/governance-steering.d.ts.map +1 -0
  7. package/dist/api/governance-steering.js +302 -0
  8. package/dist/api/governance-steering.js.map +1 -0
  9. package/dist/api/governance-wiki.d.ts +28 -0
  10. package/dist/api/governance-wiki.d.ts.map +1 -0
  11. package/dist/api/governance-wiki.js +97 -0
  12. package/dist/api/governance-wiki.js.map +1 -0
  13. package/dist/api/routes.d.ts +21 -16
  14. package/dist/api/routes.d.ts.map +1 -1
  15. package/dist/api/routes.js +196 -10
  16. package/dist/api/routes.js.map +1 -1
  17. package/dist/api/server.d.ts.map +1 -1
  18. package/dist/api/server.js +56 -1
  19. package/dist/api/server.js.map +1 -1
  20. package/dist/api/testing.d.ts +117 -0
  21. package/dist/api/testing.d.ts.map +1 -0
  22. package/dist/api/testing.js +152 -0
  23. package/dist/api/testing.js.map +1 -0
  24. package/dist/campaign/supervision.d.ts +296 -0
  25. package/dist/campaign/supervision.d.ts.map +1 -0
  26. package/dist/campaign/supervision.js +515 -0
  27. package/dist/campaign/supervision.js.map +1 -0
  28. package/dist/campaigns/plan.d.ts +64 -0
  29. package/dist/campaigns/plan.d.ts.map +1 -0
  30. package/dist/campaigns/plan.js +201 -0
  31. package/dist/campaigns/plan.js.map +1 -0
  32. package/dist/campaigns/routes.d.ts +28 -0
  33. package/dist/campaigns/routes.d.ts.map +1 -0
  34. package/dist/campaigns/routes.js +177 -0
  35. package/dist/campaigns/routes.js.map +1 -0
  36. package/dist/core/adapter.d.ts +160 -1
  37. package/dist/core/adapter.d.ts.map +1 -1
  38. package/dist/core/adapter.js +300 -1
  39. package/dist/core/adapter.js.map +1 -1
  40. package/dist/projects/routes.d.ts +4 -4
  41. package/dist/qe/acceptance.d.ts +20 -1
  42. package/dist/qe/acceptance.d.ts.map +1 -1
  43. package/dist/qe/acceptance.js +32 -0
  44. package/dist/qe/acceptance.js.map +1 -1
  45. package/dist/qe/conformance.d.ts +158 -0
  46. package/dist/qe/conformance.d.ts.map +1 -0
  47. package/dist/qe/conformance.js +244 -0
  48. package/dist/qe/conformance.js.map +1 -0
  49. package/dist/studio/assets/index-BAW9rqNT.css +32 -0
  50. package/dist/studio/assets/index-UahdRwF7.js +532 -0
  51. package/dist/studio/index.html +2 -2
  52. package/dist/studio/testid-inventory.json +4387 -0
  53. package/endpoint-manifest.json +154 -9
  54. package/package.json +3 -3
  55. package/dist/studio/assets/index-D-BFUYnY.js +0 -530
  56. package/dist/studio/assets/index-D6S9zUtO.css +0 -32
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Scenario → CampaignNode mapping (TH-9; DES-CAMPAIGN-001 §8).
3
+ *
4
+ * `POST /campaigns` accepts a batch of SCENARIOS (the qe-campaign vocabulary: garden's
5
+ * campaign-recon rungs, or any caller's batch of checks) and this module maps them onto the
6
+ * engine's `CampaignDef` — nodes, `on_success`/`on_terminal` edges, and one composed
7
+ * single-phase workflow per deterministic scenario. Pure data-in/data-out: no engine handle,
8
+ * no I/O, so every rule below is unit-testable without spawning a Core.
9
+ *
10
+ * ## The two scenario shapes, and where the work actually lives
11
+ *
12
+ * - **`tool` (deterministic)** — the scenario is a spec FILE (garden's runner spec, a script,
13
+ * any argv-runnable check) and the node's workflow is one Tool-executor phase running that
14
+ * argv verbatim: no council, no LLM, near-zero cost. The engine's Tool path (`run_tool_cmd`)
15
+ * spawns the command directly.
16
+ * - **`agent` (exploratory / authoring)** — a governed agent run over a problem statement;
17
+ * seats are spent only where judgment is the work (test-R9).
18
+ *
19
+ * ## The 1022-byte rule (load-bearing, enforced here)
20
+ *
21
+ * PTY worker prompts longer than 1022 bytes are SILENTLY DISCARDED by the canonical line
22
+ * discipline — the worker waits on input that never arrives and the unit burns its full
23
+ * timeout (the S8 campaign finding; memory: "PTY canonical line limit"). The mapping
24
+ * therefore refuses, loudly and per-token, anything that smells like a scenario BODY inlined
25
+ * where a FILE PATH belongs: every Tool argv token and every agent problem statement must be
26
+ * a single line of at most {@link MAX_INLINE_BYTES} bytes. The fix is always the same and the
27
+ * error says it: persist the content as a file and pass its path.
28
+ */
29
+ /**
30
+ * Byte ceiling for a single inline string handed to a node (a Tool argv token, an agent
31
+ * problem). 1022 is the PTY canonical-line limit — the longest line a worker's stdin
32
+ * accepts before the kernel silently discards it. Tool argv does not itself traverse a PTY,
33
+ * but the ceiling is enforced uniformly because the failure it guards against is the same
34
+ * SHAPE either way: scenario bodies belong in files, and a token this long is a body.
35
+ */
36
+ export const MAX_INLINE_BYTES = 1022;
37
+ /** id rule for campaigns and scenarios: overlay-file-safe, run-id-safe (no `:` — the engine
38
+ * keys node run ids `{campaign}:{node}:a{attempt}`), and short enough that the composed
39
+ * workflow id (`campaign-<campaign>-<scenario>`) stays under core's 128-char id cap. */
40
+ const SAFE_ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
41
+ const MAX_CAMPAIGN_ID = 64;
42
+ const MAX_SCENARIO_ID = 48;
43
+ /** Composed node workflows carry this prefix — the adapter keeps them OUT of the workflow
44
+ * catalog (`listWorkflows`) while still persisting them to the overlay dir so a fresh-process
45
+ * `resumeCampaign` can re-dispatch pending nodes by workflow id. */
46
+ export const CAMPAIGN_WORKFLOW_PREFIX = 'campaign-';
47
+ /** The composed workflow id for one deterministic scenario node. */
48
+ export function scenarioWorkflowId(campaignId, scenarioId) {
49
+ return `${CAMPAIGN_WORKFLOW_PREFIX}${campaignId}-${scenarioId}`;
50
+ }
51
+ /** Throw with the scenario + token named when an inline string breaks the single-line /
52
+ * byte-ceiling rule. One spelling of the rule for both scenario shapes. */
53
+ function assertInline(scenarioId, what, value) {
54
+ if (/[\r\n]/.test(value)) {
55
+ throw new Error(`scenario '${scenarioId}': ${what} contains a newline — scenario bodies are never ` +
56
+ 'inlined; persist the content as a file and pass its path');
57
+ }
58
+ const bytes = Buffer.byteLength(value, 'utf8');
59
+ if (bytes > MAX_INLINE_BYTES) {
60
+ throw new Error(`scenario '${scenarioId}': ${what} is ${bytes} bytes — over the ${MAX_INLINE_BYTES}-byte ` +
61
+ 'PTY canonical-line limit (longer input is SILENTLY DISCARDED and the unit burns its ' +
62
+ 'timeout). Persist the content as a file and pass its path instead');
63
+ }
64
+ }
65
+ /** The single Tool-executor phase of a deterministic scenario node.
66
+ *
67
+ * `verified_evidence: false`, deliberately (contrast `core/deliver.ts`, which arms the built-in
68
+ * evidence floor): that floor re-derives "done" from a WORKTREE DIFF, and a scenario run —
69
+ * probing an API, reading state — legitimately leaves none, so arming it would deny every
70
+ * honest PASS. The executor's exit status is an executor CLAIM, not a verdict; grading belongs
71
+ * to the qe accept trio (garden TH-10) and the acceptance gate re-derives "done" from ledger
72
+ * rows (TH-6). `executes_code: false` for the same reason as the deliver phase: deterministic
73
+ * tooling, not governed agent work.
74
+ */
75
+ function toolPhase(cmd) {
76
+ return {
77
+ id: 'execute',
78
+ kind: 'test',
79
+ executor: { type: 'tool', cmd },
80
+ gate_type: null,
81
+ gate: 'auto',
82
+ executes_code: false,
83
+ verified_evidence: false,
84
+ required_deliverables: [],
85
+ depends_on: [],
86
+ role: 'neutral',
87
+ skill_ref: null,
88
+ allowed_skills: [],
89
+ validator_pin: null,
90
+ };
91
+ }
92
+ /**
93
+ * Map a `POST /campaigns` body onto the engine's `CampaignDef` + the per-node workflows to arm.
94
+ *
95
+ * Throws a plain `Error` (the route answers 400 with the message) on: a malformed campaign or
96
+ * scenario id, a duplicate scenario id, a scenario with neither/both of `tool`/`agent`, an
97
+ * empty argv, an inline-rule break ({@link assertInline}), an unknown/self/duplicate dep, or an
98
+ * `agent.workflow` that is itself a composed campaign workflow. Cycles are LEFT to the engine's
99
+ * own launch validation — one authority for graph shape, and its reject arrives before anything
100
+ * persists.
101
+ *
102
+ * `defaultClis` is the parsed council roster used for every node that does not carry its own —
103
+ * Tool nodes never convene it (the phase bypasses the council), agent nodes do.
104
+ */
105
+ export function buildCampaign(body, defaultClis) {
106
+ const campaignId = body.id ?? `campaign-${Date.now().toString(36)}`;
107
+ if (!SAFE_ID.test(campaignId) || campaignId.length > MAX_CAMPAIGN_ID) {
108
+ throw new Error(`campaign id '${campaignId}' must start with a letter/digit, contain only letters, ` +
109
+ `digits, dots, hyphens, and underscores, and be at most ${MAX_CAMPAIGN_ID} chars`);
110
+ }
111
+ if (body.scenarios.length === 0) {
112
+ throw new Error('a campaign needs at least one scenario');
113
+ }
114
+ const ids = new Set();
115
+ for (const s of body.scenarios) {
116
+ if (!SAFE_ID.test(s.id) || s.id.length > MAX_SCENARIO_ID) {
117
+ throw new Error(`scenario id '${s.id}' must start with a letter/digit, contain only letters, digits, ` +
118
+ `dots, hyphens, and underscores, and be at most ${MAX_SCENARIO_ID} chars`);
119
+ }
120
+ if (ids.has(s.id))
121
+ throw new Error(`duplicate scenario id '${s.id}'`);
122
+ ids.add(s.id);
123
+ }
124
+ const nodes = [];
125
+ const edges = [];
126
+ const workflows = [];
127
+ for (const s of body.scenarios) {
128
+ nodes.push(buildNode(campaignId, s, defaultClis, workflows));
129
+ const seenDeps = new Set();
130
+ for (const dep of s.deps ?? []) {
131
+ if (dep === s.id)
132
+ throw new Error(`scenario '${s.id}' depends on itself`);
133
+ if (!ids.has(dep)) {
134
+ throw new Error(`scenario '${s.id}' depends on unknown scenario '${dep}'`);
135
+ }
136
+ if (seenDeps.has(dep)) {
137
+ throw new Error(`scenario '${s.id}' names dep '${dep}' twice`);
138
+ }
139
+ seenDeps.add(dep);
140
+ edges.push({ from: dep, to: s.id, condition: s.depsCondition ?? 'on_success' });
141
+ }
142
+ }
143
+ return {
144
+ def: {
145
+ id: campaignId,
146
+ name: body.name ?? campaignId,
147
+ nodes,
148
+ edges,
149
+ policy: body.policy ?? 'continue_independent',
150
+ max_concurrency: body.maxConcurrency ?? 2,
151
+ },
152
+ workflows,
153
+ };
154
+ }
155
+ /** One scenario → one node; a `tool` scenario also appends its composed workflow. */
156
+ function buildNode(campaignId, s, defaultClis, workflows) {
157
+ if ((s.tool === undefined) === (s.agent === undefined)) {
158
+ throw new Error(`scenario '${s.id}' must carry exactly one of 'tool' (deterministic argv) or 'agent' ` +
159
+ '(governed problem statement)');
160
+ }
161
+ // The node's problem is a LABEL — what the scoreboard and the run list show — never the work.
162
+ const label = s.title ?? s.id;
163
+ assertInline(s.id, 'title', label);
164
+ if (s.tool !== undefined) {
165
+ if (s.tool.cmd.length === 0) {
166
+ throw new Error(`scenario '${s.id}': tool.cmd is empty — nothing to execute`);
167
+ }
168
+ s.tool.cmd.forEach((token, i) => assertInline(s.id, `tool.cmd[${i}]`, token));
169
+ const wfId = scenarioWorkflowId(campaignId, s.id);
170
+ workflows.push({ id: wfId, phases: [toolPhase(s.tool.cmd)] });
171
+ return {
172
+ node_id: s.id,
173
+ run_spec: {
174
+ problem: label,
175
+ clis: defaultClis,
176
+ entity_mode: 'shared',
177
+ workflow_id: wfId,
178
+ ...(s.repoRef !== undefined ? { repo_ref: s.repoRef } : {}),
179
+ },
180
+ };
181
+ }
182
+ const agent = s.agent;
183
+ assertInline(s.id, 'agent.problem', agent.problem);
184
+ if (agent.workflow !== undefined && agent.workflow.startsWith(CAMPAIGN_WORKFLOW_PREFIX)) {
185
+ // A composed node workflow is private to its node — pointing a second node at one would
186
+ // couple two nodes through an artifact the catalog deliberately hides.
187
+ throw new Error(`scenario '${s.id}': agent.workflow must name a registered workflow, not a composed ` +
188
+ `'${CAMPAIGN_WORKFLOW_PREFIX}*' one`);
189
+ }
190
+ return {
191
+ node_id: s.id,
192
+ run_spec: {
193
+ problem: agent.problem,
194
+ clis: defaultClis,
195
+ entity_mode: 'shared',
196
+ ...(agent.workflow !== undefined ? { workflow_id: agent.workflow } : {}),
197
+ ...(s.repoRef !== undefined ? { repo_ref: s.repoRef } : {}),
198
+ },
199
+ };
200
+ }
201
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/campaigns/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The `/api/v1/campaigns` surface (crew#342 + TH-9; DES-CAMPAIGN-001 §8).
3
+ *
4
+ * Thin by design: validation is zod (strict, unknown keys named — the FINDING-031 doctrine),
5
+ * scenario→node mapping is `plan.ts` (pure), and the scheduler is the ENGINE's (durable,
6
+ * single-writer, crash-resumable) — the daemon keeps no shadow campaign state, so a restarted
7
+ * daemon and a second daemon over the same db answer these routes identically. Campaign
8
+ * progress is NOT polled from here: the 11 `campaign*` CoreEvents ride the daemon's existing
9
+ * allowlist-free `/ws` relay the moment the engine emits them.
10
+ *
11
+ * Error posture mirrors the chat/elicitation surfaces: a build whose engine addon lacks the
12
+ * campaign bindings answers 501 (`CampaignsUnsupportedError` — "upgrade the engine"), never
13
+ * 400 ("fix your request").
14
+ */
15
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
16
+ import { type CoreAdapter } from '../core/adapter.js';
17
+ import type { Actor } from '../core/types.js';
18
+ import type { AuditLog } from '../api/audit.js';
19
+ export interface CampaignRoutesDeps {
20
+ audit: AuditLog;
21
+ actorOf: (req: FastifyRequest & {
22
+ actor?: Actor;
23
+ }) => Actor;
24
+ /** The default council roster for agent scenarios (already parsed). */
25
+ roster: () => unknown[];
26
+ }
27
+ export declare function registerCampaignRoutes(app: FastifyInstance, adapter: CoreAdapter, deps: CampaignRoutesDeps): void;
28
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/campaigns/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,KAAK,EAAsB,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAmDhD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,KAAK,KAAK,CAAC;IAC5D,uEAAuE;IACvE,MAAM,EAAE,MAAM,OAAO,EAAE,CAAC;CACzB;AAED,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,kBAAkB,GACvB,IAAI,CAyIN"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * The `/api/v1/campaigns` surface (crew#342 + TH-9; DES-CAMPAIGN-001 §8).
3
+ *
4
+ * Thin by design: validation is zod (strict, unknown keys named — the FINDING-031 doctrine),
5
+ * scenario→node mapping is `plan.ts` (pure), and the scheduler is the ENGINE's (durable,
6
+ * single-writer, crash-resumable) — the daemon keeps no shadow campaign state, so a restarted
7
+ * daemon and a second daemon over the same db answer these routes identically. Campaign
8
+ * progress is NOT polled from here: the 11 `campaign*` CoreEvents ride the daemon's existing
9
+ * allowlist-free `/ws` relay the moment the engine emits them.
10
+ *
11
+ * Error posture mirrors the chat/elicitation surfaces: a build whose engine addon lacks the
12
+ * campaign bindings answers 501 (`CampaignsUnsupportedError` — "upgrade the engine"), never
13
+ * 400 ("fix your request").
14
+ */
15
+ import { z } from 'zod';
16
+ import { CampaignsUnsupportedError } from '../core/adapter.js';
17
+ import { API_PREFIX } from '../api/api-prefix.js';
18
+ import { buildCampaign } from './plan.js';
19
+ const V = API_PREFIX;
20
+ function message(err) {
21
+ return err instanceof Error ? err.message : String(err);
22
+ }
23
+ /** Same unknown-key-naming 400 body builder as api/routes.ts (restated because that module
24
+ * does not export it and importing routes.ts here would be a cycle through registerRoutes). */
25
+ function invalidBody(err, what) {
26
+ const unknown = err.issues.flatMap((i) => (i.code === 'unrecognized_keys' ? i.keys : []));
27
+ const error = unknown.length > 0
28
+ ? `${what}: unknown field${unknown.length > 1 ? 's' : ''} ${unknown
29
+ .map((k) => `\`${k}\``)
30
+ .join(', ')} — this endpoint does not accept ${unknown.length > 1 ? 'them' : 'it'}, and ignoring ${unknown.length > 1 ? 'them' : 'it'} would run a different request than you sent`
31
+ : what;
32
+ return { error, details: err.issues };
33
+ }
34
+ const ScenarioSchema = z
35
+ .object({
36
+ id: z.string().min(1),
37
+ title: z.string().optional(),
38
+ deps: z.array(z.string()).optional(),
39
+ depsCondition: z.enum(['on_success', 'on_terminal']).optional(),
40
+ tool: z.object({ cmd: z.array(z.string()).min(1) }).strict().optional(),
41
+ agent: z
42
+ .object({ problem: z.string().min(1), workflow: z.string().optional() })
43
+ .strict()
44
+ .optional(),
45
+ repoRef: z.string().optional(),
46
+ })
47
+ .strict();
48
+ const LaunchCampaignSchema = z
49
+ .object({
50
+ id: z.string().optional(),
51
+ name: z.string().max(200).optional(),
52
+ scenarios: z.array(ScenarioSchema).min(1),
53
+ policy: z.enum(['fail_fast', 'continue_independent', 'human_gate_on_failure']).optional(),
54
+ maxConcurrency: z.number().int().min(1).max(64).optional(),
55
+ clisJson: z.string().optional(),
56
+ })
57
+ .strict();
58
+ export function registerCampaignRoutes(app, adapter, deps) {
59
+ app.post(`${V}/campaigns`, {
60
+ config: {
61
+ manifest: {
62
+ requestType: 'LaunchCampaignBody',
63
+ responseType: '{ campaignId: string }',
64
+ // 400: zod reject / mapping reject (inline-byte rule, unknown dep, …) / engine def
65
+ // reject (cycle, empty); 409: campaign id already exists; 501: engine addon lacks
66
+ // the campaign bindings.
67
+ statusCodes: [201, 400, 409, 501],
68
+ },
69
+ },
70
+ }, async (req, reply) => {
71
+ const parsed = LaunchCampaignSchema.safeParse(req.body);
72
+ if (!parsed.success) {
73
+ return reply.code(400).send(invalidBody(parsed.error, 'Invalid request body'));
74
+ }
75
+ const body = parsed.data;
76
+ try {
77
+ const clis = body.clisJson !== undefined ? JSON.parse(body.clisJson) : deps.roster();
78
+ if (!Array.isArray(clis)) {
79
+ return reply
80
+ .code(400)
81
+ .send({ error: 'clisJson must be a JSON array of AgenticCli seats' });
82
+ }
83
+ const built = buildCampaign(body, clis);
84
+ const campaignId = await adapter.launchCampaign(built.def, built.workflows);
85
+ deps.audit.record('campaign.launched', deps.actorOf(req), {
86
+ detail: {
87
+ campaignId,
88
+ nodes: built.def.nodes.length,
89
+ edges: built.def.edges.length,
90
+ policy: built.def.policy,
91
+ maxConcurrency: built.def.max_concurrency,
92
+ },
93
+ });
94
+ return reply.code(201).send({ campaignId });
95
+ }
96
+ catch (err) {
97
+ if (err instanceof CampaignsUnsupportedError) {
98
+ return reply.code(501).send({ error: err.message });
99
+ }
100
+ const msg = message(err);
101
+ // A campaign id that already exists is a state conflict on a real resource.
102
+ if (/already exists|already launched/i.test(msg)) {
103
+ return reply.code(409).send({ error: msg });
104
+ }
105
+ return reply.code(400).send({ error: msg });
106
+ }
107
+ });
108
+ app.get(`${V}/campaigns`, {
109
+ config: {
110
+ manifest: { responseType: '{ campaigns: Campaign[] }', statusCodes: [200, 501] },
111
+ },
112
+ }, async (_req, reply) => {
113
+ try {
114
+ return { campaigns: await adapter.campaignList() };
115
+ }
116
+ catch (err) {
117
+ if (err instanceof CampaignsUnsupportedError) {
118
+ return reply.code(501).send({ error: err.message });
119
+ }
120
+ throw err;
121
+ }
122
+ });
123
+ app.get(`${V}/campaigns/:id`, {
124
+ config: {
125
+ manifest: { responseType: '{ campaign: Campaign }', statusCodes: [200, 404, 501] },
126
+ },
127
+ }, async (req, reply) => {
128
+ const { id } = req.params;
129
+ try {
130
+ const campaign = await adapter.campaignDetail(id);
131
+ if (campaign === null) {
132
+ return reply.code(404).send({ error: `unknown campaign: ${id}` });
133
+ }
134
+ return { campaign };
135
+ }
136
+ catch (err) {
137
+ if (err instanceof CampaignsUnsupportedError) {
138
+ return reply.code(501).send({ error: err.message });
139
+ }
140
+ throw err;
141
+ }
142
+ });
143
+ // Resume / cancel share one wrapper: both resolve the campaign status token, both 404 on an
144
+ // id the engine does not know, and both are actor-audited state transitions.
145
+ const transition = (action, run) => {
146
+ app.post(`${V}/campaigns/:id/${action}`, {
147
+ config: {
148
+ manifest: {
149
+ responseType: '{ campaignId: string; status: CampaignStatus }',
150
+ statusCodes: [200, 404, 409, 501],
151
+ },
152
+ },
153
+ }, async (req, reply) => {
154
+ const { id } = req.params;
155
+ try {
156
+ const status = await run(id);
157
+ deps.audit.record(`campaign.${action === 'resume' ? 'resumed' : 'cancelled'}`, deps.actorOf(req), {
158
+ detail: { campaignId: id, status },
159
+ });
160
+ return { campaignId: id, status };
161
+ }
162
+ catch (err) {
163
+ if (err instanceof CampaignsUnsupportedError) {
164
+ return reply.code(501).send({ error: err.message });
165
+ }
166
+ const msg = message(err);
167
+ if (/not found|unknown campaign/i.test(msg)) {
168
+ return reply.code(404).send({ error: msg });
169
+ }
170
+ return reply.code(409).send({ error: msg });
171
+ }
172
+ });
173
+ };
174
+ transition('resume', (id) => adapter.resumeCampaign(id));
175
+ transition('cancel', (id) => adapter.cancelCampaign(id));
176
+ }
177
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/campaigns/routes.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { CoreEvent, LaunchRunInput, RepoEntry, RepoOnboardRef, SessionView, RecordedEvent, GovernancePolicy, ConformanceRule, GovernanceClaim, CoverageReport, GraphKind, WorkflowDef, CrewSystemSettings, Project, ProjectMember, InteractionRequest } from './types.js';
1
+ import type { CoreEvent, LaunchRunInput, RepoEntry, RepoOnboardRef, SessionView, RecordedEvent, GovernancePolicy, ConformanceRule, GovernanceScoreboard, GovernanceClaim, SteeringImportEntry, SteeringImportResult, GovernanceEvalReport, GovernanceEvalSample, ImportEvalCorpusResponse, CoverageReport, GraphKind, WorkflowDef, CrewSystemSettings, Project, ProjectMember, InteractionRequest, Campaign, CampaignDef } from './types.js';
2
2
  /** Where the daemon persists system settings. Exported so tests write fixtures to the ONE
3
3
  * spelling of this path (the tests/ tree is audited against spelling core paths itself). */
4
4
  export declare function settingsFilePath(): string;
@@ -45,6 +45,47 @@ export declare class ProjectsUnsupportedError extends Error {
45
45
  export declare class ElicitationUnsupportedError extends Error {
46
46
  constructor(message: string);
47
47
  }
48
+ /**
49
+ * The campaign surface (DES-CAMPAIGN-001 / TH-9) is not available in this deployment — the
50
+ * installed wicked-core-ts predates the campaign bindings. Typed for the same reason as
51
+ * `ChatUnsupportedError`: the routes answer 501 ("upgrade the engine") on this, never 400
52
+ * ("fix your request"). Gated on METHOD PRESENCE rather than a version floor because the
53
+ * bindings ship in the next core-ts release whose number is the release train's to pick —
54
+ * a presence check is true on whatever version actually carries them.
55
+ */
56
+ export declare class CampaignsUnsupportedError extends Error {
57
+ constructor(what: string);
58
+ }
59
+ /**
60
+ * The wiki scoreboard (AW-23 / wiki-mgmt) is not available in this deployment — the installed
61
+ * wicked-core-ts predates the `governanceScoreboard` binding (ships in ≥ 0.7.4). Typed for the
62
+ * same reason as `ChatUnsupportedError`: the route answers 501 ("upgrade the engine") on this,
63
+ * never 400 ("fix your request"). Gated on METHOD PRESENCE, the campaigns doctrine — true on
64
+ * whatever release actually carries the binding.
65
+ */
66
+ /**
67
+ * The unified steering-rule model (STEERING program) is not available in this deployment —
68
+ * wicked-core-ts predates the `steeringImport` binding (ships in ≥ 0.7.5, alongside the model
69
+ * merge). Typed for the same reason as `ChatUnsupportedError`: the routes answer 501 ("upgrade
70
+ * the engine") on this, never 400 ("fix your request") — and never the WORSE failure of passing
71
+ * a steering-field write through to an engine that would silently drop the fields.
72
+ */
73
+ export declare class SteeringUnsupportedError extends Error {
74
+ constructor(what: string);
75
+ }
76
+ /**
77
+ * The installed engine addon predates the governance-evals surface (crew-testing; the
78
+ * `governanceEvals` + `governanceCorpusImport` bindings ship with wicked-core-ts 0.7.5, which is
79
+ * unreleased at the time this seam lands). Typed for the same reason as `SteeringUnsupportedError`:
80
+ * the testing routes answer 501 ("upgrade the engine") on this, never 400 ("fix your request") —
81
+ * and they must not crash a daemon running against the released 0.7.4 addon.
82
+ */
83
+ export declare class GovernanceEvalsUnsupportedError extends Error {
84
+ constructor(what: string);
85
+ }
86
+ export declare class GovernanceScoreboardUnsupportedError extends Error {
87
+ constructor(what: string);
88
+ }
48
89
  /** One live chat on the enumerate surface — see {@link CoreAdapter.chatList}. */
49
90
  export interface ChatSummary {
50
91
  chatId: string;
@@ -99,6 +140,14 @@ export declare class CoreAdapter {
99
140
  private closed;
100
141
  /** Built-in workflow ids whose overlay JSON has been written this process lifetime. */
101
142
  private readonly _builtinOverlayWritten;
143
+ /**
144
+ * The estate db the Core actor was spawned over (`CoreAdapterOptions.dbPath` — the `--db`
145
+ * flag / `~/.wicked-crew/core.db` default). Kept because some engine bindings take the db
146
+ * path as an ARG rather than reading the actor's own handle (`governanceEvals` runs its evals
147
+ * over a read-only connection to this same store); crew resolves it here, the one place the
148
+ * path is already known, so no route grows a second spelling of where the store lives.
149
+ */
150
+ readonly dbPath: string;
102
151
  /** `true` when this adapter armed the event-driven exec seam (for readiness/reporting). */
103
152
  readonly engineExec: boolean;
104
153
  /** The bus db the exec seam runs over when armed (else `undefined`). */
@@ -130,6 +179,32 @@ export declare class CoreAdapter {
130
179
  confirmGate(runId: string, approve: boolean, amend?: string): Promise<string>;
131
180
  /** Cancel a run → the status token. */
132
181
  cancelRun(runId: string): Promise<string>;
182
+ /** Whether the installed engine addon carries the campaign bindings. */
183
+ campaignsSupported(): boolean;
184
+ /** Guard: the campaign binding surface, or a typed 501-shaped refusal naming the action. */
185
+ private _campaigns;
186
+ /** Validate + launch a campaign DAG → the campaign id. `def` is the ENGINE wire shape
187
+ * (snake_case) — `campaigns/plan.ts` is the one producer of it in this daemon.
188
+ *
189
+ * `workflows` are the composed per-node Tool workflows the def's run_specs reference; they
190
+ * are armed FIRST (hot-register + overlay persist) so the engine can resolve them both now
191
+ * and after a restart — a campaign is durable, so its node workflows must be too. Unlike a
192
+ * deliver-composed def they are NOT purely ephemeral (`_armPerRunWorkflow`), and unlike a
193
+ * user workflow they never enter `userWorkflows` or the catalog (the hydration filter in
194
+ * `readOverlayWorkflows` skips the `campaign-` prefix). */
195
+ launchCampaign(def: CampaignDef, workflows?: WorkflowDef[]): Promise<string>;
196
+ /** Arm one composed campaign-node workflow: validate-then-persist (the FINDING-002 ordering —
197
+ * core's parser is the authority and runs BEFORE the overlay write), overlay file for
198
+ * restart durability, no `userWorkflows` entry. */
199
+ private _armCampaignWorkflow;
200
+ /** Resume a campaign from its persisted state → the campaign status token. */
201
+ resumeCampaign(id: string): Promise<string>;
202
+ /** Cancel a campaign (in-flight node Runs cancelled, the rest marked) → the status token. */
203
+ cancelCampaign(id: string): Promise<string>;
204
+ /** A campaign's full persisted state (DAG + node statuses + run ids), or `null` when unknown. */
205
+ campaignDetail(id: string): Promise<Campaign | null>;
206
+ /** Every campaign on the store. */
207
+ campaignList(): Promise<Campaign[]>;
133
208
  /**
134
209
  * Archive (or unarchive) a TERMINAL run (crew#265) — write-off, not delete. Resolves `true`
135
210
  * when the session existed, `false` for an unknown id (→ 404); REJECTS when the run is
@@ -297,6 +372,90 @@ export declare class CoreAdapter {
297
372
  retireConformanceRule(id: string): Promise<boolean>;
298
373
  /** Recall conformance rules matching a facet query (read-only, does not block actor). */
299
374
  recallRulesPreview(query: Record<string, string | string[] | undefined>): Promise<ConformanceRule[]>;
375
+ /** Whether the installed engine addon carries the AW-23 scoreboard binding (core-ts ≥ 0.7.4). */
376
+ wikiScoreboardSupported(): boolean;
377
+ /**
378
+ * The wiki population/connection scoreboard (AW-23 / arch-R23) — typed %, resolving %,
379
+ * enforcement evidence, and an in-band honesty marker for what cannot be measured. Read-only
380
+ * on the engine side (`open_store_ro`), so it never blocks the single-writer actor.
381
+ *
382
+ * `docsRoot` is the same docs root `rules ingest --dir` used; omitted, the typing half reports
383
+ * `available: false` with the reason — an honest in-band answer, not an error.
384
+ *
385
+ * Presence-gated (the campaigns doctrine): throws {@link GovernanceScoreboardUnsupportedError}
386
+ * on an addon that predates the binding, which the route maps to 501 ("upgrade the engine").
387
+ */
388
+ governanceScoreboard(docsRoot?: string): Promise<GovernanceScoreboard>;
389
+ /**
390
+ * How many `RuleSet` rows (doctrine domain parents, AW-13) the store holds — or `null` when
391
+ * this engine build cannot count them (no `listRuleSets` binding yet).
392
+ *
393
+ * `null` rather than `0` on purpose (the `runEvents` doctrine): "no rulesets" is a real answer
394
+ * about an unseeded store, and "I cannot count" must never impersonate it — the wiki meta's
395
+ * empty state would otherwise report a seeded store as empty and point the operator at a
396
+ * runbook they already ran.
397
+ */
398
+ countRuleSets(): Promise<number | null>;
399
+ /**
400
+ * Whether the installed engine addon carries the unified steering-rule model (core-ts ≥ 0.7.5).
401
+ *
402
+ * The sentinel is the `steeringImport` binding, which ships WITH the model merge — see the
403
+ * `GovernanceMethods` comment. Consulted by the routes before: filtering rules by
404
+ * `?type=` (rows on an old engine carry no `steering_type`, so an empty answer would
405
+ * impersonate "no rules of that type"); accepting a rule write that carries steering fields
406
+ * (an old engine would silently drop them — the extraWriteRoots doctrine); and folding the
407
+ * legacy policy write surface (410 only once the unified store exists to point at).
408
+ */
409
+ steeringSupported(): boolean;
410
+ /**
411
+ * Batch-import steering rules (frontmattered markdown docs and/or ready rule JSON) through the
412
+ * engine's ingest normalize/validate path — the SAME path `rules ingest --dir` runs, on the
413
+ * single-writer actor (the daemon must never open the store for writes itself). Fail-closed
414
+ * PER ENTRY: one bad entry rejects alone with its reason in that entry's result row, the rest
415
+ * still land. `defaultType` is the page's inferred steering type, applied to entries that
416
+ * omit one.
417
+ *
418
+ * Presence-gated (the campaigns doctrine): throws {@link SteeringUnsupportedError} on an addon
419
+ * that predates the binding, which the route maps to 501 ("upgrade the engine").
420
+ */
421
+ importSteeringRules(entries: SteeringImportEntry[], defaultType?: string): Promise<SteeringImportResult[]>;
422
+ /**
423
+ * Whether the installed engine addon carries the governance-evals surface (core-ts ≥ 0.7.5,
424
+ * unreleased at the time this seam lands). The sentinel is the `governanceEvals` binding —
425
+ * for the WHOLE evals seam, corpus import included: the two bindings ship together, and a
426
+ * daemon running against the released 0.7.4 addon must answer 501 on both routes, never crash.
427
+ */
428
+ governanceEvalsSupported(): boolean;
429
+ /**
430
+ * Run the governance evals: every sample of the corpus (`corpus` = an `evals:<name>` estate
431
+ * scope minted by {@link importGovernanceCorpus}; omitted = the engine's built-in default
432
+ * corpus) is pushed through the decide path and compared to what its `kind` says SHOULD have
433
+ * happened. Resolves the engine's serde report — snake_case field names, passed to the wire
434
+ * verbatim (the pinned crew/studio contract).
435
+ *
436
+ * `dbPath` rides the args because the eval run opens its own read-only connection to the
437
+ * steering store — the SAME store this adapter's actor writes (`this.dbPath`, the one spelling
438
+ * of where it lives). `knowledgeDb` is deliberately omitted: crew configures no separate
439
+ * knowledge db (the knowledge store is the engine's single-writer sidecar, derived from its
440
+ * own config exactly as the steering ingest path derives it).
441
+ *
442
+ * Presence-gated (the campaigns doctrine): throws {@link GovernanceEvalsUnsupportedError} on
443
+ * an addon that predates the binding, which the route maps to 501 ("upgrade the engine").
444
+ */
445
+ runGovernanceEvals(args: {
446
+ type?: string;
447
+ corpus?: string;
448
+ }): Promise<GovernanceEvalReport>;
449
+ /**
450
+ * Import a named eval corpus into the knowledge store under the `evals:<name>` scope (the
451
+ * string a later {@link runGovernanceEvals} names as `corpus`). `knowledgeDb` is omitted for
452
+ * the same reason as in {@link runGovernanceEvals} — the engine derives its own sidecar.
453
+ *
454
+ * Gated on the `governanceEvals` sentinel as well as the binding itself: the two ship
455
+ * together, and importing a corpus no engine on this host can ever run would be a trap, not
456
+ * a feature (the steering author-route doctrine).
457
+ */
458
+ importGovernanceCorpus(name: string, samples: GovernanceEvalSample[]): Promise<ImportEvalCorpusResponse>;
300
459
  private readonly userWorkflows;
301
460
  /** Whether {@link hydrateFromOverlay} has run this process lifetime. */
302
461
  private overlayHydrated;