/** * `DELETE /api/v1/projects/:projectId/interactive/docs/:doc` — the governed doc delete * (crew#338, the crew half of studio#119; the bridge half is interactive#189). * * Deleting a doc is TWO facts in TWO stores, and this route is the one door that changes both: * * 1. INTERACTIVE retires the lineage — `DELETE /api/docs/:doc` on the bridge tombstones the * workspace (soft, idempotent, 409-guarded while a build is in flight) and emits * `wicked.interactive.doc.retired` exactly once. * 2. CREW drops the doc's handoff-ledger rows — the replay-dedup rows in * `~/.wicked-crew/interactive-{draft,edit,chat,demo}-ledger.json`. The draft leg keys by * document id ("one first draft per document lifetime"), so a row that outlives its doc * claims the name forever: a doc later created under the same name is treated as * already-drafted and never gets its first draft. That ghost is what this route exists to * prevent (before it, cleanup was `rm -rf` plus hand-editing the ledger JSON). * * Deliberately NOT part of the pure-transport proxy (`proxy-routes.ts` stays response-sniffing * free): this is its own route, one static path segment more specific than the proxy's wildcard, * so fastify routes the governed DELETE here while every other method/path — including a raw * `DELETE .../interactive/api/docs/:doc`, which remains verbatim transport — keeps flowing * through the proxy untouched. Belt-and-braces for deletions that bypass this route entirely * (a direct bridge call, another tool): the `/ws` relay watches `wicked.interactive.doc.retired` * on the bus and runs the same idempotent sweep (see `ws-relay.ts` / `server.ts`). * * LOUD ON PARTIAL FAILURE — the contract's spine. The two halves can genuinely diverge (bridge * retired but a ledger file is unwritable), and the worst outcome would be a response that lets * the divergence hide. So every answer names exactly which half happened: * * - 200 both halves done — the bridge's own 200 body (retire/repeat) + `ledger` report; * - 404 unknown doc in interactive; the ledger was STILL swept (`ledger` report in the body) — * a hand-`rm -rf`'d workspace is precisely a 404 with ghost rows to drop. ONLY the retire * wire's own 404 counts (body `{"error":"unknown doc"}`): a bridge too old to carry the * retire route answers `DELETE /api/docs/:doc` with express's default not-found page, and * sweeping on THAT 404 would drop live rows for a doc the bridge still serves — the exact * silent divergence this route exists to prevent (see the 502 skew branch below); * - 409 the bridge refused (build in flight) — relayed verbatim, NOTHING swept; * - 500 PARTIAL: interactive's half happened (or was a 404 no-op) but the ledger sweep failed — * the body carries both halves and says the retry instruction out loud; * - 502 interactive did not retire (5xx / unreachable) — the ledger is deliberately untouched * (`skipped: true`), so nothing diverged; * - 503 bridge unavailable (proxy parity: `{code:"bridge_unavailable", hint}`). * * Every state change lands in the actor audit trail as `interactive.doc.deleted` (task #88). */ import type { FastifyInstance } from 'fastify'; import type { AuditLog } from '../api/audit.js'; import type { CoreAdapter } from '../core/adapter.js'; import type { Actor } from '../core/types.js'; import type { ProjectSettingsStore } from '../projects/settings.js'; import { type InteractiveBridgePool } from './bridge-pool.js'; import type { DocLedgerSweep } from './doc-ledger-sweep.js'; export interface DocDeleteDeps { settings: ProjectSettingsStore; pool: InteractiveBridgePool; audit: AuditLog; actorOf: (req: { actor?: Actor; }) => Actor; /** The four-ledger sweep (`server.ts` wires the real one over the armed seams + their files; * a directly-driven route set gets an inert default so unit tests never touch ~/.wicked-crew). */ dropDocLedgerRows: (documentId: string, projectId?: string) => DocLedgerSweep; env?: NodeJS.ProcessEnv; /** The daemon state home the DEFAULT docs root hangs off (crew ≥ 0.7.35, D-L7-1; tests point it at a scratch dir). */ stateHome?: string; /** The HOME a leading `~` in an EXPLICIT `interactiveRoot` expands against. */ home?: string; log?: (msg: string) => void; /** Budget for the bridge's retire call (tests shorten it). The tombstone write is local and * fast; the default only has to outlast a busy event-loop, not a build. */ upstreamTimeoutMs?: number; } export declare function registerInteractiveDocDelete(app: FastifyInstance, adapter: CoreAdapter, deps: DocDeleteDeps): void; //# sourceMappingURL=doc-delete-routes.d.ts.map