loki-mode 10.7.1 → 10.9.1

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 (185) hide show
  1. package/README.md +2 -2
  2. package/SKILL.md +2 -2
  3. package/VERSION +1 -1
  4. package/autonomy/loki +20 -32
  5. package/autonomy/run.sh +9 -5
  6. package/autonomy/tui.sh +5 -1
  7. package/dashboard/__init__.py +1 -1
  8. package/dashboard/api_phases.py +1 -1
  9. package/dashboard/api_start.py +0 -1
  10. package/dashboard/server.py +28 -146
  11. package/docs/COCKPIT-SPEC.md +3 -1
  12. package/docs/DASHBOARD-9.12-EVIDENCE.md +1 -1
  13. package/docs/DASHBOARD-ARCHITECTURE.md +13 -11
  14. package/docs/DASHBOARD_V2_CHECKLIST.md +2 -0
  15. package/docs/INSTALLATION.md +12 -11
  16. package/docs/MERGE-DEDUP-MAP.md +1 -1
  17. package/docs/MERGE-ROUTE-MAP.md +1 -1
  18. package/docs/MERGE3-PLAN.md +2 -2
  19. package/docs/R3-COST-OBSERVABILITY-DESIGN.md +2 -2
  20. package/docs/R4-TRUST-TRAJECTORY-DESIGN.md +1 -1
  21. package/docs/R5-AUTO-WIKI-DESIGN.md +1 -1
  22. package/docs/R6-ROLLBACK-CHECKPOINT-PLAN.md +1 -1
  23. package/docs/RARV-C-100X-PLAN.md +3 -3
  24. package/docs/SONNET5-DEFAULT-PLAN.md +2 -2
  25. package/docs/SYNERGY-TASKS.md +1 -1
  26. package/docs/TOOL-INTEGRATION.md +1 -1
  27. package/docs/TOP-100-BACKLOG.md +1 -1
  28. package/docs/alternative-installations.md +1 -1
  29. package/docs/architecture/ADR-001-runtime-migration.md +1 -1
  30. package/docs/architecture/DASHBOARD_V2_ARCHITECTURE.md +2 -0
  31. package/docs/audit-logging.md +2 -0
  32. package/docs/authentication.md +2 -0
  33. package/docs/authorization.md +2 -0
  34. package/docs/certification/02-enterprise-features/lab.md +2 -0
  35. package/docs/certification/02-enterprise-features/lesson.md +2 -0
  36. package/docs/certification/04-production-deployment/lab.md +2 -0
  37. package/docs/certification/04-production-deployment/lesson.md +2 -0
  38. package/docs/control-plane-migration.md +97 -0
  39. package/docs/dashboard-guide.md +6 -7
  40. package/docs/dev/release-checklist.md +9 -11
  41. package/docs/enterprise/integration-cookbook.md +2 -0
  42. package/docs/enterprise/migration.md +1 -1
  43. package/docs/enterprise/performance.md +3 -1
  44. package/docs/enterprise/sdk-guide.md +2 -0
  45. package/docs/enterprise/security.md +2 -0
  46. package/docs/plans/FORGE-AUTONOMOUS-QUEUE.md +2 -2
  47. package/docs/research-2026-07/OBSERVABILITY-TOOL-SPEC.md +1 -1
  48. package/docs/retrospectives/v7.5.15-honesty-audit.md +2 -2
  49. package/docs/test-scenarios/enterprise-scenarios.md +2 -2
  50. package/docs/v10/ARCHITECT-CUTS.md +9 -9
  51. package/docs/v10/BACKLOG.md +7 -7
  52. package/docs/v10/BOARD.md +25 -25
  53. package/docs/v10/CONTROL-PLANE.md +4 -4
  54. package/docs/v10/CP-ENTERPRISE-UI.md +270 -9
  55. package/docs/v10/D51-PHASE-B.md +1 -1
  56. package/docs/v10/DECISIONS.md +3 -3
  57. package/docs/v10/DEPS.md +10 -10
  58. package/docs/v10/FAILURE-CLASSES.md +66 -1
  59. package/docs/v10/LEGACY-REMOVAL.md +2 -2
  60. package/docs/v10/METRICS.md +6 -0
  61. package/docs/v10/SWARM.md +2 -2
  62. package/loki-ts/data/model-pricing.json +6 -6
  63. package/loki-ts/dist/cockpit.js +11 -11
  64. package/loki-ts/dist/loki.js +819 -809
  65. package/loki-ts/package.json +1 -1
  66. package/magic/core/design_tokens.py +1 -2
  67. package/magic/core/generator.py +2 -2
  68. package/magic/tokens/README.md +1 -3
  69. package/mcp/__init__.py +1 -1
  70. package/package.json +3 -8
  71. package/packages/control-plane/dist/server.js +17954 -5696
  72. package/packages/control-plane/drizzle/0004_cpe03_local_repos_actions.sql +17 -0
  73. package/packages/control-plane/drizzle/meta/0004_snapshot.json +542 -0
  74. package/packages/control-plane/drizzle/meta/_journal.json +9 -2
  75. package/packages/control-plane/package.json +4 -1
  76. package/packages/control-plane/src/db/schema.ts +19 -0
  77. package/packages/control-plane/src/server/app.ts +11 -22
  78. package/packages/control-plane/src/server/audit.ts +15 -0
  79. package/packages/control-plane/src/server/auth.ts +7 -1
  80. package/packages/control-plane/src/server/legacy/routes-audit.ts +50 -0
  81. package/packages/control-plane/src/server/legacy/routes-control.ts +26 -0
  82. package/packages/control-plane/src/server/legacy/routes-memory.ts +30 -0
  83. package/packages/control-plane/src/server/legacy/routes-metrics.ts +18 -0
  84. package/packages/control-plane/src/server/legacy/routes.ts +237 -0
  85. package/packages/control-plane/src/server/legacy/shim.ts +201 -0
  86. package/packages/control-plane/src/server/repos.ts +27 -0
  87. package/packages/control-plane/src/server/routes/artifacts.ts +104 -0
  88. package/packages/control-plane/src/server/routes/audit.ts +17 -0
  89. package/packages/control-plane/src/server/routes/checkpoints.ts +171 -0
  90. package/packages/control-plane/src/server/routes/config.ts +319 -0
  91. package/packages/control-plane/src/server/routes/control.ts +75 -0
  92. package/packages/control-plane/src/server/routes/cost.ts +70 -0
  93. package/packages/control-plane/src/server/routes/cost_ledger.ts +210 -0
  94. package/packages/control-plane/src/server/routes/fleet.ts +101 -0
  95. package/packages/control-plane/src/server/routes/index.ts +71 -0
  96. package/packages/control-plane/src/server/routes/integrations.ts +123 -0
  97. package/packages/control-plane/src/server/routes/memory.ts +219 -0
  98. package/packages/control-plane/src/server/routes/merge_risk.ts +209 -0
  99. package/packages/control-plane/src/server/routes/metrics.ts +93 -0
  100. package/packages/control-plane/src/server/routes/notify.ts +48 -0
  101. package/packages/control-plane/src/server/routes/providers.ts +111 -0
  102. package/packages/control-plane/src/server/routes/session_control.ts +155 -0
  103. package/packages/control-plane/src/server/routes/start.ts +39 -0
  104. package/packages/control-plane/src/server/routes/stats.ts +76 -0
  105. package/packages/control-plane/src/server/routes/stream.ts +85 -0
  106. package/packages/control-plane/src/server/routes/verify.ts +66 -0
  107. package/packages/control-plane/src/server/spawn.ts +79 -0
  108. package/packages/control-plane/src/shipper/discover.ts +19 -1
  109. package/packages/control-plane/ui/dist/assets/index-BQbFv65M.css +1 -0
  110. package/packages/control-plane/ui/dist/assets/index-Dbhu244h.js +155 -0
  111. package/packages/control-plane/ui/dist/index.html +2 -2
  112. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  113. package/references/magic-modules-patterns.md +2 -2
  114. package/skills/factory-operations.md +3 -3
  115. package/skills/magic-modules.md +1 -1
  116. package/skills/release-cadence.md +1 -1
  117. package/tools/audit-docs.py +1 -1
  118. package/tools/index-codebase.py +1 -1
  119. package/web-app/dist/assets/{AdminPage-BJSU5hY1.js → AdminPage-DqMnfZrH.js} +1 -1
  120. package/web-app/dist/assets/{Avatar-CSOSpw9c.js → Avatar-BEof29du.js} +1 -1
  121. package/web-app/dist/assets/{Badge-COr7vj0D.js → Badge-WD53OL5m.js} +1 -1
  122. package/web-app/dist/assets/{Button-DGoWFERp.js → Button-Rzk5flhU.js} +1 -1
  123. package/web-app/dist/assets/{CockpitPage-CBpWkNXY.js → CockpitPage-Bqz3PCMh.js} +1 -1
  124. package/web-app/dist/assets/{ComparePage-BwT8vqrL.js → ComparePage-Byo2NzAZ.js} +1 -1
  125. package/web-app/dist/assets/{ErrorBoundary-D7SicDDA.js → ErrorBoundary-BuE0kH3W.js} +1 -1
  126. package/web-app/dist/assets/{EvidenceReceiptPanel-bx-5liG2.js → EvidenceReceiptPanel-CKFHOPBx.js} +1 -1
  127. package/web-app/dist/assets/{GitHubIssuesPanel-BAeeWLqY.js → GitHubIssuesPanel-Df_kTMqI.js} +1 -1
  128. package/web-app/dist/assets/{GitHubPRsPanel-CssD8lGI.js → GitHubPRsPanel-CouvcKrZ.js} +1 -1
  129. package/web-app/dist/assets/{HomePage-SL5PT3m5.js → HomePage-dEpLzj0x.js} +2 -2
  130. package/web-app/dist/assets/{LoginPage-C8hFOZOM.js → LoginPage-6DVjUND5.js} +1 -1
  131. package/web-app/dist/assets/{MagicPage-xHbHkrPf.js → MagicPage-BDQ9MOfY.js} +1 -1
  132. package/web-app/dist/assets/{MetricsPage-B_2CKFcC.js → MetricsPage-CXiJ04G_.js} +1 -1
  133. package/web-app/dist/assets/{NotFoundPage-CXwXMq35.js → NotFoundPage-BaXhZvyj.js} +1 -1
  134. package/web-app/dist/assets/{ProjectPage-AaisMQI-.js → ProjectPage-BzE4V7Ti.js} +3 -3
  135. package/web-app/dist/assets/{ProjectsPage-Cej5ResX.js → ProjectsPage-ipf6TLMX.js} +1 -1
  136. package/web-app/dist/assets/{SettingsPage-DKv4mn9H.js → SettingsPage-BDxkUKN6.js} +1 -1
  137. package/web-app/dist/assets/{ShowcasePage-Dkiyzne4.js → ShowcasePage-YQXD72SM.js} +1 -1
  138. package/web-app/dist/assets/{SystemSettingsPage-DaBLQ5dU.js → SystemSettingsPage-Dou7zHS0.js} +1 -1
  139. package/web-app/dist/assets/{TeamsPage-B2yUr31k.js → TeamsPage-CQarfcYr.js} +1 -1
  140. package/web-app/dist/assets/{TemplatesPage-CfYiANhU.js → TemplatesPage-CPi1R8Xj.js} +1 -1
  141. package/web-app/dist/assets/{TerminalOutput-CMXoexLM.js → TerminalOutput-WiGhWdYD.js} +1 -1
  142. package/web-app/dist/assets/{activity-B0dzoZtY.js → activity-DOa7ejQA.js} +1 -1
  143. package/web-app/dist/assets/{bell-DFOTaSoo.js → bell-CX-VAlYN.js} +1 -1
  144. package/web-app/dist/assets/{bot-Dz-WSQyP.js → bot-EDXaZmHc.js} +1 -1
  145. package/web-app/dist/assets/{check-L1QIRueJ.js → check-CnBdKk3L.js} +1 -1
  146. package/web-app/dist/assets/{chevron-left-1TrnIEZS.js → chevron-left-BJ_ydRQW.js} +1 -1
  147. package/web-app/dist/assets/{circle-alert-8Bf6xEDx.js → circle-alert-tz8ufhc_.js} +1 -1
  148. package/web-app/dist/assets/{clock-DB8NE6P5.js → clock-DFBZPSsH.js} +1 -1
  149. package/web-app/dist/assets/{cloud-DUSL3wZS.js → cloud-uy3p_5Q7.js} +1 -1
  150. package/web-app/dist/assets/{code-xml-DbjryWNl.js → code-xml-3CbYD9YL.js} +1 -1
  151. package/web-app/dist/assets/{database-DrezrMSs.js → database-YiNBXc9c.js} +1 -1
  152. package/web-app/dist/assets/{dollar-sign-CGbaLMzR.js → dollar-sign-D0wBa4Cv.js} +1 -1
  153. package/web-app/dist/assets/{file-code-corner-Dfypa-r4.js → file-code-corner-DDdZVZ5v.js} +1 -1
  154. package/web-app/dist/assets/{file-plus-CzV5L2Gd.js → file-plus-D09LTBqt.js} +1 -1
  155. package/web-app/dist/assets/{globe-BoYjTwPQ.js → globe-BirTPwff.js} +1 -1
  156. package/web-app/dist/assets/{hammer-WO-HuiyJ.js → hammer-DaE5qgnd.js} +1 -1
  157. package/web-app/dist/assets/{index-CjIOUjnd.js → index-D8t7KnQf.js} +3 -3
  158. package/web-app/dist/assets/{layers-Bn6jtKNk.js → layers-DVVrckRY.js} +1 -1
  159. package/web-app/dist/assets/{loader-circle-gERWRnmW.js → loader-circle-2-nMGmcb.js} +1 -1
  160. package/web-app/dist/assets/{lock-BIiMLJq8.js → lock-D0Xj43pC.js} +1 -1
  161. package/web-app/dist/assets/{package-dLvDSVys.js → package-BB7S3egm.js} +1 -1
  162. package/web-app/dist/assets/{plus-BHM3ZLhZ.js → plus-BLhoMrn3.js} +1 -1
  163. package/web-app/dist/assets/{refresh-cw-BAp_alv6.js → refresh-cw-DMc2uPLw.js} +1 -1
  164. package/web-app/dist/assets/{rotate-ccw-CBhwq_MG.js → rotate-ccw-DM3IP9x7.js} +1 -1
  165. package/web-app/dist/assets/{scroll-text-DoS-6dfU.js → scroll-text-lH2uW7rB.js} +1 -1
  166. package/web-app/dist/assets/{server-5JgdDWQO.js → server-BkerZcdR.js} +1 -1
  167. package/web-app/dist/assets/{shield-alert-DjmlLT34.js → shield-alert-B8ohcUfu.js} +1 -1
  168. package/web-app/dist/assets/{thumbs-up-BtiEXl3p.js → thumbs-up-CTIxLzPD.js} +1 -1
  169. package/web-app/dist/assets/{trash-2-ClsWU_Vg.js → trash-2-C3zIpsH5.js} +1 -1
  170. package/web-app/dist/assets/{trending-down-DPS4M7_X.js → trending-down-BTT7wV4Y.js} +1 -1
  171. package/web-app/dist/assets/{trending-up-D6OJ38-V.js → trending-up-BzHdCDvF.js} +1 -1
  172. package/web-app/dist/assets/{upload-DOCzLUpF.js → upload-HxR34-A8.js} +1 -1
  173. package/web-app/dist/assets/{usePolling-CljQTWj_.js → usePolling-DDj6Fupy.js} +1 -1
  174. package/web-app/dist/assets/{user-C0G82Weu.js → user-qH5y9yNy.js} +1 -1
  175. package/web-app/dist/index.html +1 -1
  176. package/dashboard/static/assets/mermaid.min.js +0 -2030
  177. package/dashboard/static/cost.html +0 -344
  178. package/dashboard/static/favicon.svg +0 -5
  179. package/dashboard/static/index.html +0 -15577
  180. package/dashboard/static/proofs.html +0 -194
  181. package/dashboard/static/start.html +0 -131
  182. package/dashboard/static/trust.html +0 -325
  183. package/packages/control-plane/src/server/actions.ts +0 -60
  184. package/packages/control-plane/ui/dist/assets/index-DHDI_dHM.css +0 -1
  185. package/packages/control-plane/ui/dist/assets/index-DaQr0MPS.js +0 -129
