wicked-crew 0.7.0 → 0.7.2

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 (90) hide show
  1. package/README.md +103 -0
  2. package/dist/api/delivery-index.d.ts +61 -0
  3. package/dist/api/delivery-index.d.ts.map +1 -0
  4. package/dist/api/delivery-index.js +87 -0
  5. package/dist/api/delivery-index.js.map +1 -0
  6. package/dist/api/endpoint-manifest-live.d.ts +29 -0
  7. package/dist/api/endpoint-manifest-live.d.ts.map +1 -0
  8. package/dist/api/endpoint-manifest-live.js +72 -0
  9. package/dist/api/endpoint-manifest-live.js.map +1 -0
  10. package/dist/api/endpoint-manifest.d.ts +107 -0
  11. package/dist/api/endpoint-manifest.d.ts.map +1 -0
  12. package/dist/api/endpoint-manifest.js +108 -0
  13. package/dist/api/endpoint-manifest.js.map +1 -0
  14. package/dist/api/governance-wiki.d.ts +28 -0
  15. package/dist/api/governance-wiki.d.ts.map +1 -0
  16. package/dist/api/governance-wiki.js +97 -0
  17. package/dist/api/governance-wiki.js.map +1 -0
  18. package/dist/api/requirements.d.ts +7 -0
  19. package/dist/api/requirements.d.ts.map +1 -1
  20. package/dist/api/requirements.js +23 -2
  21. package/dist/api/requirements.js.map +1 -1
  22. package/dist/api/routes.d.ts +21 -16
  23. package/dist/api/routes.d.ts.map +1 -1
  24. package/dist/api/routes.js +129 -12
  25. package/dist/api/routes.js.map +1 -1
  26. package/dist/api/server.d.ts.map +1 -1
  27. package/dist/api/server.js +106 -5
  28. package/dist/api/server.js.map +1 -1
  29. package/dist/campaign/supervision.d.ts +296 -0
  30. package/dist/campaign/supervision.d.ts.map +1 -0
  31. package/dist/campaign/supervision.js +515 -0
  32. package/dist/campaign/supervision.js.map +1 -0
  33. package/dist/campaigns/plan.d.ts +64 -0
  34. package/dist/campaigns/plan.d.ts.map +1 -0
  35. package/dist/campaigns/plan.js +201 -0
  36. package/dist/campaigns/plan.js.map +1 -0
  37. package/dist/campaigns/routes.d.ts +28 -0
  38. package/dist/campaigns/routes.d.ts.map +1 -0
  39. package/dist/campaigns/routes.js +177 -0
  40. package/dist/campaigns/routes.js.map +1 -0
  41. package/dist/cli/index.js +13 -4
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/core/adapter.d.ts +84 -1
  44. package/dist/core/adapter.d.ts.map +1 -1
  45. package/dist/core/adapter.js +204 -6
  46. package/dist/core/adapter.js.map +1 -1
  47. package/dist/core/deliverable-floor.d.ts +65 -13
  48. package/dist/core/deliverable-floor.d.ts.map +1 -1
  49. package/dist/core/deliverable-floor.js +96 -21
  50. package/dist/core/deliverable-floor.js.map +1 -1
  51. package/dist/interactive/bridge-pool.d.ts +24 -0
  52. package/dist/interactive/bridge-pool.d.ts.map +1 -1
  53. package/dist/interactive/bridge-pool.js +26 -2
  54. package/dist/interactive/bridge-pool.js.map +1 -1
  55. package/dist/interactive/edit-events.d.ts +3 -2
  56. package/dist/interactive/edit-events.d.ts.map +1 -1
  57. package/dist/interactive/edit-events.js +15 -7
  58. package/dist/interactive/edit-events.js.map +1 -1
  59. package/dist/projects/graph-paths.d.ts +32 -2
  60. package/dist/projects/graph-paths.d.ts.map +1 -1
  61. package/dist/projects/graph-paths.js +49 -4
  62. package/dist/projects/graph-paths.js.map +1 -1
  63. package/dist/projects/graph.d.ts +13 -1
  64. package/dist/projects/graph.d.ts.map +1 -1
  65. package/dist/projects/graph.js +73 -19
  66. package/dist/projects/graph.js.map +1 -1
  67. package/dist/projects/routes.d.ts +12 -2
  68. package/dist/projects/routes.d.ts.map +1 -1
  69. package/dist/projects/routes.js +18 -1
  70. package/dist/projects/routes.js.map +1 -1
  71. package/dist/qe/acceptance.d.ts +20 -1
  72. package/dist/qe/acceptance.d.ts.map +1 -1
  73. package/dist/qe/acceptance.js +32 -0
  74. package/dist/qe/acceptance.js.map +1 -1
  75. package/dist/qe/conformance.d.ts +158 -0
  76. package/dist/qe/conformance.d.ts.map +1 -0
  77. package/dist/qe/conformance.js +244 -0
  78. package/dist/qe/conformance.js.map +1 -0
  79. package/dist/qe/ledger.d.ts +3 -2
  80. package/dist/qe/ledger.d.ts.map +1 -1
  81. package/dist/qe/ledger.js +5 -4
  82. package/dist/qe/ledger.js.map +1 -1
  83. package/dist/studio/assets/index-C8rx24F1.js +530 -0
  84. package/dist/studio/assets/index-CwmIj-f2.css +32 -0
  85. package/dist/studio/index.html +2 -2
  86. package/dist/studio/testid-inventory.json +3924 -0
  87. package/endpoint-manifest.json +704 -0
  88. package/package.json +8 -5
  89. package/dist/studio/assets/index-8p8uwCxG.js +0 -530
  90. package/dist/studio/assets/index-D6S9zUtO.css +0 -32
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # wicked-crew
2
+
3
+ **The harness for your agent harnesses** — run the coding-agent CLIs you already use as governed
4
+ workers through durable, multi-agent workflows. Intent in, verified work out: the evaluator is
5
+ structurally not the creator, gates are deny-dominates, and "done" is re-derived from evidence
6
+ instead of asserted.
7
+
8
+ Local-first: loopback only, no accounts, no billing, no model reselling. Crew drives the agents
9
+ *you* already pay for, on *your* auth and *your* plan.
10
+
11
+ ## Install
12
+
13
+ Requires Node.js ≥ 22 and at least one coding-agent CLI on your machine (e.g. Claude Code, Codex).
14
+
15
+ ```bash
16
+ npm install -g wicked-crew
17
+ wicked-crew serve
18
+ ```
19
+
20
+ Or without installing:
21
+
22
+ ```bash
23
+ npx wicked-crew serve
24
+ ```
25
+
26
+ Or use the family installer — [`npx wicked-installer`](https://www.npmjs.com/package/wicked-installer)
27
+ installs/updates the whole wicked-\* family, crew included.
28
+
29
+ `serve` starts the daemon on `http://127.0.0.1:7701` (override with `--port` or `CREW_PORT`) and
30
+ serves the bundled **wicked-studio** browser console same-origin — open the URL and you have the
31
+ control room: launch and steer runs, answer human gates, browse projects and evidence, watch live
32
+ engine events. Durable state (runs, evidence, event log) lives in `~/.wicked-crew/` (`--db`
33
+ overrides).
34
+
35
+ ## Quickstart
36
+
37
+ ```bash
38
+ # 1. Start the daemon (leave it running)
39
+ wicked-crew serve
40
+
41
+ # 2. Launch a governed run — from the console at http://127.0.0.1:7701,
42
+ # or headless over the API:
43
+ curl -X POST http://127.0.0.1:7701/api/v1/runs \
44
+ -H 'content-type: application/json' \
45
+ -d '{"problem": "Add input validation to the signup form", "workflow": "feature"}'
46
+
47
+ # 3. Watch it move through the workflow's phases; approve or reject at the gates.
48
+ ```
49
+
50
+ One run token moves through the workflow's phases with a gate between every one — e.g. for
51
+ `feature`: clarify → design → build → adversarial-review → test → review. Each phase is executed
52
+ by a versioned skill on an assigned CLI worker; the gates decide, and every decision is audited.
53
+
54
+ ## The governed-run model
55
+
56
+ - **You own the workflow; the agent owns the work.** The orchestrator sequences phases and holds
57
+ the gates; the agent does the coding inside a phase.
58
+ - **Workflows are data.** `feature` / `bug` / `migration` ship built-in; new ones are drop-in JSON
59
+ files, not code.
60
+ - **Gates are real, not self-graded.** Phase transitions resolve **deny-dominates** on a
61
+ deterministic structural floor, with an independent evaluator seat that reads cold evidence
62
+ only — a model may fail a gate, never solely approve one.
63
+ - **Evidence, not assertion.** "Done" is re-derived from evidence at the gate, never claimed by
64
+ the agent that did the work.
65
+
66
+ The engine underneath is [wicked-core](https://github.com/mikeparcewski/wicked-core) (Rust,
67
+ single-writer), embedded via the `wicked-core-ts` napi bridge — the daemon is a thin REST+WS
68
+ layer over it.
69
+
70
+ ## The acceptance gate
71
+
72
+ `GET /api/v1/runs/:id/acceptance` answers "does the QE evidence ledger accept this run's work?" —
73
+ crew's machine gate, absorbed from the retired wicked-testing product. <!-- historical --> It reads the repo's
74
+ evidence ledger (a wicked-ledger store at `<repo>/.wicked-qe/`, written by wicked-garden's QE
75
+ skills; legacy `.wicked-testing/` ledgers are still read) and resolves the workflow's acceptance
76
+ requirement **deny-dominates**: only a `PASS` verdict satisfies it. `FAIL`, `CONDITIONAL`,
77
+ `PARTIAL`, `INCONCLUSIVE`, a missing ledger, or a missing verdict each deny with their own named
78
+ reason — no evidence is never a pass. The route always returns 200 for a known run ("no verdict"
79
+ is a real answer about the gate, not an error); `?qeRun=<id>` pins the read to one QE run.
80
+
81
+ ## CLI
82
+
83
+ ```
84
+ wicked-crew serve|start|resume|gate|status|mcp
85
+ ```
86
+
87
+ | command | what it does |
88
+ |---|---|
89
+ | `serve` | run the daemon: REST `/api/v1` + WS `/ws` + the bundled studio console |
90
+ | `start` | boot and launch a run headless (`--problem`, `--workflow`, `--repo`) |
91
+ | `resume` | resume a persisted run (`--session <id>`) |
92
+ | `gate` | answer a pending human gate from the terminal |
93
+ | `status` | inspect run state |
94
+ | `mcp` | stdio MCP server — crew-as-a-tool for coding agents |
95
+
96
+ ## Links
97
+
98
+ - Repo: <https://github.com/mikeparcewski/wicked-crew>
99
+ - Site & docs: <https://wc.wickedagile.com>
100
+ - Wire contract (every `/api/v1` + `/ws` shape, types-only): [`wicked-crew-api-types`](https://www.npmjs.com/package/wicked-crew-api-types)
101
+ - Engine: [wicked-core](https://github.com/mikeparcewski/wicked-core) · Console: [wicked-studio](https://github.com/mikeparcewski/wicked-studio)
102
+
103
+ MIT
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The run→delivered-PR index (CREW-UX-8, crew#321).
3
+ *
4
+ * `LaunchRunBody.deliver?: 'pr'` is input-only; the deliver phase computes the PR URL
5
+ * (`core/deliver.ts` — re-derived from the remote, refused without one) and prints it as the
6
+ * unit's last output line, which is console text, not a wire. This index is the read-side
7
+ * latency layer that lets the run DTOs echo `session.delivery` on BOTH `GET /runs` and
8
+ * `GET /runs/:id` without a `workOutput` read per request — the persisted field that makes a
9
+ * list surface's "4 of 5 siblings delivered" rollup affordable without an N-fetch fan-out
10
+ * (wicked-studio#27).
11
+ *
12
+ * Mirrors `RetryIndex`/`GuidanceIndex`'s posture exactly: the DURABLE record is the audit
13
+ * trail (the engine run record has no such field) — action `run.delivered`, `detail.url` —
14
+ * hydrated once at server start (best-effort: a missing or unreadable trail leaves deliveries
15
+ * blank, the pre-#321 behavior, not an error) and updated at the same post-terminal point
16
+ * that writes the audit entry.
17
+ *
18
+ * KNOWN LIMIT (stated in the contract too): runs that delivered before this landed have no
19
+ * `run.delivered` entry and carry no field. Do NOT backfill by scanning `work_output` at
20
+ * hydrate — that is an unbounded boot cost. Historical runs still resolve their URL through
21
+ * the per-run output endpoint.
22
+ */
23
+ import type { AuditLog } from './audit.js';
24
+ import type { SessionView, WorkUnit } from '../core/types.js';
25
+ /** The wire shape `AgentSession.delivery` carries (api-types 0.11.0). */
26
+ export interface SessionDelivery {
27
+ kind: 'pull_request';
28
+ url: string;
29
+ }
30
+ /**
31
+ * The PR URL in a deliver transcript — crew's own extraction, mirrored
32
+ * (`core/deliver.ts`: `grep -Eo 'https://[^[:space:]]+/pull/[0-9]+' | tail -1`). Requiring
33
+ * the digits keeps `…/pull/new/<branch>` — the create-PR form git prints on every push —
34
+ * from ever matching; the LAST match wins, same as `tail -1`.
35
+ */
36
+ export declare function prUrlFrom(text: string): string | null;
37
+ /**
38
+ * This run's deliver unit, or `null`. The composed id suffix (`<base>:deliver`) is the
39
+ * primary key; the `tool_cmd` probe is the fallback for an operator OVERLAY that carried the
40
+ * deliver phase under its own name — do NOT key on `workflow_id`, which is plain for
41
+ * overlay-carried deliver phases (crew#321).
42
+ */
43
+ export declare function deliverUnitOf(view: SessionView): WorkUnit | null;
44
+ export declare class DeliveryIndex {
45
+ private readonly runToUrl;
46
+ /**
47
+ * Load deliveries from EVERY `run.delivered` entry in the trail — exhaustively, not capped
48
+ * (BRIEF-UX-002 C5, the same defect class as `RetryIndex.hydrate`: a durable record must
49
+ * not vanish because 1000+ newer writes landed on top of it). The trail answers newest
50
+ * first, so the FIRST entry seen per run wins; older entries for the same run are
51
+ * superseded and skipped. Still best-effort, like its siblings; cost is one full-file scan
52
+ * at boot. (Third exhaustive trail scan at boot, after RetryIndex and GuidanceIndex — a
53
+ * FOURTH should trigger consolidating them into one pass, per crew#321.)
54
+ */
55
+ hydrate(audit: AuditLog, log?: (msg: string) => void): Promise<void>;
56
+ /** Record the run's delivered PR URL (idempotent — the newest write wins). */
57
+ set(runId: string, url: string): void;
58
+ /** The delivery for this run, or `undefined` (the DTO spells that as an ABSENT field). */
59
+ deliveryFor(runId: string): SessionDelivery | undefined;
60
+ }
61
+ //# sourceMappingURL=delivery-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery-index.d.ts","sourceRoot":"","sources":["../../src/api/delivery-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGrD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAIhE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD;;;;;;;;OAQG;IACG,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB1E,8EAA8E;IAC9E,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAIrC,0FAA0F;IAC1F,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;CAIxD"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The run→delivered-PR index (CREW-UX-8, crew#321).
3
+ *
4
+ * `LaunchRunBody.deliver?: 'pr'` is input-only; the deliver phase computes the PR URL
5
+ * (`core/deliver.ts` — re-derived from the remote, refused without one) and prints it as the
6
+ * unit's last output line, which is console text, not a wire. This index is the read-side
7
+ * latency layer that lets the run DTOs echo `session.delivery` on BOTH `GET /runs` and
8
+ * `GET /runs/:id` without a `workOutput` read per request — the persisted field that makes a
9
+ * list surface's "4 of 5 siblings delivered" rollup affordable without an N-fetch fan-out
10
+ * (wicked-studio#27).
11
+ *
12
+ * Mirrors `RetryIndex`/`GuidanceIndex`'s posture exactly: the DURABLE record is the audit
13
+ * trail (the engine run record has no such field) — action `run.delivered`, `detail.url` —
14
+ * hydrated once at server start (best-effort: a missing or unreadable trail leaves deliveries
15
+ * blank, the pre-#321 behavior, not an error) and updated at the same post-terminal point
16
+ * that writes the audit entry.
17
+ *
18
+ * KNOWN LIMIT (stated in the contract too): runs that delivered before this landed have no
19
+ * `run.delivered` entry and carry no field. Do NOT backfill by scanning `work_output` at
20
+ * hydrate — that is an unbounded boot cost. Historical runs still resolve their URL through
21
+ * the per-run output endpoint.
22
+ */
23
+ /**
24
+ * The PR URL in a deliver transcript — crew's own extraction, mirrored
25
+ * (`core/deliver.ts`: `grep -Eo 'https://[^[:space:]]+/pull/[0-9]+' | tail -1`). Requiring
26
+ * the digits keeps `…/pull/new/<branch>` — the create-PR form git prints on every push —
27
+ * from ever matching; the LAST match wins, same as `tail -1`.
28
+ */
29
+ export function prUrlFrom(text) {
30
+ const matches = text.match(/https:\/\/\S+\/pull\/\d+/g);
31
+ return matches === null ? null : (matches[matches.length - 1] ?? null);
32
+ }
33
+ /**
34
+ * This run's deliver unit, or `null`. The composed id suffix (`<base>:deliver`) is the
35
+ * primary key; the `tool_cmd` probe is the fallback for an operator OVERLAY that carried the
36
+ * deliver phase under its own name — do NOT key on `workflow_id`, which is plain for
37
+ * overlay-carried deliver phases (crew#321).
38
+ */
39
+ export function deliverUnitOf(view) {
40
+ const byId = view.units.find((u) => u.id.endsWith(':deliver'));
41
+ if (byId !== undefined)
42
+ return byId;
43
+ return view.units.find((u) => (u.tool_cmd ?? []).join(' ').includes('gh pr create')) ?? null;
44
+ }
45
+ export class DeliveryIndex {
46
+ runToUrl = new Map();
47
+ /**
48
+ * Load deliveries from EVERY `run.delivered` entry in the trail — exhaustively, not capped
49
+ * (BRIEF-UX-002 C5, the same defect class as `RetryIndex.hydrate`: a durable record must
50
+ * not vanish because 1000+ newer writes landed on top of it). The trail answers newest
51
+ * first, so the FIRST entry seen per run wins; older entries for the same run are
52
+ * superseded and skipped. Still best-effort, like its siblings; cost is one full-file scan
53
+ * at boot. (Third exhaustive trail scan at boot, after RetryIndex and GuidanceIndex — a
54
+ * FOURTH should trigger consolidating them into one pass, per crew#321.)
55
+ */
56
+ async hydrate(audit, log) {
57
+ try {
58
+ const seen = new Set();
59
+ for (const entry of await audit.readAll({ action: 'run.delivered' })) {
60
+ if (typeof entry.runId !== 'string')
61
+ continue;
62
+ if (seen.has(entry.runId))
63
+ continue;
64
+ // Newest entry decides, even when malformed — marking the run seen BEFORE the url
65
+ // check keeps a corrupt newest write from resurrecting an older one (the #312 rule).
66
+ seen.add(entry.runId);
67
+ const url = entry.detail?.['url'];
68
+ if (typeof url !== 'string' || url === '')
69
+ continue;
70
+ this.runToUrl.set(entry.runId, url);
71
+ }
72
+ }
73
+ catch (err) {
74
+ log?.(`[runs] delivery-index hydrate failed (prior runs read as undelivered until restart): ${err instanceof Error ? err.message : String(err)}`);
75
+ }
76
+ }
77
+ /** Record the run's delivered PR URL (idempotent — the newest write wins). */
78
+ set(runId, url) {
79
+ this.runToUrl.set(runId, url);
80
+ }
81
+ /** The delivery for this run, or `undefined` (the DTO spells that as an ABSENT field). */
82
+ deliveryFor(runId) {
83
+ const url = this.runToUrl.get(runId);
84
+ return url === undefined ? undefined : { kind: 'pull_request', url };
85
+ }
86
+ }
87
+ //# sourceMappingURL=delivery-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery-index.js","sourceRoot":"","sources":["../../src/api/delivery-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACxD,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,IAAI,CAAC;AAC/F,CAAC;AAED,MAAM,OAAO,aAAa;IACP,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtD;;;;;;;;OAQG;IACH,KAAK,CAAC,OAAO,CAAC,KAAe,EAAE,GAA2B;QACxD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,KAAK,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;gBACrE,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;oBAAE,SAAS;gBAC9C,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;oBAAE,SAAS;gBACpC,kFAAkF;gBAClF,qFAAqF;gBACrF,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACtB,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;gBAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE;oBAAE,SAAS;gBACpD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,EAAE,CACH,wFACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,GAAG,CAAC,KAAa,EAAE,GAAW;QAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,0FAA0F;IAC1F,WAAW,CAAC,KAAa;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC;IACvE,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Build the LIVE endpoint manifest by booting the real server assembly (TH-11).
3
+ *
4
+ * A separate module from endpoint-manifest.ts on purpose: this imports `createServer` while
5
+ * server.ts imports the hook installer — folding both into one file would make the cycle
6
+ * structural instead of avoiding it.
7
+ *
8
+ * # Why boot `createServer` rather than re-list routes by hand
9
+ *
10
+ * The manifest's whole value is that it cannot drift from the daemon: it is read from the same
11
+ * `onRoute` hook, over the same registration calls (`registerRoutes`, project routes, terminal
12
+ * WS, `/ws`, interactive event routes), that the served daemon runs. A hand-maintained list would
13
+ * be a second spelling of the route table — the exact thing the manifest exists to end.
14
+ *
15
+ * # What is faked, and why it is safe
16
+ *
17
+ * Route REGISTRATION never calls the engine — only handlers do, and no request is ever injected
18
+ * here. The adapter stub answers the three things `createServer` itself asks at boot:
19
+ * `getSettings()` (worker-config root export), `projectsSupported()` → false (skips membership
20
+ * hydration), and `stub: true` (any answering seam that were ever armed would refuse rather than
21
+ * subscribe). Every optional seam is disabled explicitly, the audit trail goes to a temp file
22
+ * (never `~/.wicked-crew/audit.log`), and `studioRoot` points at a nonexistent directory so the
23
+ * server boots HEADLESS — deliberately: the static wildcard route depends on whether a studio
24
+ * bundle is installed on the generating machine, and a committed manifest must not vary by
25
+ * install state. The manifest documents the API + WS surface.
26
+ */
27
+ import { type EndpointManifest } from './endpoint-manifest.js';
28
+ export declare function collectLiveEndpointManifest(): Promise<EndpointManifest>;
29
+ //# sourceMappingURL=endpoint-manifest-live.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest-live.d.ts","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAmC7E"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Build the LIVE endpoint manifest by booting the real server assembly (TH-11).
3
+ *
4
+ * A separate module from endpoint-manifest.ts on purpose: this imports `createServer` while
5
+ * server.ts imports the hook installer — folding both into one file would make the cycle
6
+ * structural instead of avoiding it.
7
+ *
8
+ * # Why boot `createServer` rather than re-list routes by hand
9
+ *
10
+ * The manifest's whole value is that it cannot drift from the daemon: it is read from the same
11
+ * `onRoute` hook, over the same registration calls (`registerRoutes`, project routes, terminal
12
+ * WS, `/ws`, interactive event routes), that the served daemon runs. A hand-maintained list would
13
+ * be a second spelling of the route table — the exact thing the manifest exists to end.
14
+ *
15
+ * # What is faked, and why it is safe
16
+ *
17
+ * Route REGISTRATION never calls the engine — only handlers do, and no request is ever injected
18
+ * here. The adapter stub answers the three things `createServer` itself asks at boot:
19
+ * `getSettings()` (worker-config root export), `projectsSupported()` → false (skips membership
20
+ * hydration), and `stub: true` (any answering seam that were ever armed would refuse rather than
21
+ * subscribe). Every optional seam is disabled explicitly, the audit trail goes to a temp file
22
+ * (never `~/.wicked-crew/audit.log`), and `studioRoot` points at a nonexistent directory so the
23
+ * server boots HEADLESS — deliberately: the static wildcard route depends on whether a studio
24
+ * bundle is installed on the generating machine, and a committed manifest must not vary by
25
+ * install state. The manifest documents the API + WS surface.
26
+ */
27
+ import { mkdtempSync, rmSync } from 'node:fs';
28
+ import { tmpdir } from 'node:os';
29
+ import { join } from 'node:path';
30
+ import { createServer } from './server.js';
31
+ import { buildEndpointManifest } from './endpoint-manifest.js';
32
+ export async function collectLiveEndpointManifest() {
33
+ // Deterministic boot: auth mode is pinned OFF via options (no env/file resolution), and the
34
+ // logger is silenced for the duration — this runs inside test output and CI logs.
35
+ const savedLogLevel = process.env['LOG_LEVEL'];
36
+ process.env['LOG_LEVEL'] = 'silent';
37
+ const scratch = mkdtempSync(join(tmpdir(), 'crew-endpoint-manifest-'));
38
+ const adapter = {
39
+ stub: true,
40
+ projectsSupported: () => false,
41
+ getSettings: async () => ({}),
42
+ // The daemon's single CoreEvent fan-out subscribes at boot; no event ever arrives here.
43
+ onEvent: () => () => { },
44
+ };
45
+ try {
46
+ const app = await createServer(adapter, {
47
+ auth: { mode: 'off' },
48
+ auditPath: join(scratch, 'audit.log'),
49
+ projectEvents: { disabled: true },
50
+ interactiveWsRelay: { disabled: true },
51
+ seatHealthProbe: { enabled: false },
52
+ stallWatchdog: { enabled: false },
53
+ // Nonexistent on purpose — headless boot; see the module header.
54
+ studioRoot: join(scratch, 'no-studio-bundle'),
55
+ });
56
+ try {
57
+ await app.ready();
58
+ return buildEndpointManifest(app.endpointManifest ?? []);
59
+ }
60
+ finally {
61
+ await app.close();
62
+ }
63
+ }
64
+ finally {
65
+ if (savedLogLevel === undefined)
66
+ delete process.env['LOG_LEVEL'];
67
+ else
68
+ process.env['LOG_LEVEL'] = savedLogLevel;
69
+ rmSync(scratch, { recursive: true, force: true });
70
+ }
71
+ }
72
+ //# sourceMappingURL=endpoint-manifest-live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest-live.js","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAyB,MAAM,wBAAwB,CAAC;AAEtF,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,4FAA4F;IAC5F,kFAAkF;IAClF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC;IACpC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,IAAI;QACV,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC9B,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC7B,wFAAwF;QACxF,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;KACE,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE;YACtC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACrB,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;YACrC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACjC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACtC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACnC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACjC,iEAAiE;YACjE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC;SAC9C,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,qBAAqB,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;;YAC5D,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The endpoint manifest (TH-11) — crew's route table as a MACHINE-READABLE build artifact.
3
+ *
4
+ * # Why this exists
5
+ *
6
+ * routes.ts declares zero fastify schemas (verified: grep `schema:` = 0), no OpenAPI spec exists
7
+ * anywhere in crew, and `wicked-crew-api-types` is types-only with no endpoint-to-type binding —
8
+ * so nothing machine-readable said which methods+paths this daemon serves, what a request body is
9
+ * called, or which status codes a route can answer. Request validation is NOT a vacuum (13
10
+ * `.strict()` zod objects guard the bodies), but zod objects are route-internal: a test harness,
11
+ * a drift check, or an API-test generator had nothing to read. With no fastify schemas declared,
12
+ * `@fastify/swagger` has nothing to generate from either — the `onRoute` hook IS the cheap path.
13
+ *
14
+ * # How it works
15
+ *
16
+ * {@link installEndpointManifestHook} registers a fastify `onRoute` hook (added by `createServer`
17
+ * BEFORE any route registration, so every route the daemon serves is seen) that accumulates one
18
+ * {@link EndpointEntry} per method+path. Two declaration channels feed the type/status fields:
19
+ *
20
+ * 1. `config.manifest` on the route options ({@link ManifestRouteConfig}) — the explicit
21
+ * channel. Names should reference `wicked-crew-api-types` exports where one exists
22
+ * (`LaunchRunBody`, `GateDecision`, …) so the manifest binds endpoints to the published
23
+ * wire contract; a structural spelling (`'{ runId: string }'`) is the honest fallback for
24
+ * wire shapes the contract has no name for.
25
+ * 2. `schema.response` keys, when fastify schemas start existing (the longer-term
26
+ * zod-to-json-schema path) — status codes are folded in automatically, so migrating a route
27
+ * to real fastify schemas enriches the manifest without touching this module.
28
+ *
29
+ * Routes that declare neither still land in the manifest with `requestType`/`responseType` null
30
+ * and `statusCodes: []` — the manifest records "where declared", never invents.
31
+ *
32
+ * # What consumes it
33
+ *
34
+ * - `scripts/generate-endpoint-manifest.ts` (npm run manifest:endpoints) writes the committed
35
+ * `endpoint-manifest.json` at the package root — the build artifact.
36
+ * - `tests/endpoint-manifest.test.ts` rebuilds the live route table and fails on ANY drift from
37
+ * the committed file, so an added/removed/renamed endpoint fails CI until the manifest is
38
+ * regenerated and the diff reviewed. The manifest diff IS the API regression trigger.
39
+ * - `scripts/generate-api-tests.ts` derives positive + 400/404/409 negative API tests from the
40
+ * committed manifest (see `tests/generated/`).
41
+ *
42
+ * HEAD entries are dropped: fastify v5 auto-exposes a HEAD twin for every GET
43
+ * (`exposeHeadRoutes`), which would double the GET surface with rows nobody declared.
44
+ */
45
+ import type { FastifyInstance } from 'fastify';
46
+ /** The explicit per-route declaration channel — `{ config: { manifest: {...} } }` on a route. */
47
+ export interface ManifestRouteConfig {
48
+ /** Request-body type name — a `wicked-crew-api-types` export where one exists. */
49
+ requestType?: string;
50
+ /** Success-response type name, or a structural spelling when the contract has no name for it. */
51
+ responseType?: string;
52
+ /** Every status code this route answers on purpose (success + named error codes). */
53
+ statusCodes?: number[];
54
+ }
55
+ declare module 'fastify' {
56
+ interface FastifyContextConfig {
57
+ /** TH-11 — this route's row in the committed endpoint manifest. */
58
+ manifest?: ManifestRouteConfig;
59
+ }
60
+ interface FastifyInstance {
61
+ /** Accumulated by `createServer`'s onRoute hook; read after `app.ready()`. */
62
+ endpointManifest?: EndpointEntry[];
63
+ }
64
+ }
65
+ /** One method+path the daemon serves. */
66
+ export interface EndpointEntry {
67
+ method: string;
68
+ path: string;
69
+ /** Declared request-body type name, or null — "where declared", never invented. */
70
+ requestType: string | null;
71
+ /** Declared response type name, or null. */
72
+ responseType: string | null;
73
+ /** Declared status codes (config.manifest + fastify schema.response keys), sorted. */
74
+ statusCodes: number[];
75
+ /** Present (true) only on websocket routes (`/ws`, `/ws/terminals/:id`). */
76
+ websocket?: boolean;
77
+ }
78
+ /** The committed artifact's shape (endpoint-manifest.json). */
79
+ export interface EndpointManifest {
80
+ version: 1;
81
+ /**
82
+ * The `wicked-crew-api-types` version this route table was generated against — the published
83
+ * wire contract the type names bind to. Evidence manifests that cite endpoints should carry
84
+ * this (recon R8/R11: api-types drift is live; studio pinned ^0.8.0 against 0.10.0).
85
+ */
86
+ apiTypesVersion: string;
87
+ /** Sorted by path, then method — a stable order so drift diffs are readable. */
88
+ endpoints: EndpointEntry[];
89
+ }
90
+ /**
91
+ * Register the accumulating `onRoute` hook. MUST run before any route is registered — fastify
92
+ * only replays `onRoute` for routes added after the hook exists. Returns the live array the hook
93
+ * appends to; `createServer` exposes it as `app.endpointManifest`.
94
+ */
95
+ export declare function installEndpointManifestHook(app: FastifyInstance): EndpointEntry[];
96
+ /**
97
+ * The published wire contract's version, read from the actually-installed dependency.
98
+ *
99
+ * NOT `require('wicked-crew-api-types/package.json')`: that package's exports map exposes only
100
+ * `.` (types-only, no runtime condition), so both the subpath and a bare resolve are refused.
101
+ * Walking the resolver's candidate directories and reading the file with fs sidesteps the
102
+ * exports map while still honoring the real resolution order (workspace link included).
103
+ */
104
+ export declare function apiTypesVersion(): string;
105
+ /** Entries → the committed artifact: stable order, contract version stamped. */
106
+ export declare function buildEndpointManifest(entries: EndpointEntry[]): EndpointManifest;
107
+ //# sourceMappingURL=endpoint-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest.d.ts","sourceRoot":"","sources":["../../src/api/endpoint-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAE7D,iGAAiG;AACjG,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,oBAAoB;QAC5B,mEAAmE;QACnE,QAAQ,CAAC,EAAE,mBAAmB,CAAC;KAChC;IACD,UAAU,eAAe;QACvB,8EAA8E;QAC9E,gBAAgB,CAAC,EAAE,aAAa,EAAE,CAAC;KACpC;CACF;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,sFAAsF;IACtF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,eAAe,GAAG,aAAa,EAAE,CAgCjF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAKhF"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The endpoint manifest (TH-11) — crew's route table as a MACHINE-READABLE build artifact.
3
+ *
4
+ * # Why this exists
5
+ *
6
+ * routes.ts declares zero fastify schemas (verified: grep `schema:` = 0), no OpenAPI spec exists
7
+ * anywhere in crew, and `wicked-crew-api-types` is types-only with no endpoint-to-type binding —
8
+ * so nothing machine-readable said which methods+paths this daemon serves, what a request body is
9
+ * called, or which status codes a route can answer. Request validation is NOT a vacuum (13
10
+ * `.strict()` zod objects guard the bodies), but zod objects are route-internal: a test harness,
11
+ * a drift check, or an API-test generator had nothing to read. With no fastify schemas declared,
12
+ * `@fastify/swagger` has nothing to generate from either — the `onRoute` hook IS the cheap path.
13
+ *
14
+ * # How it works
15
+ *
16
+ * {@link installEndpointManifestHook} registers a fastify `onRoute` hook (added by `createServer`
17
+ * BEFORE any route registration, so every route the daemon serves is seen) that accumulates one
18
+ * {@link EndpointEntry} per method+path. Two declaration channels feed the type/status fields:
19
+ *
20
+ * 1. `config.manifest` on the route options ({@link ManifestRouteConfig}) — the explicit
21
+ * channel. Names should reference `wicked-crew-api-types` exports where one exists
22
+ * (`LaunchRunBody`, `GateDecision`, …) so the manifest binds endpoints to the published
23
+ * wire contract; a structural spelling (`'{ runId: string }'`) is the honest fallback for
24
+ * wire shapes the contract has no name for.
25
+ * 2. `schema.response` keys, when fastify schemas start existing (the longer-term
26
+ * zod-to-json-schema path) — status codes are folded in automatically, so migrating a route
27
+ * to real fastify schemas enriches the manifest without touching this module.
28
+ *
29
+ * Routes that declare neither still land in the manifest with `requestType`/`responseType` null
30
+ * and `statusCodes: []` — the manifest records "where declared", never invents.
31
+ *
32
+ * # What consumes it
33
+ *
34
+ * - `scripts/generate-endpoint-manifest.ts` (npm run manifest:endpoints) writes the committed
35
+ * `endpoint-manifest.json` at the package root — the build artifact.
36
+ * - `tests/endpoint-manifest.test.ts` rebuilds the live route table and fails on ANY drift from
37
+ * the committed file, so an added/removed/renamed endpoint fails CI until the manifest is
38
+ * regenerated and the diff reviewed. The manifest diff IS the API regression trigger.
39
+ * - `scripts/generate-api-tests.ts` derives positive + 400/404/409 negative API tests from the
40
+ * committed manifest (see `tests/generated/`).
41
+ *
42
+ * HEAD entries are dropped: fastify v5 auto-exposes a HEAD twin for every GET
43
+ * (`exposeHeadRoutes`), which would double the GET surface with rows nobody declared.
44
+ */
45
+ import { existsSync, readFileSync } from 'node:fs';
46
+ import { createRequire } from 'node:module';
47
+ import { join } from 'node:path';
48
+ /**
49
+ * Register the accumulating `onRoute` hook. MUST run before any route is registered — fastify
50
+ * only replays `onRoute` for routes added after the hook exists. Returns the live array the hook
51
+ * appends to; `createServer` exposes it as `app.endpointManifest`.
52
+ */
53
+ export function installEndpointManifestHook(app) {
54
+ const entries = [];
55
+ app.addHook('onRoute', (route) => {
56
+ const methods = Array.isArray(route.method) ? route.method : [route.method];
57
+ const declared = route.config?.manifest;
58
+ // The future-proofing channel: fastify response schemas keyed by status code. None exist
59
+ // today (routes.ts declares zero), but the fold means the zod→fastify-schema migration
60
+ // enriches the manifest for free.
61
+ const schemaResponse = route.schema
62
+ ?.response;
63
+ const schemaCodes = Object.keys(schemaResponse ?? {})
64
+ .map(Number)
65
+ .filter((n) => Number.isInteger(n) && n >= 100 && n <= 599);
66
+ const statusCodes = [...new Set([...(declared?.statusCodes ?? []), ...schemaCodes])].sort((a, b) => a - b);
67
+ const websocket = route.websocket === true;
68
+ for (const method of methods) {
69
+ // Fastify v5 auto-exposes a HEAD twin per GET; recording it would double the GET surface
70
+ // with rows nobody declared and no client calls on purpose.
71
+ if (method === 'HEAD')
72
+ continue;
73
+ entries.push({
74
+ method,
75
+ path: route.url,
76
+ requestType: declared?.requestType ?? null,
77
+ responseType: declared?.responseType ?? null,
78
+ statusCodes,
79
+ ...(websocket ? { websocket: true } : {}),
80
+ });
81
+ }
82
+ });
83
+ return entries;
84
+ }
85
+ /**
86
+ * The published wire contract's version, read from the actually-installed dependency.
87
+ *
88
+ * NOT `require('wicked-crew-api-types/package.json')`: that package's exports map exposes only
89
+ * `.` (types-only, no runtime condition), so both the subpath and a bare resolve are refused.
90
+ * Walking the resolver's candidate directories and reading the file with fs sidesteps the
91
+ * exports map while still honoring the real resolution order (workspace link included).
92
+ */
93
+ export function apiTypesVersion() {
94
+ const require = createRequire(import.meta.url);
95
+ for (const dir of require.resolve.paths('wicked-crew-api-types') ?? []) {
96
+ const pkgPath = join(dir, 'wicked-crew-api-types', 'package.json');
97
+ if (existsSync(pkgPath)) {
98
+ return JSON.parse(readFileSync(pkgPath, 'utf8')).version;
99
+ }
100
+ }
101
+ throw new Error('wicked-crew-api-types is not resolvable from packages/crew — the manifest cannot stamp the wire-contract version');
102
+ }
103
+ /** Entries → the committed artifact: stable order, contract version stamped. */
104
+ export function buildEndpointManifest(entries) {
105
+ const endpoints = [...entries].sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
106
+ return { version: 1, apiTypesVersion: apiTypesVersion(), endpoints };
107
+ }
108
+ //# sourceMappingURL=endpoint-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-manifest.js","sourceRoot":"","sources":["../../src/api/endpoint-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The `/api/v1/governance/wiki/*` surface (wiki-mgmt) — the management wire for the graph-backed
3
+ * architecture wiki, so the wiki is VISIBLE (not just queryable): a scoreboard that tells a
4
+ * populated wiki from an ingested-once-and-decaying one, and a meta probe cheap enough for the
5
+ * UI to call on mount so an empty store shows an honest "nothing seeded — here's the runbook"
6
+ * instead of a blank table.
7
+ *
8
+ * Thin by design, like `campaigns/routes.ts`: the scoreboard is computed by wicked-core's
9
+ * governance layer (`wicked-governance::scoreboard`, read-only `open_store_ro` — safe beside the
10
+ * live single-writer daemon); the daemon keeps no shadow wiki state. Error posture mirrors the
11
+ * campaign surface: a build whose engine addon lacks the `governanceScoreboard` binding answers
12
+ * 501 (`GovernanceScoreboardUnsupportedError` — "upgrade the engine"), never 400 ("fix your
13
+ * request").
14
+ *
15
+ * Rule/ruleset BROWSE and RETIRE deliberately live elsewhere — this module adds no second door:
16
+ * `GET /governance/rules` (facet-filterable, retired rows flagged) is the browse surface and
17
+ * `DELETE /governance/rules/:id` (retire-not-delete, FINDING-038) is the kill switch, both in
18
+ * `api/routes.ts`.
19
+ */
20
+ import type { FastifyInstance } from 'fastify';
21
+ import { type CoreAdapter } from '../core/adapter.js';
22
+ /**
23
+ * The authoring guide the honest empty state points at: the AW-13 seed runbook — frontmatter
24
+ * conventions, the `seed_wiki.py` driver, and the `wicked-core rules` ingest/fanout/relink CLIs.
25
+ */
26
+ export declare const WIKI_AUTHORING_DOC = "https://github.com/mikeparcewski/wicked-core/blob/main/crates/wicked-governance/seed/README.md";
27
+ export declare function registerGovernanceWikiRoutes(app: FastifyInstance, adapter: CoreAdapter): void;
28
+ //# sourceMappingURL=governance-wiki.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance-wiki.d.ts","sourceRoot":"","sources":["../../src/api/governance-wiki.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAM5F;;;GAGG;AACH,eAAO,MAAM,kBAAkB,mGACmE,CAAC;AAMnG,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAwE7F"}