Production Cutover — legacy daemon/server.mjs -> node/src/server.ts
SkillDev toolsSOP for wiring AIDE's new TS route stack (node/src/server.ts, all 110+ openapi routes) into production so `npm start` serves it, strangler-fig migration away from the legacy monolith daemon/server.mjs. Covers the verified two-server reality, route inventory reconciliation, facade routing, contract-test safety net, per-domain cutover phases with verification gates, rollback, and decommissioning. Use when starting the cutover work, when a shipped feature 404s in the real UI, when touching scripts/start.mjs or either server entrypoint, or before decommissioning any legacy module.
Use Production Cutover — legacy daemon/server.mjs -> node/src/server.ts in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Production Cutover — legacy daemon/server.mjs -> node/src/server.ts and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Production Cutover skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by anonymousnomad/covert-coder in skills/packs/aide-production-cutover/SKILL.md and read by Ahel’s review.
CURRENT VERIFIED REALITY (2026-09-03)
scripts/start.mjs:35-37launches TS arch on4778, legacy on4779, facade on4777, and the root UI on4173.common/facade-route-map.jsonis generated and verified for155TS paths,64TS prefixes, and3exact flips; shared/legacy paths remain deterministic until parity is proven.npm testandnpm run veritaspass; the real launcher probe and serialized arch suite are green.- The packaged path stages and smoke-tests the same TS/legacy/facade topology via
desktop/stack-launcher.mjs; native Tauri compilation remains a Rust-toolchain gate. desktop/src/main.rslaunches the managed packaged stack in source. Do not claim native desktop readiness until Rust build/install/offline smoke passes.
Historical verified reality (superseded where it conflicts above)
- TWO HTTP servers coexist:
daemon/server.mjs(legacy monolith: model-manager, community, lsp/dap managers, training, academy/tutor, plugins, blueprint, replay, arena, operator, tasks, session, artifacts, providers, workflow, handoff — hand-rolled routes, its own 404 envelope{error:'not found'}at line ~478) andnode/src/server.ts(ArchServer: ALL new-stack routes via openapi.ts buildRoutes — agent loop, index/RAG, modelhub, handoff TS routes, byok, git, editor, LSP/DAP TS, etc.; env AIDE_ARCH_PORT default 4778; standalonemain()launchable vianode node/src/server.ts). scripts/start.mjsspawns ONLY the legacy server on 4777 + serves UI on 4173. NOTHING in production launches the TS stack — every phase we shipped since the rebuild is invisible tonpm startusers. This is THE wiring gap.- Stale-daemon trap: an old daemon process holding port 4777 makes fresh launches fail silently (new proc dies on EADDRINUSE, old one answers with 404s for new routes). ALWAYS check
Get-CimInstance Win32_Process -Filter "Name='node.exe'"+ creation date before blaming code. - Arch tests already prove the TS stack over real HTTP (
tests/arch/*.test.tsuseArchServer.listen(0)).
Research base (primary sources)
- Fowler, Strangler Fig Application (martinfowler.com, 2004/2024): grow the new system around the old; event interception at seams; incremental displacement beats big-bang rewrite on risk.
- Azure Architecture Center, Strangler Fig pattern: place a façade that routes per-request to legacy or modern; façade must not become SPOF/bottleneck; plan cross-system calls through an anti-corruption layer; remove transitional pieces only after validation.
- AWS Prescriptive Guidance, Strangler fig: request-based routing by URL path is the standard mechanism; feature toggles give instant rollback.
- CircleCI engineering blog (2025): consumer-driven CONTRACT TESTS are the migration safety net — both sides must satisfy the same interface expectations; parallel-run/shadow validation before traffic flips.
- Cartwright/Horn/Lewis, Patterns of Legacy Displacement (martinfowler.com): parallel run, divert-the-flow, legacy mimic; transitional architecture is deliberate, quantified, and temporary.
Strategy decision (locked; current ports supersede the historical values below)
Strangler-fig with a thin Node facade on the SINGLE user-facing port (4777), routing by path-prefix between two backends:
- Facade = small http server (scripts/facade.mjs) spawned by start.mjs; holds a static ROUTE_MAP { prefix -> 'ts' | 'legacy' }.
- TS currently runs on internal port
4778, legacy on internal port4779; neither is user-facing. - Default ROUTE_MAP starts 100% legacy EXCEPT paths only the TS stack serves (agent/index/modelhub/byok/handoff-ts) — instant feature visibility with zero legacy regression.
- Cutover = flipping prefixes after that domain's parity gates pass; decommission = deleting the legacy branch from ROUTE_MAP then the module.
Why this way
- Big-bang swap is forbidden by our own laws (Verify-First; no brick wall): legacy still owns domains the TS stack never reimplemented (community, academy, training arena, plugins, blueprint). Flipping everything at once breaks users of those features.
- Path-prefix facade is exactly the AWS/Azure request-based-routing pattern; contract tests are Fowler's recommended safety net and we ALREADY have the machinery (shared zod contracts + arch tests + drift check).
- Single exposed port preserves the user mental model and the In-the-Box law.
Phase plan (each phase: build -> verify gate -> journal -> commit)
Phase C0 — Route inventory (no code changes)
Enumerate exact route tables of BOTH servers into docs/evidence/route-inventory.md: legacy = grep path literals in daemon/server.mjs; TS = parse common/openapi.json routes[]. Produce three lists: ts-only, legacy-only, both. GATE: counts reconcile against openapi.json route count and legacy handler count; journal records numbers.
Phase C1 — Facade + dual-spawn
- scripts/facade.mjs: http server; per-request lookup longest-matching prefix in ROUTE_MAP; proxy (http.request, stream pipe both directions incl. SSE/WS upgrade handling — WS channels must be proxied with upgrade events, not just GET/POST); health endpoints
/api/health/ts+/api/health/legacy; ROUTE_MAP loaded from.aide/facade-routes.jsonif present else built-in default. - start.mjs currently spawns facade(4777) + ts(4778) + legacy(4779); kills tree on exit (reuse ProcessManager patterns from aide-arch-backend-core).
- Tests FIRST (tests/unit/test-facade.mjs, no network beyond 127.0.0.1 loopback fixtures): prefix routing table hits both backends; unknown path -> legacy (preserves old behavior); SSE stream passes through unbuffered; WS upgrade proxied; backend-down -> typed 502 envelope, never hang; ROUTE_MAP file override honored; kill-tree cleanup leaves zero orphan node procs.
GATE: unit green; manual
npm start-> hit one ts-only route AND one legacy route through 4777; veritas PASS; CI green.
Phase C2..Cn — Domain cutovers (one domain per phase, smallest first)
Order (risk-ascending): editor/files/git -> search/index -> agent+byok -> model runtime -> tasks/build -> hub -> THEN the hard tail (community, academy, training, plugins, blueprint) which may require PORTING instead of flipping (decide per domain when inventory shows legacy-only surface). Per domain: (1) write contract tests asserting TS responses satisfy what the LEGACY frontend/consumers expect where shapes differ — anti-corruption adapter goes in facade OR TS route gains a compat alias route (prefer alias in TS, keep zod strict core + explicit legacy-shape mapper); (2) flip prefix; (3) parallel-run window: facade can shadow-compare responses (log-only diff mode) before enforcing; (4) verify: full gates + REAL UI walkthrough of that domain's features through 4777; (5) journal + commit. GATE per domain: arch suite green, targeted e2e through facade green, live UI click-through recorded as evidence screenshot, veritas PASS, CI green.
Phase CF — Decommission
When ROUTE_MAP has zero legacy entries AND Phase C0 inventory shows every legacy capability ported or consciously dropped (user sign-off): remove legacy spawn + modules, facade becomes plain reverse proxy or is deleted (TS binds 4777 directly), update README/GETTING_STARTED. GATE: clean-install smoke per aide-packaging-offline battery; grep proves no imports of deleted modules remain.
Code guidance (specifics)
- Proxy: NEVER buffer SSE — pipe streams; set
response.flushHeaders(); handle client abort (request.on('close')-> destroy upstream socket) or chat streaming will hang. - WS: facade must handle
'upgrade'event explicitly; plain http.request does NOT proxy upgrades. Map ws path/channel to same backend rules; splice raw socket pair after manual handshake check. - Timeouts: upstream connect timeout 5s, response idle timeout configurable (chat streams idle >60s legitimately? no — llama tokens flow; but long tool-calls may idle: set 300s idle, documented).
- Ports: internal ports must be env-overridable (AIDE_TS_PORT/AIDE_LEGACY_PORT) for tests to avoid collisions (port doctrine 4777/79/80).
- Windows: spawn with
{ stdio:['ignore','pipe','pipe'] }, capture child stderr to .aide/logs/facade.log; kill via taskkill /pid /T /F fallback if tree-kill fails (ProcessManager precedent). - Error envelope at facade level MUST match house shape ({error:{code,message}} vs legacy {error:'string'} — the anti-corruption mapping lives here; do not let two error shapes leak to the frontend randomly).
Pitfalls / issues / bugs watch-list
- EADDRINUSE silent death (verified today): always preflight port ownership; facade should detect "backend port busy at boot" and FAIL LOUD with the owning PID.
- Double-serving during transition: a route existing on BOTH servers with different shapes = heisenbugs depending on ROUTE_MAP state. Rule: while a route exists on both, facade routes it to ONE side deterministically; log a warning at boot listing dual-served prefixes.
- Cookie/session/state divergence: legacy stores under .aide/* files with its own schemas; TS services re-read some of the same dirs (.aide/byok, .aide/handoff). Inventory must include STATE files, not just routes — two writers to one JSON = corruption risk. Mitigation: per-domain cutover includes migrating state ownership; until then only one side may WRITE a given file (enforce in code review checklist).
- WS channel union mismatch: events.ts ChannelName is strict zod; legacy emits its own event names. Frontend listeners written for legacy names will silently starve. Audit browser/src subscriptions vs each backend's emitted names BEFORE flipping any domain with live updates.
- Long-running downloads/training jobs crossing a facade restart: job state must be recoverable from disk (modelhub manifest.json precedent) — never trust in-memory-only job maps across the cutover period.
- CI: arch suite hangs locally (known OPEN ISSUE) — do not add facade integration tests to the arch chain until hang root-caused; keep them in unit layer with loopback fixtures.
Threat matrix
| Threat | Vector | Control |
|---|---|---|
| SSRF-style probing via facade | crafted Host/path headers forwarded internally | facade fixes upstream Host header itself; strips Hop-by-hop headers (Connection, Keep-Alive, Transfer-Encoding, TE, Trailer, Upgrade except managed WS) |
| Path traversal into backend routing | encoded slashes, %2e tricks changing prefix match | normalize + decode ONCE in facade before prefix match; reject .. segments; match on decoded canonical path |
| Port hijack on dev machines | malware binds 4779 | bind internal ports to 127.0.0.1 only (both backends already HOST=127.0.0.1 — verify, don't assume) |
| Secret leakage via proxy logs | Authorization headers hitting facade logs | facade logs method/path/status/duration ONLY; never headers/bodies |
| Rollback impossible after partial state migration | mixed writers corrupt .aide state | per-domain write-ownership rule (pitfall 3) + expand-contract style state moves |
| Facade SPOF | crash takes whole IDE down | facade is tiny + stateless; watchdog auto-restart w/ backoff; backends independent of facade lifecycle |
Verification battery (per phase, non-negotiable)
- tsc x2 + eslint clean.
- Unit suite incl. new facade tests (loopback only, deterministic).
- Full arch suite standalone-per-file locally (known hang caveat), CI arbitrates full chain.
- Live: npm start from clean shell; curl matrix across ROUTE_MAP (one route per backend per domain); SSE chat stream observed token-by-token; WS event received through facade.
- Real UI walkthrough of the flipped domain; evidence screenshot into docs/evidence/.
- veritas PASS; push; CI green BEFORE claiming the phase done; AGENT_NOTES entry per developer-code-and-credo LOG step.
Signals
- GitHub stars
- 43
- Forks
- 14
- Last commit
- Oct 2026
Advanced
- Item type
- skill
- Key
aide-production-cutover- Source
- github.com/anonymousnomad/covert-coder
github.com/anonymousnomad/covert-coder
Related picks
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScriptomh-rust
Skill · rlaope
The pick for Rustrust-async-task-design
Skill · hashgraph-online
The pick for Rusthandsontable-playwright-e2e
Skill · handsontable
The pick for End-to-end testingmstar-e2e
Skill · btspoony
The pick for End-to-end testing