@@ -0,0 +1,15 @@
1
+ // Audit helper: one row per UI-initiated action. Never throws (an audit failure must not break the action) and never stores secrets.
2
+ import type { Db } from "../db/migrate.ts";
3
+ import { actions } from "../db/schema.ts";
4
+
5
+ export type AuditEntry = { actor?: string; kind: string; target?: string; result: string; detail?: string };
6
+
7
+ const clip = (s: string | undefined, n: number): string | null => (s === undefined ? null : s.slice(0, n));
8
+
9
+ /** Records an action. Returns true when the row was written. */
10
+ export function audit(db: Db, e: AuditEntry): boolean {
11
+ try {
12
+ db.insert(actions).values({ ts: new Date().toISOString(), actor: e.actor ?? "local", kind: e.kind, target: clip(e.target, 500), result: e.result, detail: clip(e.detail, 2000) }).run();
13
+ return true;
14
+ } catch { return false; }
15
+ }
@@ -1,6 +1,6 @@
1
1
  // Control Plane auth: bearer token on /v1, Host allowlist on loopback (DNS rebinding), bind refusal without a token.
2
2
  import { createHash, timingSafeEqual } from "node:crypto";
3
- import type { MiddlewareHandler } from "hono";
3
+ import type { Context, MiddlewareHandler } from "hono";
4
4
 
