01 — Deep Dive: Binding Constraints, Host Ground Truth, Footprint Evidence
Research executed 2026-07-19 via a 10-packet parallel research workflow (repo-constraints, host-empirical, rust-services, go-services, ts-bun-encore, db-layer, obs-stack, web-ui, case-studies, agentic-lang-fit, process-count-audit — case studies and language-fit evidence are covered in 02-case-studies.md, not here), ~861k subagent tokens, 295 tool calls total. Every claim below carries its packet’s confidence tag (confirmed / supported / hypothesis / assumption) where the source material assigned one; raw packets are evidence/r1-research-packets-raw.json, digest is evidence/r1-research-digest.txt.
1. What is already decided (binding constraints)
The task brief framing — “language undecided for all four apps” — is wrong for three of the four. Only one slot is actually open.
| Product | Language/runtime | Status | Source |
|---|---|---|---|
| WorkLedger | Encore.ts internal service, Drizzle, Postgres | Pinned, not a free choice — ADR D5 explicitly named a standalone repo as fallback-only, not the default | plans/002-work-ledger-product/10-architecture.md:382 |
| Atlas | Encore.ts internal-plane service (copies github service template), Postgres + pg_trgm |
Pinned — layering per .claude/rules/encore-ts.md is binding; no vector DB in v1 |
plans/003-atlas-intelligence-layer/10-architecture.md:104-108,23 |
| Dual-run Parity Harness | TypeScript workspace package + CLI + CI runner, outside Encore | Pinned (confirmed) — “Cloud Run can’t run pinned-EOL images or tap MBUS” (architect correction #4) | plans/002-dual-run-parity-harness/architecture.md:27 |
| FleetOps ingest/board | — | Open. Existing TUIs (Rust fleetops, Go ghmonitor) are the only footprint-conscious prior art; the web Board stack and ingest-service language are undecided |
plans/004-fleet-monitor/05-ux-ui.md (no language pin found) |
Why this matters for the new hard constraint: none of the three Encore-adjacent architecture docs weighed process-memory footprint as a decision criterion anywhere in their ADR tables — every choice there was justified by Encore-convention-fit, zero-Core-PR, or OSS license/maturity, never host resource cost (confirmed, ADR tables at plans/002-dual-run-parity-harness/architecture.md:296-307, plans/003-atlas-intelligence-layer/10-architecture.md:323-332, plans/002-work-ledger-product/10-architecture.md:376-386). So “lock WorkLedger + Atlas + harness kernel into Node/TS” is not new risk introduced by this decision — it’s a sunk cost from three prior decisions this doc cannot reopen. The only lever available for the stated memory ceiling is the FleetOps ingest+board slot, plus footprint hygiene on the already-Node parts (process count, dev-vs-built-binary, consolidation).
Integration seams that constrain the open slot’s shape:
- FleetOps’ Grafana reads WorkLedger’s 5 SQL views directly as a Postgres datasource, no API layer — “human reads are SQL views, not endpoints” (P1). If the ledger isn’t live, the Board silently degrades from 5 objects to 3, and metric tiles read “unavailable, ledger not connected,” never a fabricated zero (
plans/002-work-ledger-product/10-architecture.md:311-319,329;plans/004-fleet-monitor/05-ux-ui.md:35,109). - The Dual-run harness’s only write into the Encore world is
work-ledger.transition(verified, evidence=<url>)orblock(question)— a CLI/HTTP call, not a shared process or library (plans/002-work-ledger-product/10-architecture.md:327;plans/002-dual-run-parity-harness/architecture.md:139-140). - Every one of the three Encore-adjacent products repeats the identical binding: local-only via
encore run/docker-compose, no cloud dependency until the billing gate clears (confirmed, repeated across all three architecture docs). This is a hard scope wall on the whole decision, not incidental — cloud is off the table regardless of what this doc recommends. - research/04’s cost model assumes “1,000 logical agents · 200 active max,” a materially larger scale than the “64 concurrent” ceiling MEASURED and cited in both Atlas’s and WorkLedger’s own architecture docs (
research/04-models-tooling-cost.md:49vsplans/003-atlas-intelligence-layer/10-architecture.md:54) — an internal scale inconsistency across planning docs the stack decision should size against the larger, more conservative number, not the smaller one.
Already rejected, don’t re-litigate: the three pinned products each carry their own AVOID list, and none of the rejections were footprint-driven — re-opening them on memory grounds would be relitigating settled convention-fit/license/maturity calls, not responding to new evidence. Harness: opendiffy/diffy (license-blocked CC BY-NC-ND), GoReplay Pro, Signadot/Speedscale, DB-value diffing, any Encore-hosted kernel (plans/002-dual-run-parity-harness/architecture.md:404). Atlas: a new Core task-events topic, per-question Temporal workflow, embeddings/vector DB in v1, a full Atlas MCP server, Backstage TechDocs (~$1.52M MODELED 3-yr TCO, 2-3 FTE) (plans/003-atlas-intelligence-layer/10-architecture.md:325-332,139). WorkLedger: pg_advisory_xact_lock, pgmq/graphile-worker/river, dual-write Temporal sync, any kanban/UI (plans/002-work-ledger-product/10-architecture.md:257-259,378,427).
2. Host ground truth
Measured 2026-07-19, this machine, via nproc/free -m/ps aux/du:
| Metric | Value | Source |
|---|---|---|
| CPU cores | 14 | nproc |
| Total RAM | 48,174 MB (48.2 GB) | free -m |
| Free RAM | 31,499 MB (31.5 GB) | free -m |
| Current Claude Code processes | 49 total (9 main + 40 npm/node MCP children) | ps aux | grep claude |
| All Claude processes RSS | 7.64 GB (7,824,740 kB) | ps aux sum |
| Average RSS per Claude process | 156 MB | 7,824,740 kB ÷ 49 |
| Main Opus processes RSS | 5.21 GB (9 processes) | ps aux sum |
| Node version | v24.18.0 | node --version |
| Bun version | 1.3.14 | bun --version |
| Cargo version | 1.96.1 | cargo --version |
| Go version | 1.26.4 | go version |
| Kernel | Linux 6.6.114.1-microsoft-standard-WSL2 | uname -a |
Nothing is installed yet. Postgres, Temporal, Encore CLI, psql: none present. No Grafana/Prometheus/Loki process running, no docker container up (which + docker ps + ps aux, all empty).
Existing TUI prior art (personal tools, not this repo): 5 Rust binaries + 1 Go binary, ~60k combined LOC (fleetops 6.4k, bridge 16.7k, ground-control 4.4k, tokenomics 25.1k Rust; ghmonitor 6.6k Go). They occupy 12 GB on disk almost entirely from Rust target/ build artifacts (1.2–6.1 GB per tool) — source trees themselves are 180KB–824KB. fleetops already emits a fleet snapshot JSON command matching FleetOps’ board-state needs; ghmonitor (Go) already does structured NDJSON event output.
The scaling wall this whole decision exists to avoid: at the stated “hundreds of concurrent agents” target, extrapolating today’s measured 156MB/process average or the brief’s 200–500MB/process planning range both blow past the 48GB host:
| Scenario | Projected RSS | vs. 48GB host |
|---|---|---|
| 500 agents × 200MB (low end) | 100 GB | 208% |
| 500 agents × 500MB (high end) | 250 GB | 521% |
Every MB the service layer (WorkLedger, Atlas, FleetOps, harness, DB, observability) can shave is headroom clawed back from an already-tight agent-fleet budget — this is the entire reason footprint, not convention-fit, is now the deciding criterion for the one open slot and for hygiene on the three pinned ones.
3. Runtime footprint evidence
Idle/small-load RSS across the candidate languages/frameworks, real (non-marketing) benchmarks only:
| Runtime | Idle RSS | Source |
|---|---|---|
| Minimal musl axum (Rust), under wrk load | 1.1 MB | apatisandor.hu/blog/rust-microservice (confirmed) |
| axum v0.7, Sharkbench saturated load | 8.5 MB (21,030 req/s, 1.6ms median) | sharkbench.dev/web/rust-axum (confirmed) |
| axum, post-load settling (glibc default allocator) | ~160 MB idle | github.com/tokio-rs/axum/discussions/2589 — allocator-retention artifact, not a leak; fixed by switching to mimalloc (confirmed) |
| Go, simple isolated program | 5.1 MB | povilasv.me/go-memory-management (supported) |
| Go, minimal backend at ~6k RPM/50ms | 32–64 MB, ~0.5 core | thariqzs Medium (supported) |
| Go, GoDoxy net/http reverse proxy | 150–200 MB, <5% idle CPU | deepwiki.com/yusing/godoxy-wiki (supported) |
| PocketBase (Go+SQLite+embedded UI) | 10 MB baseline, 90–150 MB under load, 10k+ realtime conns on a $6/mo VPS | byteiota.com (supported) |
| Bun 1.2, small HTTP server | ~35–45 MB idle | aggregated 2026 benchmark roundup (supported) |
| Node 22, small HTTP server | ~60–75 MB idle | aggregated 2026 benchmark roundup (supported) |
| Bun vs Node, Sharkbench Fastify | Bun 49.8MB / 20,683 req/s vs Node 57.0MB / 9,340 req/s (12.67% less RSS, 2.21x throughput) | sharkbench.dev (confirmed) |
| Bare Node hello-world floor | ~30–55 MB — no benchmark found below ~30MB for any Node process | aggregated (supported) |
| Encore.ts (Rust core + Node) | no official RSS figure exists anywhere — throughput-only benchmark (121K req/s @150 workers, 2.3ms p99, Encore’s own unverified numbers) | encore.dev/blog/rust-runtime (confirmed: no memory number published) |
The per-process floor is the real lever, not framework choice. V8/Node give each OS process its own isolate/heap/GC/event loop with zero cross-process sharing; pointer compression roughly halves per-isolate heap (512MB→256MB in one cited multi-tenant example) but that’s a per-process optimization, not sharing (supported, blog.platformatic.dev). Generic process-isolation studies (non-Node-specific) show 2–5x memory and 8x CPU overhead vs. a single shared process (hypothesis, arxiv.org/pdf/2110.04751). Consolidating FleetOps ingest+board into one Bun/Go/Rust process instead of 3–4 separate services is the single highest-leverage footprint decision available for the open slot — baseline-per-process dominates at this scale far more than which framework runs inside it.
Go-specific findings for the open slot (goroutine fan-out + SQLite driver choice): goroutines start with a 2KB stack (doubles on growth, ~400 bytes fixed descriptor overhead) — orders of magnitude cheaper than a thread- or process-per-connection model, directly relevant to per-poller/per-SSE-client fan-out on a host already carrying hundreds of Node processes (confirmed, medium.com/a-journey-with-go). A Go SSE server scaled to ~150,000 concurrent connections on an 8GB laptop before OOM, hitting ephemeral-port exhaustion (~28,232 connections) well before memory became the binding constraint — nowhere close to FleetOps’ expected tens-to-low-hundreds of watchers (supported, blog.pranshu-raj.in). For SQLite access: mattn/go-sqlite3 (CGO) is consistently 1.1–2.1x faster than modernc.org/sqlite (pure Go) on writes, widening on write-heavy workloads at scale (confirmed, datastation.multiprocess.io) — use it since this box already has a C toolchain (it builds Go itself). PocketBase defaults to modernc.org/sqlite specifically for CGO-free cross-compilation and only uses mattn when CGO_ENABLED=1 (supported) — a build-portability tradeoff, not a performance one, and irrelevant here since this is a single-host, non-cross-compiled deployment. Counter-examples worth noting: Miniflux (Go) refuses to support SQLite at all, citing CGO/GCC build friction it wasn’t willing to carry (supported, github.com/miniflux/v2#2299); k3s ships SQLite as its default single-node control-plane store at ~512MB RAM in a <100MB binary, a production precedent well beyond FleetOps’ expected load (supported, docs.k3s.io).
Caveats that affect the two open-slot candidates directly:
cargo buildCPU contention (not memory) is Rust’s real risk on this host: it grabs all cores via the jobserver protocol by default and will compete with concurrent Node agent processes. Mitigate withcargo checkin the dev loop (collapses minutes to seconds for small services) andCARGO_BUILD_JOBScaps — not a reason to avoid Rust at the ~150–500 LOC scale these services are (supported/hypothesis, users.rust-lang.org).- GOMEMLIMIT must be set manually for Go services sharing this host — the Go runtime does not yet auto-detect cgroup/host memory limits (proposal golang/go#75164 is open, unmerged); without it, GC frequency follows heap-doubling and RSS can approach the ceiling before a collection triggers (confirmed, golang/go#75164).
- Bun.serve’s default 10-second idle-connection timeout silently kills a quiet SSE stream unless
server.timeout(req, 0)is called per-request — a real gotcha if Bun is chosen for the FleetOps Board’s SSE feed (confirmed, bun.com/docs/guides/http/sse). - Encore daemon vs. built binary:
encore runlocally requires Docker (for Postgres provisioning) plus a persistent backgroundencore daemonprocess and a live trace dashboard on :9400 — overhead on top of the app process itself, present for every local dev instance.encore build dockerproduces a self-hosted image with no dev daemon, no file-watcher, no live-trace buffer (supported, encore.dev docs). This is a real, actionable cut for WorkLedger/Atlas’s always-on posture — see §6 ofprocess-count-audit, folded into §5 below. - Hono’s SSE helper has lifecycle gaps.
streamSSEcan require manualstream.sleep()heartbeats to avoid the connection closing, and there are open upstream issues about cleanup on client disconnect (supported, github.com/orgs/honojs/discussions/3472) — a maturity caveat if Hono is picked for the harness or a TS-based Board UI, separate from Bun’s own 10s timeout above. - Dev-loop speed, if staying on the pinned TS runtime for the harness:
tsx(~18ms cold start, ~150–300ms watch-restart) is the practical default overts-node(~520–1200ms cold start, ~29x slower) for anything on Node; Bun’s native TS execution is faster again (~5ms cold start, ~20ms watch-restart) and should be preferred overtsxwherever the substrate constraint doesn’t force plain Node (confirmed/supported, pkgpulse.com). Neither number is a footprint figure, but a tight harness dev loop matters when hundreds of agents are iterating against it.
4. Database layer evidence
Postgres is not the memory bottleneck at this scale. A tuned local instance (128–256MB shared_buffers) costs low hundreds of MB total (confirmed: ~130MB RSS worst-case at 25 active connections in a 140MB-capped container, stevenpg.com) — noise next to tens of GB of agent-process RSS. True per-connection marginal overhead is ~7.6MiB (huge_pages=off) / ~1.3MiB (on) once measured correctly (confirmed, blog.anarazel.de); AWS’s own idle-connection measurement lands near the folk “10MB/connection” figure (1.5MB bare idle to 14.5MB with temp tables, confirmed, aws.amazon.com/blogs/database) — but this only matters if agents open Postgres connections directly, which the pinned architecture avoids.
PgBouncer connection math: the pool is the queue, not connection count. Agents never touch Postgres directly — they hit one Encore.ts service’s HTTP endpoint, which holds one small node-postgres pool (default max=10). The 11th concurrent request queues in-process behind connectionTimeoutMillis, not at the database (supported, node-postgres.com/apis/pool). PgBouncer’s own footprint is negligible (2–5MB base + ~2KB/client, confirmed) but it only earns its keep once multiple app process instances each open their own pool and the sum approaches Postgres’s default max_connections=100 — not for a single-instance Encore.ts service serving hundreds of agents over HTTP (hypothesis: even at 2–3 scaled-out instances × pool 10–20, still well under 100). A single PgBouncer node commonly handles max_client_conn=10,000 if it’s ever needed (supported, planetscale.com) — headroom far beyond anything this decision requires.
Idle-connection cost only matters if the design changes. Hundreds/thousands of idle Postgres connections do cost real memory at scale — AWS RDS measurements show ~5GB at 500 idle connections, ~10–14.5GB at 1,000 (confirmed, aws.amazon.com/blogs/database) — but this is the scenario the pooled-HTTP design already avoids by construction, not a live risk under the current architecture. It would only become relevant if some future change let agents open Postgres connections directly instead of going through the Encore.ts service.
SQLite WAL comfortably absorbs FleetOps’ hook-ingest load. Real benchmarks: 70,000–100,000 write-tx/sec single-thread for small records; a real-world schema-with-joins benchmark sustained 8,378 writes/sec at 64 concurrent workers with p99 <10ms (confirmed, shivekkhurana.com). Degradation (“database is locked”) only appears past ~100 concurrent writer threads hammering simultaneously — not hundreds of events/sec spread over time — and an app-level write-mutex/queue (which FleetOps’ single ingest process already is) sidesteps this ceiling entirely (confirmed, tenthousandmeters.com). One real footgun: checkpoint starvation if any long-lived reader (e.g. a persistent dashboard connection) holds an open read transaction — mitigate by keeping Board UI reads short-lived (confirmed, sqlite.org/wal.html).
SKIP LOCKED has no SQLite equivalent worth taking. SQLite lacks row-level locking entirely; the closest analog (BEGIN IMMEDIATE + UPDATE...RETURNING) serializes all claims through SQLite’s whole-database writer lock — correct and atomic, but strictly serial, not Postgres’s true concurrent row-level claiming (confirmed). No memory evidence in this research justifies deviating from the already-pinned Postgres/Drizzle design for WorkLedger’s claim API — the DB layer was never the footprint problem (supported: Postgres’s total footprint is a >100x gap below the agent-fleet RSS this constraint protects headroom for).
pg_trgm/tsvector is adequate for Atlas, no alternative search engine justified. Supabase’s own benchmark on a ~4,000-document corpus: 0.9ms–19.09ms across 10 test queries, “complexity neutral — no new systems needed” (confirmed, supabase.com/blog/postgres-full-text-search-vs-the-rest). pg_trgm is known to degrade at genuinely large scale (PGroonga, dedicated engines win by 10x+) but that ceiling is irrelevant at “thousands of Q&A rows” (supported).
DuckDB: no role yet. Postgres handles light analytics on small tables fine; adding DuckDB is unnecessary overhead until analytics queries contend with OLTP or corpora hit millions of rows — pure YAGNI at WorkLedger/Atlas’s current scale (supported, motherduck.com).
5. Observability footprint evidence
The planned OTel-Collector → Prometheus → Loki → Grafana docker-compose stack has real evidence against it at this scale.
| Component | RSS as planned | Cut available |
|---|---|---|
| OTel Collector | ~50MiB constant floor even near-idle; ~180MB/0.15 CPU at 1,000 spans/sec (supported, dash0.com / oneuptime.com) | Eliminate. Claude Code’s own docs confirm per-signal endpoint override (OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) and explicitly document “any OTLP-accepting backend, or a self-hosted collector” as options — the collector is provably optional (confirmed, code.claude.com/docs/en/monitoring-usage.md). Prometheus 3.0+ and Loki 3.0+ both ship native OTLP receivers for exactly this single-source case. |
| Prometheus | ~200–420MB RSS floor even at small deployments, independent of retention — dominated by the current ~2h in-memory head block + Go GC overhead (supported, prometheus-alert-generator.com) | Replace with VictoriaMetrics single-node: flat 4.3GB vs. Prometheus’s 14GB-stabilized/23GB-spike at a matched 2.8M-series/3,400-target workload (supported, valyala.medium.com) — no published number below ~1M series exists (assumption at Robert’s tiny scale), but the ratio holds directionally. |
| Loki | ~1.5GB idle, monolithic mode, no active ingestion — the single largest line item in the entire observability bill, for content-free tool-decision/error-event text (supported, Grafana community forum “Loki Monolithic Memory Consumption”) | Replace with VictoriaLogs (0.6–2GiB vs. Loki’s 6–7GiB steady-state at a matched 500GB/7-day benchmark, ~12x faster cold-cache queries, confirmed/supported, truefoundry.com) — or skip a log DB entirely and reuse the SQLite HookEvent table FleetOps is already writing, via a Grafana SQLite datasource (ponytail rung 5: already-installed dependency). |
| Grafana | 512MB min / 1 CPU; idle RSS creeping release-over-release (~180Mi→330Mi between two adjacent versions, confirmed, github.com/grafana/grafana/issues/123017) | Keep — embedded SQLite, no extra DB process, free official Claude Code dashboard (ID 25255, explicitly lists VictoriaMetrics as a supported backend). One real ambiguity: WorkLedger’s and FleetOps’ docs each describe “a local Grafana” without cross-referencing each other — must be enforced as one shared instance, or the footprint math below silently doubles. |
| Temporal dev server | ~112MB (start-dev-class, 23 threads, confirmed, community.temporal.io 2023 data point) | Absent from v1 by design, correctly. Both WorkLedger’s projection and Atlas’s signal-resume are explicitly deferred to v2 — the audit risk is someone reflexively running temporal server start-dev “just in case,” adding 100–400MB for nothing used in v1. |
Net numbers: total always-on RSS as literally documented across all four products sums to ~2.3–2.4GB (otel-collector + Prometheus + Loki + Grafana + ingest/board + Encore app + encored daemon + local Postgres, derived estimate). Applying the collector-elimination + Loki replacement/removal + running the Encore service as a built binary (encore build docker) instead of the encore run dev daemon cuts that to ~550–700MB — roughly a 70–75% reduction — before a single Claude Code agent process starts (hypothesis: derived synthesis, process-count-audit packet). This is the observability bill-of-materials the stack decision should budget against.
6. Web-surface evidence
For a 1–2 operator, localhost-only, always-on board pushing 1–5 SSE updates/sec, the evidence converges on a narrow shape.
Shell-script footprint, if a client-side helper is used at all:
| Option | Size (gzipped) | Notes |
|---|---|---|
| htmx | ~14–17.6 KB | Zero build step; recommended for “forms, tables, dashboards, CRUD” (solodevstack.com) |
| Alpine.js | ~10–15 KB | Behavior-only, no SSE primitive of its own |
| Datastar | 11.75 KiB | SSE-signal-patch native; merges htmx+Alpine-style behavior in one script (data-star.dev) |
Protocol choice: SSE vs. WebSocket per-message latency differs by only ~3ms (negligible at 1–5 msg/sec, confirmed, websocket.org/comparisons/sse) — throughput/latency is not the deciding factor; connection simplicity and reconnect semantics are (below). PocketBase’s own SSE implementation closes idle connections after 5 minutes of silence (confirmed, pocketbase.io/docs/api-realtime) — a reference point for how aggressively a Board UI’s SSE endpoint can safely time out quiet connections without surprising the browser’s native reconnect.
- htmx or Datastar over any SPA framework. htmx is one script tag (~14–17.6KB gzipped), zero build step, and 2025-era sources explicitly recommend it for “forms, tables, dashboards, CRUD operations” (supported, solodevstack.com). Datastar (11.75 KiB) is purpose-built around SSE-driven signal patches and has existing Go+SSE “real-time system stats dashboard” writeups nearly identical to the FleetOps use case (supported, data-star.dev / kitemetric.com). SvelteKit/React SPAs are consistently framed as tools for complex client-state apps (drag-drop, canvas, offline), not this shape, though no direct server-memory benchmark vs. htmx was found (hypothesis).
- SSE over WebSocket. Less server code, no frame masking/ping-pong/protocol negotiation, and native browser reconnect +
Last-Event-IDreplay with zero client code (confirmed, ably.com / websocket.org). SSE’s one real weakness — proxy/CDN silent buffering — is structurally absent on a pure loopback connection (confirmed). - No aggregation-app precedent; thin nav shell over separate surfaces instead. The observed 2025–2026 small-team pattern is Beszel+Dozzle-style cross-linked single-purpose tools, not one app reading N databases directly (supported, denshub.com: “the most common self-hosted monitoring stack in 2026”). Grafana’s own dashboard/panel/data links already implement this navigation pattern for free (confirmed, grafana.com docs). iframe stays reserved for Grafana specifically (which natively supports embed tokens) — Web Components load ~4.5x faster than iframes for anything else (supported, dp-lewis.medium.com).
- Single static-embedded binary, zero Vite/npm pipeline. Go
go:embedor Rustrust-embed/axum-embedserving plain htmx/Datastar HTML avoids a second toolchain agents must keep in sync. Beszel (Go+PocketBase+SQLite: hub ~45–100MB RAM including DB+API+web UI+logic in one process, agents 6–23MB idle, 10k+ realtime connections via SSE) is a directly-matching production precedent for exactly this shape (confirmed, ramnode.com / akashrajpurohit.com). - TUI-first, thin web companion on the same backend. Glances’
-wflag and Hermes TUI’s “SSH dashboard, optional Web UI” split are the documented pattern for solo/small-team agent-session monitoring (supported) — supports extending the existing Rustfleetops/GoghmonitorTUIs with a thin web renderer over the same SQLite ingest, rather than building a separate web-first system. - Language for the web shell itself: Go or Rust, not a second Node/Bun process. Given hundreds of concurrent 200–500MB Node agent processes already loading the host, and Encore.ts’s own blog documenting 2–4ms IPC overhead per request across its Node↔Rust sidecar boundary (confirmed, encore.dev/blog/rust-runtime) — a rationale against reusing Encore.ts as the general web-overview shell, not just against Node — the web shell should match the existing TUI languages (10–50MB idle) rather than add a third JS runtime process to the host. A directionally consistent (if not directly comparable) data point: Rust agent-framework tooling runs ~10MB memory/~4MB binary/~50ms cold start vs. 85–450MB for Python equivalents (hypothesis, zylos.ai) — the same order-of-magnitude gap that separates Go/Rust from Node across every table in this doc.
Embedding an SPA vs. embedding plain static assets: go:embed-ing a Vite-built SPA into a Go binary (or rust-embed/axum-embed for Rust) is a well-documented, production-used pattern for internal dashboards where deployment simplicity outweighs CDN/rolling-deploy needs (supported, dermitch.de) — but it still means maintaining two toolchains (Go/Rust build + npm/Vite build) side by side. Skipping the SPA step and embedding plain static htmx/Datastar HTML+JS directly is a strictly smaller pipeline: one language, one build command, no node_modules on the host at all (hypothesis, synthesized from the embed-pattern sources plus htmx/Datastar’s own no-build-step design).
Cross-references: case studies and agentic-language-fit evidence (13-language Claude Code benchmark, Rust repo-level resolve rates, Grit/C-compiler scale reports) live in 02-case-studies.md. Candidate stack options built from this evidence are in 03-options.md; adversarial steelmanning in 04-steelman.md; the 5-minute decision is 00-SYNTHESIS.md.