5
5
  const LOOPBACK_BINDS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
6
6
 
@@ -9,6 +9,12 @@ export function isLoopbackHost(host: string | undefined | null): boolean {
9
9
  return !!host && /^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i.test(host);
10
10
  }
11
11
 
12
+ /** True when the real socket peer (not the spoofable Host header) is loopback. An unknown peer fails closed. */
13
+ export function peerIsLoopback(c: Context): boolean {
14
+ const ip = (c.env as { requestIP?: (r: Request) => { address?: string } | null } | undefined)?.requestIP?.(c.req.raw)?.address;
15
+ return typeof ip === "string" && /^(127\.\d+\.\d+\.\d+|::1|::ffff:127\.\d+\.\d+\.\d+)$/.test(ip);
16
+ }
17
+
12
18
  /** Constant-time check of an Authorization header against the token. Hashing first makes the compare length-safe. */
13
19
  export function tokenMatches(header: string | undefined | null, token: string): boolean {
14
20
  const m = /^Bearer (.+)$/i.exec(header ?? "");
@@ -0,0 +1,50 @@
1
+ // CPE24-P2: legacy (port 57374) mappings for audit and checkpoints. Reads share their implementation with routes/checkpoints.ts; the two POSTs
2
+ // keep the /v1 rule (loopback peer, loopback Host, JSON) even though the shim guard already passed, so a token alone never reaches a restore.
3
+ import type { Context } from "hono";
4
+ import type { Db } from "../../db/migrate.ts";
5
+ import { audit } from "../audit.ts";
6
+ import { isLoopbackHost, peerIsLoopback } from "../auth.ts";
7
+ import { auditSummary, createCheckpoint, getCheckpoint, listCheckpoints, parseDays, parseLimit, rollbackCheckpoint, validCheckpointId } from "../routes/checkpoints.ts";
8
+
9
+ type AuditList = (c: Context) => { err: Response; rows?: undefined } | { err?: undefined; rows: unknown[] };
10
+
11
+ const BAD_ID = "Invalid checkpoint_id: must be 1-128 chars of alphanumeric, hyphens, and underscores";
12
+
13
+ export function auditCheckpointMapped(db: Db, repoDir: string, auditList: AuditList): Record<string, (c: Context) => Response | Promise<Response>> {
14
+ const local = (c: Context) => peerIsLoopback(c) && isLoopbackHost(c.req.header("host")) && (c.req.header("content-type") ?? "").toLowerCase().startsWith("application/json");
15
+ const refuse = (c: Context) => c.json({ detail: "loopback JSON requests only" }, 403);
16
+ return {
17
+ "GET /api/enterprise/audit": (c) => { const r = auditList(c); return r.err ?? c.json(r.rows); },
18
+ "GET /api/enterprise/audit/summary": (c) => c.json(auditSummary(db, parseDays(c.req.query("days")))),
19
+ "GET /api/checkpoints": (c) => c.json(listCheckpoints(repoDir, parseLimit(c.req.query("limit")))),
20
+ "GET /api/checkpoints/{checkpoint_id}": (c) => {
21
+ const id = c.req.param("checkpoint_id") ?? "";
22
+ if (!validCheckpointId(id)) return c.json({ detail: BAD_ID }, 400);
23
+ const m = getCheckpoint(repoDir, id);
24
+ return m ? c.json(m) : c.json({ detail: "Checkpoint not found" }, 404);
25
+ },
26
+ "POST /api/checkpoints": async (c) => {
27
+ if (!local(c)) return refuse(c);
28
+ const body = (await c.req.json().catch(() => ({}))) as { message?: unknown };
29
+ const message = typeof body?.message === "string" ? body.message : "";
30
+ if (message.length > 500) return c.json({ detail: "message must be at most 500 characters" }, 422);
31
+ try {
32
+ const meta = createCheckpoint(repoDir, message);
33
+ audit(db, { kind: "checkpoint.create", target: String(meta.id), result: "ok", detail: "legacy route" });
34
+ return c.json(meta, 201);
35
+ } catch (e) {
36
+ audit(db, { kind: "checkpoint.create", result: "error", detail: (e as Error).message });
37
+ return c.json({ detail: "could not create the checkpoint" }, 500);
38
+ }
39
+ },
40
+ "POST /api/checkpoints/{checkpoint_id}/rollback": (c) => {
41
+ if (!local(c)) return refuse(c);
42
+ const id = c.req.param("checkpoint_id") ?? "";
43
+ if (!validCheckpointId(id)) { audit(db, { kind: "checkpoint.rollback", target: id.slice(0, 128), result: "refused", detail: "invalid id (legacy route)" }); return c.json({ detail: BAD_ID }, 400); }
44
+ const r = rollbackCheckpoint(repoDir, id);
45
+ if (!r) { audit(db, { kind: "checkpoint.rollback", target: id, result: "refused", detail: "not found (legacy route)" }); return c.json({ detail: "Checkpoint not found" }, 404); }
46
+ audit(db, { kind: "checkpoint.rollback", target: id, result: r.errors.length ? "partial" : "ok", detail: `restored ${r.restored}; pre-rollback snapshot ${r.pre_rollback_snapshot} (legacy route)` });
47
+ return c.json({ ...r, message: `Restored ${r.restored} item(s) from ${id}. Prior state saved as ${r.pre_rollback_snapshot} (undo this rollback by restoring it).` });
48
+ },
49
+ };
50
+ }
@@ -0,0 +1,26 @@
1
+ // CPE24-P4: legacy (port 57374) mappings for session control and the Completion Council. They share the /v1 implementation in
2
+ // routes/session_control.ts (fixed action enum, signal files only, same-origin check, audit row). The shim guard (CP token or loopback peer) has already run.
3
+ import type { Context } from "hono";
4
+ import type { Db } from "../../db/migrate.ts";
5
+ import { councilConvergence, councilReport, councilState, councilTranscripts, councilVerdicts, intQ, runControl } from "../routes/session_control.ts";
6
+ import { lokiDirOf } from "../routes/cost_ledger.ts";
7
+
8
+ export function controlMapped(db: Db, repoDir: string): Record<string, (c: Context) => Response> {
9
+ const loki = () => lokiDirOf(repoDir);
10
+ return {
11
+ "POST /api/control/pause": (c) => runControl(db, repoDir, "pause", c, "legacy"),
12
+ "POST /api/control/resume": (c) => runControl(db, repoDir, "resume", c, "legacy"),
13
+ "POST /api/control/stop": (c) => runControl(db, repoDir, "stop", c, "legacy"),
14
+ "POST /api/council/force-review": (c) => runControl(db, repoDir, "council-review", c, "legacy"),
15
+ "GET /api/council/state": (c) => c.json(councilState(loki())),
16
+ "GET /api/council/verdicts": (c) => { const n = intQ(c.req.query("limit"), 1000); return n === null || n === 0 ? c.json({ detail: "limit must be 1..1000" }, 422) : c.json(councilVerdicts(loki(), n)); },
17
+ "GET /api/council/convergence": (c) => c.json(councilConvergence(loki())),
18
+ "GET /api/council/report": (c) => c.json(councilReport(loki())),
19
+ "GET /api/council/transcripts": (c) => {
20
+ const limit = intQ(c.req.query("limit"), 200), iterMin = intQ(c.req.query("iter_min"), Number.MAX_SAFE_INTEGER);
21
+ if (limit === null || limit === 0 || iterMin === null) return c.json({ detail: "limit must be 1..200 and iter_min a non-negative integer" }, 422);
22
+ const r = councilTranscripts(loki(), { limit, since: c.req.query("since"), iter_min: iterMin });
23
+ return "error" in r ? c.json({ detail: r.error }, 400) : c.json(r);
24
+ },
25
+ };
26
+ }
@@ -0,0 +1,30 @@
1
+ // CPE24-P3: legacy (port 57374) mappings for memory, context, focus and tasks. Each reads the same source as its /v1 route (routes/memory.ts)
2
+ // and keeps the legacy response shape. The shim's guard (legacyGuard) has already run; POST /api/focus adds the same loopback, JSON and Origin checks as /v1/focus.
3
+ import type { Context } from "hono";
4
+ import { contextTracking, focusInfo, lokiFor, memoryEconomics, memoryEpisode, memoryEpisodes, memoryPatterns, memorySkills, mutationAllowed, setFocus, tasksList } from "../routes/memory.ts";
5
+
6
+ export function memoryMapped(repoDir: string): Record<string, (c: Context) => Response | Promise<Response>> {
7
+ const loki = () => lokiFor(repoDir);
8
+ return {
9
+ "GET /api/tasks": (c) => {
10
+ if (c.req.query("project_id") !== undefined) return c.json({ detail: "project_id filter is not supported by the Control Plane" }, 400);
11
+ return c.json(tasksList(loki(), c.req.query("status")));
12
+ },
13
+ "GET /api/memory/episodes": (c) => {
14
+ const lim = c.req.query("limit") === undefined ? 50 : Number(c.req.query("limit"));
15
+ return Number.isInteger(lim) && lim >= 1 && lim <= 1000 ? c.json(memoryEpisodes(loki(), lim)) : c.json({ detail: "limit must be 1..1000" }, 422);
16
+ },
17
+ "GET /api/memory/episodes/{episode_id}": (c) => { const e = memoryEpisode(loki(), c.req.param("episode_id") ?? ""); return e ? c.json(e) : c.json({ detail: "Episode not found" }, 404); },
18
+ "GET /api/memory/patterns": (c) => c.json(memoryPatterns(loki())),
19
+ "GET /api/memory/patterns/{pattern_id}": (c) => { const p = memoryPatterns(loki()).find((x) => x.id === c.req.param("pattern_id")); return p ? c.json(p) : c.json({ detail: "Pattern not found" }, 404); },
20
+ "GET /api/memory/skills": (c) => c.json(memorySkills(loki())),
21
+ "GET /api/memory/economics": (c) => c.json(memoryEconomics(loki())),
22
+ "GET /api/context": (c) => { const r = contextTracking(loki()); return c.json(r.body, r.status); },
23
+ "POST /api/focus": async (c) => {
24
+ if (!mutationAllowed(c)) return c.json({ detail: "loopback JSON requests only" }, 403);
25
+ const body = await c.req.json().catch(() => null);
26
+ const r = setFocus(repoDir, typeof body === "object" && body !== null ? (body as Record<string, unknown>).project_dir : undefined);
27
+ return r.ok ? c.json(focusInfo(repoDir)) : c.json({ detail: r.error }, 400);
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,18 @@
1
+ // CPE24-P1: legacy (port 57374) mappings for metrics, cost and fleet spend. Each reads the same source as its /v1 route (routes/metrics.ts,
2
+ // routes/cost_ledger.ts, routes/fleet.ts) and keeps the legacy response shape. The shim's guard (legacyGuard) has already run.
3
+ import type { Context } from "hono";
4
+ import { costSnapshot, costTimeline, lokiDirOf } from "../routes/cost_ledger.ts";
5
+ import { fleetRuns, fleetSummary, parseInactive } from "../routes/fleet.ts";
6
+ import { buildMetricsText } from "../routes/metrics.ts";
7
+
8
+ export function metricsMapped(repoDir: string): Record<string, (c: Context) => Response> {
9
+ const inactive = (c: Context) => parseInactive(c.req.query("include_inactive"));
10
+ return {
11
+ "GET /api/cost": (c) => c.json(costSnapshot(lokiDirOf(repoDir))),
12
+ "GET /api/cost/timeline": (c) => c.json(costTimeline(lokiDirOf(repoDir))),
13
+ "GET /metrics": (c) => c.text(buildMetricsText(repoDir), 200, { "content-type": "text/plain; charset=utf-8" }),
14
+ // Legacy rows carry the project path; the guarded shim serves only authenticated or loopback callers, like legacy.
15
+ "GET /api/fleet/runs": (c) => c.json(fleetRuns(inactive(c))),
16
+ "GET /api/fleet/summary": (c) => c.json(fleetSummary(inactive(c))),
17
+ };
18
+ }
@@ -0,0 +1,237 @@
1
+ // GENERATED from docs/v10/CP-ENTERPRISE-UI.md section 5.2 (one row per legacy dashboard route that the shim answers).
2
+ // test/server/legacy-shim.test.ts asserts this table and the doc table agree. Regenerate both together, never one alone.
3
+ export type LegacyAction = "map" | "308" | "410" | "501";
4
+ export interface LegacyRoute { method: string; path: string; action: LegacyAction; open: boolean; migrate: string }
5
+
6
+ export const LEGACY_ROUTES: readonly LegacyRoute[] = [
7
+ { method: "GET", path: "/openapi.json", action: "410", open: true, migrate: "" },
8
+ { method: "GET", path: "/docs", action: "410", open: true, migrate: "" },
9
+ { method: "GET", path: "/docs/oauth2-redirect", action: "410", open: true, migrate: "" },
10
+ { method: "GET", path: "/redoc", action: "410", open: true, migrate: "" },
11
+ { method: "POST", path: "/api/v2/tenants", action: "410", open: false, migrate: "no tenants: local single user" },
12
+ { method: "GET", path: "/api/v2/tenants", action: "410", open: false, migrate: "no tenants: local single user" },
13
+ { method: "GET", path: "/api/v2/tenants/{tenant_id}", action: "410", open: false, migrate: "no tenants: local single user" },
14
+ { method: "PUT", path: "/api/v2/tenants/{tenant_id}", action: "410", open: false, migrate: "" },
15
+ { method: "DELETE", path: "/api/v2/tenants/{tenant_id}", action: "410", open: false, migrate: "no tenants: local single user" },
16
+ { method: "GET", path: "/api/v2/tenants/{tenant_id}/projects", action: "410", open: false, migrate: "" },
17
+ { method: "POST", path: "/api/v2/runs", action: "501", open: false, migrate: "" },
18
+ { method: "GET", path: "/api/v2/runs", action: "map", open: false, migrate: "/v1/runs" },
19
+ { method: "GET", path: "/api/v2/runs/{run_id}", action: "map", open: false, migrate: "/v1/runs/:id" },
20
+ { method: "POST", path: "/api/v2/runs/{run_id}/cancel", action: "501", open: false, migrate: "" },
21
+ { method: "POST", path: "/api/v2/runs/{run_id}/replay", action: "501", open: false, migrate: "" },
22
+ { method: "GET", path: "/api/v2/runs/{run_id}/timeline", action: "map", open: false, migrate: "/v1/runs/:id" },
23
+ { method: "POST", path: "/api/v2/api-keys", action: "410", open: false, migrate: "CP bearer token set by env (LOKI_CONTROL_TOKEN)" },
24
+ { method: "GET", path: "/api/v2/api-keys", action: "410", open: false, migrate: "CP bearer token set by env (LOKI_CONTROL_TOKEN)" },
25
+ { method: "GET", path: "/api/v2/api-keys/{identifier}", action: "410", open: false, migrate: "" },
26
+ { method: "PUT", path: "/api/v2/api-keys/{identifier}", action: "410", open: false, migrate: "" },
27
+ { method: "DELETE", path: "/api/v2/api-keys/{identifier}", action: "410", open: false, migrate: "CP bearer token set by env (LOKI_CONTROL_TOKEN)" },
28
+ { method: "POST", path: "/api/v2/api-keys/{identifier}/rotate", action: "410", open: false, migrate: "CP bearer token set by env (LOKI_CONTROL_TOKEN)" },
29
+ { method: "GET", path: "/api/v2/policies", action: "410", open: false, migrate: "" },
30
+ { method: "PUT", path: "/api/v2/policies", action: "410", open: false, migrate: "" },
31
+ { method: "POST", path: "/api/v2/policies/evaluate", action: "410", open: false, migrate: "" },
32
+ { method: "GET", path: "/api/v2/audit", action: "map", open: false, migrate: "/v1/audit" },
33
+ { method: "GET", path: "/api/v2/audit/verify", action: "501", open: false, migrate: "" },
34
+ { method: "GET", path: "/api/v2/audit/export", action: "410", open: false, migrate: "" },
35
+ { method: "GET", path: "/api/operator/runs/{run_id}", action: "410", open: false, migrate: "" },
36
+ { method: "GET", path: "/api/operator/tests", action: "410", open: false, migrate: "" },
37
+ { method: "GET", path: "/api/operator/receipts", action: "410", open: false, migrate: "" },
38
+ { method: "GET", path: "/api/operator/releases", action: "410", open: false, migrate: "" },
39
+ { method: "GET", path: "/api/operator/phases", action: "410", open: false, migrate: "" },
40
+ { method: "GET", path: "/api/operator/workspaces/runs", action: "410", open: false, migrate: "" },
41
+ { method: "GET", path: "/api/operator/workspaces/runs/{ws}/{run_id}", action: "410", open: false, migrate: "" },
42
+ { method: "GET", path: "/api/onboarding/state", action: "410", open: false, migrate: "" },
43
+ { method: "POST", path: "/api/onboarding/provider", action: "410", open: false, migrate: "" },
44
+ { method: "POST", path: "/api/onboarding/github", action: "410", open: false, migrate: "" },
45
+ { method: "GET", path: "/api/onboarding/repos", action: "410", open: false, migrate: "" },
46
+ { method: "POST", path: "/api/onboarding/repo", action: "410", open: false, migrate: "" },
47
+ { method: "GET", path: "/api/backlog/issues", action: "410", open: false, migrate: "" },
48
+ { method: "POST", path: "/api/backlog/run", action: "410", open: false, migrate: "" },
49
+ { method: "GET", path: "/api/backlog/status", action: "410", open: false, migrate: "" },
50
+ { method: "GET", path: "/api/v1/runs", action: "410", open: false, migrate: "" },
51
+ { method: "GET", path: "/api/v1/runs/{run_id}", action: "410", open: false, migrate: "" },
52
+ { method: "POST", path: "/api/v1/runs", action: "410", open: false, migrate: "" },
53
+ { method: "POST", path: "/api/v1/runs/{run_id}/stop", action: "410", open: false, migrate: "" },
54
+ { method: "GET", path: "/start", action: "308", open: false, migrate: "/" },
55
+ { method: "MOUNT", path: "/lab", action: "308", open: true, migrate: "/" },
56
+ { method: "GET", path: "/api/providers/models", action: "410", open: true, migrate: "" },
57
+ { method: "GET", path: "/.well-known/agent.json", action: "map", open: true, migrate: "/v1/runs" },
58
+ { method: "GET", path: "/api/status", action: "map", open: false, migrate: "/v1/runs" },
59
+ { method: "GET", path: "/api/projects", action: "501", open: false, migrate: "" },
60
+ { method: "POST", path: "/api/projects", action: "501", open: false, migrate: "" },
61
+ { method: "GET", path: "/api/projects/{project_id}", action: "501", open: false, migrate: "" },
62
+ { method: "PUT", path: "/api/projects/{project_id}", action: "501", open: false, migrate: "" },
63
+ { method: "DELETE", path: "/api/projects/{project_id}", action: "501", open: false, migrate: "" },
64
+ { method: "GET", path: "/api/tasks", action: "map", open: false, migrate: "/v1/tasks" },
65
+ { method: "POST", path: "/api/tasks", action: "410", open: false, migrate: "no v10 task queue; read-only GET /v1/tasks" },
66
+ { method: "GET", path: "/api/tasks/{task_id}", action: "410", open: false, migrate: "no v10 task queue; read-only GET /v1/tasks" },
67
+ { method: "PUT", path: "/api/tasks/{task_id}", action: "410", open: false, migrate: "no v10 task queue; read-only GET /v1/tasks" },
68
+ { method: "DELETE", path: "/api/tasks/{task_id}", action: "410", open: false, migrate: "no v10 task queue; read-only GET /v1/tasks" },
69
+ { method: "POST", path: "/api/tasks/{task_id}/move", action: "410", open: false, migrate: "no v10 task queue; read-only GET /v1/tasks" },
70
+ { method: "WS", path: "/ws", action: "410", open: true, migrate: "SSE at GET /v1/stream" },
71
+ { method: "GET", path: "/api/registry/projects", action: "410", open: false, migrate: "" },
72
+ { method: "POST", path: "/api/registry/projects", action: "410", open: false, migrate: "repos are discovered locally; GET /v1/repos" },
73
+ { method: "GET", path: "/api/registry/projects/{identifier}", action: "410", open: false, migrate: "" },
74
+ { method: "DELETE", path: "/api/registry/projects/{identifier}", action: "410", open: false, migrate: "" },
75
+ { method: "GET", path: "/api/registry/projects/{identifier}/health", action: "410", open: false, migrate: "" },
76
+ { method: "POST", path: "/api/registry/projects/{identifier}/access", action: "410", open: false, migrate: "" },
77
+ { method: "GET", path: "/api/registry/discover", action: "410", open: false, migrate: "" },
78
+ { method: "POST", path: "/api/registry/sync", action: "410", open: false, migrate: "repos are discovered locally; GET /v1/repos" },
79
+ { method: "GET", path: "/api/registry/tasks", action: "410", open: false, migrate: "" },
80
+ { method: "GET", path: "/api/registry/learnings", action: "410", open: false, migrate: "" },
81
+ { method: "GET", path: "/api/fleet/runs", action: "map", open: false, migrate: "/v1/fleet/runs" },
82
+ { method: "GET", path: "/api/fleet/summary", action: "map", open: false, migrate: "/v1/fleet/summary" },
83
+ { method: "GET", path: "/api/fleet/runs/{identifier}", action: "410", open: false, migrate: "" },
84
+ { method: "POST", path: "/api/focus", action: "map", open: false, migrate: "/v1/focus" },
85
+ { method: "GET", path: "/api/focus", action: "410", open: false, migrate: "" },
86
+ { method: "DELETE", path: "/api/focus", action: "410", open: false, migrate: "" },
87
+ { method: "GET", path: "/api/session/model", action: "410", open: false, migrate: "" },
88
+ { method: "POST", path: "/api/session/model", action: "410", open: false, migrate: "no mid-run model switch; start a new run with another model (POST /v1/runs)" },
89
+ { method: "GET", path: "/api/running-projects", action: "410", open: false, migrate: "" },
90
+ { method: "POST", path: "/api/control/start", action: "501", open: false, migrate: "" },
91
+ { method: "GET", path: "/api/control/builds/{execution_id}", action: "410", open: false, migrate: "" },
92
+ { method: "GET", path: "/api/control/builds/{execution_id}/proof", action: "410", open: false, migrate: "" },
93
+ { method: "POST", path: "/api/control/builds/{execution_id}/stop", action: "410", open: false, migrate: "" },
94
+ { method: "POST", path: "/api/running-projects/stop", action: "410", open: false, migrate: "" },
95
+ { method: "POST", path: "/api/fleet/runs/{identifier}/retry", action: "410", open: false, migrate: "" },
96
+ { method: "POST", path: "/api/fleet/runs/{identifier}/cancel", action: "410", open: false, migrate: "" },
97
+ { method: "GET", path: "/api/enterprise/status", action: "410", open: true, migrate: "" },
98
+ { method: "GET", path: "/api/auth/info", action: "410", open: true, migrate: "" },
99
+ { method: "POST", path: "/api/enterprise/tokens", action: "410", open: false, migrate: "" },
100
+ { method: "GET", path: "/api/enterprise/tokens", action: "410", open: false, migrate: "" },
101
+ { method: "DELETE", path: "/api/enterprise/tokens/{identifier}", action: "410", open: false, migrate: "" },
102
+ { method: "GET", path: "/api/enterprise/audit", action: "map", open: false, migrate: "/v1/audit" },
103
+ { method: "GET", path: "/api/enterprise/audit/summary", action: "map", open: false, migrate: "/v1/audit/summary" },
104
+ { method: "GET", path: "/api/compliance", action: "410", open: false, migrate: "" },
105
+ { method: "GET", path: "/api/memory/summary", action: "410", open: false, migrate: "" },
106
+ { method: "GET", path: "/api/memory/episodes", action: "map", open: false, migrate: "/v1/memory/episodes" },
107
+ { method: "GET", path: "/api/memory/episodes/{episode_id}", action: "map", open: false, migrate: "/v1/memory/episodes/{id}" },
108
+ { method: "GET", path: "/api/memory/patterns", action: "map", open: false, migrate: "/v1/memory/patterns" },
109
+ { method: "GET", path: "/api/memory/patterns/{pattern_id}", action: "map", open: false, migrate: "/v1/memory/patterns/{id}" },
110
+ { method: "GET", path: "/api/memory/skills", action: "map", open: false, migrate: "/v1/memory/skills" },
111
+ { method: "GET", path: "/api/memory/skills/{skill_id}", action: "410", open: false, migrate: "" },
112
+ { method: "GET", path: "/api/memory/economics", action: "map", open: false, migrate: "/v1/memory/economics" },
113
+ { method: "POST", path: "/api/memory/consolidate", action: "410", open: false, migrate: "read-only GET /v1/memory/*" },
114
+ { method: "POST", path: "/api/memory/retrieve", action: "410", open: false, migrate: "read-only GET /v1/memory/*" },
115
+ { method: "GET", path: "/api/memory/index", action: "410", open: false, migrate: "" },
116
+ { method: "GET", path: "/api/memory/timeline", action: "410", open: false, migrate: "" },
117
+ { method: "GET", path: "/api/memory/files", action: "410", open: false, migrate: "" },
118
+ { method: "GET", path: "/api/memory/file", action: "410", open: false, migrate: "" },
119
+ { method: "GET", path: "/api/memory/search", action: "410", open: false, migrate: "" },
120
+ { method: "GET", path: "/api/memory/stats", action: "410", open: false, migrate: "" },
121
+ { method: "GET", path: "/api/learning/metrics", action: "501", open: false, migrate: "" },
122
+ { method: "GET", path: "/api/learning/trends", action: "410", open: false, migrate: "" },
123
+ { method: "GET", path: "/api/learning/signals", action: "410", open: false, migrate: "" },
124
+ { method: "GET", path: "/api/learning/aggregation", action: "410", open: false, migrate: "" },
125
+ { method: "POST", path: "/api/learning/aggregate", action: "410", open: false, migrate: "read-only GET /v1/memory/patterns" },
126
+ { method: "GET", path: "/api/learning/preferences", action: "410", open: false, migrate: "" },
127
+ { method: "GET", path: "/api/learning/errors", action: "410", open: false, migrate: "" },
128
+ { method: "GET", path: "/api/learning/success", action: "410", open: false, migrate: "" },
129
+ { method: "GET", path: "/api/learning/tools", action: "410", open: false, migrate: "" },
130
+ { method: "POST", path: "/api/control/pause", action: "map", open: false, migrate: "/v1/control/pause" },
131
+ { method: "POST", path: "/api/control/resume", action: "map", open: false, migrate: "/v1/control/resume" },
132
+ { method: "POST", path: "/api/control/stop", action: "map", open: false, migrate: "/v1/control/stop" },
133
+ { method: "GET", path: "/api/cost", action: "map", open: false, migrate: "/v1/cost/snapshot" },
134
+ { method: "GET", path: "/api/budget", action: "501", open: false, migrate: "" },
135
+ { method: "GET", path: "/api/cost/timeline", action: "map", open: false, migrate: "/v1/cost/timeline" },
136
+ { method: "GET", path: "/api/trust/trajectory", action: "410", open: false, migrate: "" },
137
+ { method: "GET", path: "/api/gate-policy", action: "410", open: false, migrate: "" },
138
+ { method: "GET", path: "/api/pricing", action: "410", open: false, migrate: "" },
139
+ { method: "GET", path: "/api/council/state", action: "map", open: false, migrate: "/v1/council/state" },
140
+ { method: "GET", path: "/api/council/verdicts", action: "map", open: false, migrate: "/v1/council/verdicts" },
141
+ { method: "GET", path: "/api/council/convergence", action: "map", open: false, migrate: "/v1/council/convergence" },
142
+ { method: "GET", path: "/api/council/report", action: "map", open: false, migrate: "/v1/council/report" },
143
+ { method: "POST", path: "/api/council/force-review", action: "map", open: false, migrate: "/v1/control/council-review" },
144
+ { method: "GET", path: "/api/council/transcripts", action: "map", open: false, migrate: "/v1/council/transcripts" },
145
+ { method: "GET", path: "/api/council/transcripts/{iteration_id}", action: "410", open: false, migrate: "" },
146
+ { method: "GET", path: "/api/context", action: "map", open: false, migrate: "/v1/context" },
147
+ { method: "GET", path: "/api/notifications", action: "410", open: false, migrate: "" },
148
+ { method: "GET", path: "/api/notifications/triggers", action: "410", open: false, migrate: "" },
149
+ { method: "PUT", path: "/api/notifications/triggers", action: "410", open: false, migrate: "GET /v1/notifications (derived from events)" },
150
+ { method: "POST", path: "/api/notifications/{notification_id}/acknowledge", action: "410", open: false, migrate: "" },
151
+ { method: "POST", path: "/api/notifications/{notification_id}/unacknowledge", action: "410", open: false, migrate: "" },
152
+ { method: "GET", path: "/api/checkpoints", action: "map", open: false, migrate: "/v1/checkpoints" },
153
+ { method: "GET", path: "/api/checkpoints/{checkpoint_id}", action: "map", open: false, migrate: "/v1/checkpoints/{id}" },
154
+ { method: "POST", path: "/api/checkpoints", action: "map", open: false, migrate: "/v1/checkpoints" },
155
+ { method: "POST", path: "/api/checkpoints/{checkpoint_id}/rollback", action: "map", open: false, migrate: "/v1/checkpoints/{id}/rollback" },
156
+ { method: "GET", path: "/api/agents", action: "410", open: false, migrate: "" },
157
+ { method: "POST", path: "/api/agents/{agent_id}/kill", action: "410", open: false, migrate: "POST /v1/control/stop" },
158
+ { method: "POST", path: "/api/agents/{agent_id}/pause", action: "410", open: false, migrate: "POST /v1/control/stop (v10 has no pause)" },
159
+ { method: "POST", path: "/api/agents/{agent_id}/resume", action: "410", open: false, migrate: "POST /v1/control/resume" },
160
+ { method: "GET", path: "/api/logs", action: "410", open: false, migrate: "" },
161
+ { method: "POST", path: "/api/collab/join", action: "410", open: false, migrate: "" },
162
+ { method: "POST", path: "/api/collab/leave", action: "410", open: false, migrate: "" },
163
+ { method: "GET", path: "/api/collab/users", action: "410", open: false, migrate: "" },
164
+ { method: "GET", path: "/api/collab/users/{user_id}", action: "410", open: false, migrate: "" },
165
+ { method: "POST", path: "/api/collab/users/{user_id}/heartbeat", action: "410", open: false, migrate: "" },
166
+ { method: "POST", path: "/api/collab/users/{user_id}/cursor", action: "410", open: false, migrate: "" },
167
+ { method: "POST", path: "/api/collab/users/{user_id}/status", action: "410", open: false, migrate: "" },
168
+ { method: "GET", path: "/api/collab/presence", action: "410", open: false, migrate: "" },
169
+ { method: "GET", path: "/api/collab/file/{file_path:path}", action: "410", open: false, migrate: "" },
170
+ { method: "GET", path: "/api/collab/state", action: "410", open: false, migrate: "" },
171
+ { method: "GET", path: "/api/collab/state/value", action: "410", open: false, migrate: "" },
172
+ { method: "POST", path: "/api/collab/operation", action: "410", open: false, migrate: "" },
173
+ { method: "POST", path: "/api/collab/sync", action: "410", open: false, migrate: "" },
174
+ { method: "GET", path: "/api/collab/history", action: "410", open: false, migrate: "" },
175
+ { method: "WS", path: "/ws/collab", action: "410", open: true, migrate: "" },
176
+ { method: "GET", path: "/api/collab/status", action: "410", open: false, migrate: "" },
177
+ { method: "GET", path: "/api/secrets/status", action: "410", open: false, migrate: "" },
178
+ { method: "GET", path: "/api/github/status", action: "410", open: false, migrate: "" },
179
+ { method: "GET", path: "/api/github/tasks", action: "410", open: false, migrate: "" },
180
+ { method: "GET", path: "/api/github/sync-log", action: "410", open: false, migrate: "" },
181
+ { method: "GET", path: "/api/health/processes", action: "410", open: false, migrate: "" },
182
+ { method: "GET", path: "/metrics", action: "map", open: false, migrate: "/v1/metrics" },
183
+ { method: "GET", path: "/api/checklist", action: "410", open: false, migrate: "" },
184
+ { method: "GET", path: "/api/usage", action: "410", open: false, migrate: "" },
185
+ { method: "GET", path: "/api/checklist/summary", action: "410", open: false, migrate: "" },
186
+ { method: "GET", path: "/api/prd-observations", action: "410", open: false, migrate: "" },
187
+ { method: "GET", path: "/api/checklist/waivers", action: "410", open: false, migrate: "" },
188
+ { method: "POST", path: "/api/checklist/waivers", action: "410", open: false, migrate: "plan.json and receipt claims in GET /v1/runs/:id" },
189
+ { method: "DELETE", path: "/api/checklist/waivers/{item_id}", action: "410", open: false, migrate: "plan.json and receipt claims in GET /v1/runs/:id" },
190
+ { method: "GET", path: "/api/council/gate", action: "501", open: false, migrate: "" },
191
+ { method: "GET", path: "/api/app-runner/status", action: "410", open: false, migrate: "" },
192
+ { method: "GET", path: "/api/app-runner/logs", action: "410", open: false, migrate: "" },
193
+ { method: "GET", path: "/api/app-runner/errors", action: "410", open: false, migrate: "" },
194
+ { method: "POST", path: "/api/control/app-restart", action: "410", open: false, migrate: "evidence in GET /v1/runs/:source/:run/artifact/*" },
195
+ { method: "POST", path: "/api/control/app-stop", action: "410", open: false, migrate: "evidence in GET /v1/runs/:source/:run/artifact/*" },
196
+ { method: "GET", path: "/api/playwright/results", action: "410", open: false, migrate: "" },
197
+ { method: "GET", path: "/api/playwright/screenshot", action: "410", open: false, migrate: "" },
198
+ { method: "GET", path: "/api/failures", action: "410", open: false, migrate: "" },
199
+ { method: "GET", path: "/api/prompt-versions", action: "410", open: false, migrate: "" },
200
+ { method: "POST", path: "/api/prompt-optimize", action: "410", open: false, migrate: "verify stage: POST /v1/runs/:source/:run/verify" },
201
+ { method: "GET", path: "/api/activity", action: "501", open: false, migrate: "" },
202
+ { method: "GET", path: "/api/session-diff", action: "410", open: false, migrate: "" },
203
+ { method: "POST", path: "/api/activity", action: "410", open: false, migrate: "" },
204
+ { method: "GET", path: "/cost", action: "308", open: false, migrate: "/" },
205
+ { method: "GET", path: "/trust", action: "308", open: false, migrate: "/" },
206
+ { method: "GET", path: "/", action: "308", open: false, migrate: "/" },
207
+ { method: "GET", path: "/api/quality-score", action: "410", open: false, migrate: "" },
208
+ { method: "GET", path: "/api/quality-score/history", action: "410", open: false, migrate: "" },
209
+ { method: "POST", path: "/api/quality-scan", action: "410", open: false, migrate: "verify stage: POST /v1/runs/:source/:run/verify" },
210
+ { method: "GET", path: "/api/quality-report", action: "410", open: false, migrate: "verify stage: POST /v1/runs/:source/:run/verify" },
211
+ { method: "GET", path: "/api/migration/list", action: "501", open: false, migrate: "" },
212
+ { method: "POST", path: "/api/migration/start", action: "410", open: false, migrate: "" },
213
+ { method: "GET", path: "/api/migration/{migration_id}/status", action: "501", open: false, migrate: "" },
214
+ { method: "GET", path: "/api/migration/{migration_id}/plan", action: "410", open: false, migrate: "" },
215
+ { method: "GET", path: "/api/migration/{migration_id}/features", action: "410", open: false, migrate: "" },
216
+ { method: "GET", path: "/api/migration/{migration_id}/seams", action: "410", open: false, migrate: "" },
217
+ { method: "POST", path: "/api/migration/{migration_id}/advance", action: "410", open: false, migrate: "" },
218
+ { method: "POST", path: "/api/migration/{migration_id}/start-phase", action: "410", open: false, migrate: "" },
219
+ { method: "GET", path: "/api/managed/events", action: "410", open: false, migrate: "managed memory dropped; GET /v1/runs/:source/:run/events" },
220
+ { method: "GET", path: "/api/managed/status", action: "410", open: false, migrate: "managed memory dropped; GET /v1/runs" },
221
+ { method: "GET", path: "/api/managed/memory_versions/{memory_id}", action: "410", open: false, migrate: "" },
222
+ { method: "GET", path: "/api/findings/{iteration}", action: "410", open: false, migrate: "" },
223
+ { method: "GET", path: "/api/quality/architecture", action: "410", open: false, migrate: "" },
224
+ { method: "GET", path: "/api/learnings", action: "501", open: false, migrate: "" },
225
+ { method: "GET", path: "/api/escalations", action: "501", open: false, migrate: "" },
226
+ { method: "GET", path: "/api/escalations/{filename}", action: "410", open: false, migrate: "" },
227
+ { method: "GET", path: "/api/phases", action: "410", open: false, migrate: "" },
228
+ { method: "GET", path: "/api/proofs", action: "501", open: false, migrate: "" },
229
+ { method: "GET", path: "/api/proofs/summary", action: "501", open: false, migrate: "" },
230
+ { method: "GET", path: "/api/proofs/{run_id}", action: "501", open: false, migrate: "" },
231
+ { method: "GET", path: "/api/proofs/{run_id}/html", action: "410", open: false, migrate: "" },
232
+ { method: "GET", path: "/api/spec", action: "410", open: false, migrate: "" },
233
+ { method: "GET", path: "/api/spec/history", action: "410", open: false, migrate: "" },
234
+ { method: "GET", path: "/api/wiki", action: "410", open: false, migrate: "" },
235
+ { method: "GET", path: "/api/wiki/{section}", action: "410", open: false, migrate: "" },
236
+ { method: "POST", path: "/api/wiki/ask", action: "410", open: false, migrate: "wiki deleted; no replacement" },
237
+ ];