Skip to content
GRPNR.

Factory apps validation — Synthesis & Decision

Recommendation: Option C — Two-Lane Thin Factory. Build 4 things (work ledger, triage layer, fleet monitor, dual-run harness) ON Zaruba’s substrate under a CODEOWNERS-partitioned lane; scope the knowledge graph down to an audit-evidence extractor; drop the rewrite engine, scaffolder, standalone decommission app, and MBUS bridge. Confidence: high on the portfolio shape and the drops; medium on placement (inside monorepo-development’s internal plane vs Robert-owned repo) — that hinges on assumption A1 (Tomas accepts the partitioned lane), with A-lite (own repo, API-only integration) as the named fallback.

Context

SSH access to groupon2 landed 2026-07-18 and changed the ground truth: Zaruba’s greenfield is a rebuild-from-audit program (legacy = telemetry evidence, never ported; dual-run/shadow to parity; kill gates), not the mass-porting program the original batch-1/2 research assumed. He has already built the two-plane Encore architecture, a tasks human-in-the-loop service, a full Temporal workflow-management platform, and an MCP/docs skeleton [01-deep-dive [E1]–[E3]]. Infra is 0% provisioned behind a human-only billing gate [E5]. The legacy sample (1.05M active backend LOC) shows MBUS load-bearing, EOL runtimes, and two half-finished stranglers rotting in code [E6]. Tomas is decision-bound; review capacity is the system’s scaling constraint; Robert’s committed lane is execution tooling + fleet monitor [E8].

Options considered

Option One-liner Weighted score
A — Full Factory All 9 apps standalone in a Robert-owned plane 2.40
B — Zaruba-Native Extension No factory; thin extensions inside Zaruba’s repos only 3.35
C — Two-Lane Thin Factory 4 builds on Zaruba’s substrate, partitioned lane, rest scoped/dropped 4.45
D — Defer-Most Monitor + assessment now, everything else after M4 3.00

Decision matrix

Criterion Wt A B C D
Simplicity & operability 20% 2 — two Temporal platforms, 9 apps to run (04-A-pros#1,#5) 4 — fewest parts, but Robert’s fleet ops degraded by gated timelines (04-B-pros#3) 4 — one substrate, 3 thin services + 1 real build (03-C) 3 — simple, but fleet runs unmanaged/manual (04-D-adv concession)
Agentic-development fit 20% 3 — full control but two convention islands to teach agents (04-A-adv concession 2) 3 — inherits strong conventions; agent-scale features blocked on human-gated paths (04-B-pros#2) 5 — one convention set, typed Encore+Drizzle+PG, structural partition [E2] 2 — fleet without ledger/triage is illegible to operate (03-D)
Domain fit (DDD) 15% 2 — duplicate task/workflow bounded contexts (04-A-pros#1,#3) 3 — Robert owns no context; semantic squatting in Tomas’s aggregates (04-B-cross) 5 — work-unit lifecycle is a real, Robert-owned context; human-task + workflow invariants stay in Zaruba’s aggregates (03-C) 2 — no new contexts at all
Evolution & headroom 15% 3 — can grow, but shadow-platform dead-end risk (04-A-pros#8) 2 — hard ceiling: agent-scale features must argue into core-plane services (03-B) 4 — per-app droppable; lanes extend to old-estate 1→N split later (03-C) 3 — max optionality, idle capability
Testability (TDD) 10% 4 — full isolation, but harness only contract-testable across repos 4 — inherits vitest discipline 5 — house vitest+Docker-PG pattern + harness contract tests vs legacy lab (03-C) 3 — monitor layer only
Delivery speed 10% 1 — months to first value (03-A) 4 — days, but gated on billing/reviews (04-B-pros#3) 4 — weeks (03-C) 5 — immediate, tiny scope
Cost 5% 1 — 9 apps + dup ops (03-A) 5 — near-zero marginal (04-B-adv#7) 4 — 3 thin + 1 real build (03-C) 5 — minimal
Risk & reversibility 5% 2 — 9-app sunk-cost surface (04-A-adv#5 cuts both ways) 3 — reversible commits, but capability gaps accrue (04-B concessions) 4 — each app independently droppable; A-lite fallback (04-C-cross) 4 — nothing to back out, but drift risk unbounded (04-D-pros#8)
Weighted total 2.40 3.35 4.45 3.00

Sensitivity: C leads B by 1.10 — no ±5pp swap of adjacent weights flips the winner (largest movement ~0.15). Decision is robust; no prose tiebreaker needed.

Per-app verdicts (the actual question)

# App Verdict Shape
1 Migration ledger / orchestrator BUILD THIN One Encore service, internal plane, Postgres SSOT (unit × state × attempts × cost × blocking-question × disposition). One-way projection from workflow-management/Temporal events — Temporal stays the only execution truth. Absorbs the claim-before-work queue (research/12) and the decommission ledger as row types.
2 Question-triage BUILD-ON-ZARUBA Robert-owned dedup/rate-limit/knowledge-write-back service in the internal plane; Tomas’s tasks service gets ONE negotiated PR (agent-question TaskType). The portfolio’s R&D bet — unsolved in public literature [E7]. Ship naive exact-match dedup first, embeddings second.
3 Fleet monitor ADOPT + thin layer (confirmed, unchanged from research/12) OTel→Prometheus→Grafana, fork claude-code-otel, dashboard 25255, local-first (docker-compose) until the billing gate clears; thin layer = rework rate, cost/merged-unit, queue depth, stall detection. Robert’s committed deliverable — first to ship.
4 Verification / dual-run harness BUILD — flagship (promoted again) Staged scope: Orders-domain adapter generalizing Zaruba’s 3-oracle reconciliation first; generalize only per flagship gate. Includes the legacy-runtime lab (pinned EOL images) and MBUS shadow-taps where a domain’s parity requires event comparison.
5 Codebase knowledge graph MCP SCOPE DOWN Legacy-audit evidence extractor (tree-sitter over legacy sample, feeds HOT/COLD audits). Productize to MCP only if audit demand recurs at flagship 2. Extraction quality gets verified sampling — it feeds irreversible scope decisions (04-C-pros#6).
6 Declarative rewrite engine DROP Doctrine ports no legacy code [E1][E4]. Re-decision trigger: old-estate 1→N Encore split enters Robert’s lane.
7 Service scaffolder DROP Zaruba’s skills cover it. Re-decision trigger: factory needs a 5th service.
8 Decommission ledger MERGE into #1 Disposition row type per legacy component (MIGRATED/DROPPED/DEFERRED/DEAD + owner + date). Directly targets the in-house failure mode: two rotting half-stranglers [E6].
9 MBUS bridge DROP as app Harness-internal shadow-taps only. A standalone bridge reverses Zaruba’s explicit rejection [E4] — fastest route to collision (04-A-pros#2).
flowchart LR
  subgraph Z[monorepo-development — system of record]
    subgraph CP[core plane — Tomas DRI]
      ZT[tasks svc]; ZW[workflow-management]; ZO[reconciliation oracles]
    end
    subgraph IP[internal plane — factory lane, Robert DRI via CODEOWNERS]
      WL[work-ledger svc<br/>PG SSOT + claim queue + dispositions]
      QT[triage svc<br/>dedup / rate-limit / write-back]
      VH[dual-run harness<br/>shadow diff + parity kernel]
    end
  end
  subgraph OBS[observability — local-first, cloud when billing clears]
    G[(Grafana + thin fleet layer)]
  end
  QT -- one negotiated TaskType PR --> ZT
  ZW -- events, one-way projection --> WL
  VH -- generalizes per flagship --> ZO
  VH -- shadow taps --> LAB[legacy runtime lab<br/>pinned EOL images]
  KG[audit evidence extractor] --> WL
  Fleet[Robert's agent fleet] -- claims units --> WL
  Fleet -. OTel .-> G

Critical flow — dual-run parity (highest-risk flow)

sequenceDiagram
  participant L as work-ledger
  participant F as fleet agent
  participant H as harness
  participant LG as legacy lab
  participant N as new service (greenfield)
  L->>F: claim unit (atomic)
  F->>H: request characterization
  H->>LG: replay recorded traffic (+ MBUS shadow-tap if needed)
  H->>N: same traffic
  H->>H: diff responses + events (3-oracle pattern)
  alt parity < threshold
    H->>L: unit blocked + evidence
    L->>QT: mint agent-question task
  else parity ≥ threshold
    H->>L: unit verified → PR lane
  end

Evolution path

flowchart LR
  V1[v1: monitor local + ledger + naive-dedup triage + Orders harness adapter]
  V2[v2: cloud observability + embedding dedup + Deal-Factory harness adapter]
  V3[v3: KG productized to MCP + old-estate 1toN lanes in ledger]
  V1 -- billing gate clears AND M4 gate passes --> V2
  V2 -- audit demand recurs at flagship 2 AND ledger >500 units --> V3

Risk register (from the winner’s prosecution, 04)

Risk Likelihood Detection signal Mitigation / accepted
Harness doesn’t generalize past Orders (04-C-pros#1) Medium 2nd domain adapter costs >2× the first Staged scope per flagship gate; shared kernel minimal; accept per-domain adapters as the product
Ledger drifts from Temporal execution state (04-C-pros#2) Low–Medium Weekly reconciliation audit mismatches One-way projection by construction; Temporal sole execution truth; ledger owns only business fields
Triage graft resented / TaskType PR stalls (04-C-pros#3,#4) Medium PR unmerged >2 weeks Layer lives in Robert’s service; fallback: standalone TaskType-free inbox, integrate later
A1 fails — no partitioned lane granted Medium No OWNERSHIP.md agreement within 2 weeks of the Tomas conversation Named fallback: A-lite (Robert repo, API-only) — same portfolio, different placement
Audit-extractor errors propagate into signed scope (04-C-pros#6) Medium Spot-check disagreement rate >5% Verified sampling on every extraction batch; confidence tags per fact (Zaruba’s own MEASURED/MODELED convention)
Fleet sized beyond evidence (64-concurrency ceiling) [E9] High if ignored Rework rate rises with concurrency Ramp 10→50→64 gated on ledger metrics; 1000 = queued units, never concurrent editors

Pre-mortem (12 months later, it failed because…)

  1. The lane never existed. Tomas didn’t want factory services in his repo; negotiation dragged; Robert built nothing for a quarter. Detection: A1 unresolved 2 weeks after the answer draft (research/14) is sent. Response: trigger A-lite immediately — the portfolio is placement-agnostic by design.
  2. Harness became the schedule. Every domain needed bespoke oracles; parity infrastructure consumed the year; fleet throughput never mattered. Detection: adapter-cost curve rising at domain 2. Response: cap harness investment per flagship; fall back to impact-selected tests (~70% cut [E9]) where full parity is uneconomic.
  3. Two sources of truth. Ledger and Temporal disagreed during an Orders incident; trust in both collapsed. Detection: first reconciliation mismatch. Response: projection-only invariant enforced by code review rule + weekly audit query.
  4. Triage shipped clever, not useful. Embedding dedup was tuned for weeks while stakeholders drowned; questions/unit never fell. Detection: questions/unit flat after 4 weeks. Response: naive-first ordering is in v1 by decision, not preference.
  5. Blind cloud scale-up. Billing cleared, fleet scaled to hundreds concurrent, rework exploded — the 64 ceiling was real. Detection: rework rate vs concurrency curve. Response: concurrency gated on ledger metrics per ramp plan; hard cap until data says otherwise.

Assumptions

# Assumption Fallback
A1 Tomas accepts a CODEOWNERS-partitioned factory lane in the internal plane A-lite: Robert-owned repo, API-only integration (portfolio unchanged)
A2 No generic dual-run machinery exists yet beyond merchant-accounting oracles Harness scope shrinks to adapters — still Robert’s
A3 Billing gate clears in weeks Local-first observability is the product until then
A4 Legacy telemetry access for shadow-recording obtainable Static characterization + recorded-fixture replay until then
A5 Sima’s groupon2 role unknown Re-run collision protocol with 3 parties if active

Program hardness — re-score

Easier than the batch-1/2 blueprint assumed, harder in different places. Gone: 65M-LOC codemod lane, mass porting, MBUS bridge, 1000-concurrent orchestration (replaced by 1000 queued units at ≤64 concurrent). Zaruba’s substrate removes ~2 apps of build. Remaining hardness concentrates in: (1) harness generalization across EOL estates — the hardest technical piece and now the flagship; (2) the two-lead integration — political/collaboration risk is the new top risk, mitigated structurally (CODEOWNERS lane, one-way projections, single negotiated PRs); (3) legacy-runtime resurrection for characterization; (4) triage dedup R&D. Robert’s factory build: weeks, not months (3 thin services + staged harness + dashboards). Fleet spend stays dominated by execution tokens, anchored by Bun ($165k/1M LOC) — the greenfield rebuild consumes far fewer agent-edit tokens than the old porting model implied.

Architect review addendum (grpn-eng:encore-architect, 2026-07-18, post-decision)

Portfolio shape confirmed; four corrections adopted:

  1. Projection source (HIGH): no core→internal event path exists (decision log #36: “no cross-plane signal bridge yet”). Work-ledger projects from the internal-plane workflow execution stream (workflow-proxy interceptor: _ensureInProgress, _progressUpdate, workflowStatusUpdate…) — fleet unit lifecycles run as internal-plane workflows on Zaruba’s worker framework. Zero core-plane asks.
  2. Ledger scope: claim/attempts/blocking-question reuse Temporal task queues + tasks service natively (TaskWorkflowStamp); work-ledger keeps ONLY business fields (cost, disposition, evidence links) + external-claim endpoint for non-Temporal Claude agents.
  3. Triage v1 = zero PRs: existing INFORMATION_REQUEST + assigneeGroupSlug “agent-questions” queue. TaskType enum PR demoted to optional v2. Real negotiation = inbox pollution, solved by dedup/rate-limit + queue filter.
  4. Harness shape: parity kernel + legacy-runtime lab = workspace package + CLI/CI runner outside Encore (Cloud Run can’t run pinned EOL images or tap MBUS); Encore surface = parity-result rows in work-ledger. Legacy creds stay in the lab, never in Encore secrets.

Also: name is always work-ledger (merchant-accounting owns “ledger”); lead the A1 ask with OWNERSHIP.md + directory convention, CODEOWNERS later on his startup-mode schedule; check old-estate _core_system/ai-agents overlap answer before ledger lands; recorded legacy traffic = PII/PCI → security-architect sign-off on fixture retention/masking; explicit columns not defaultTableColumns for append-only rows, keyset pagination, registerInternalService for each new service. Cross-plane clients stay lazy (app ids empty until billing/linking) — v1 runs on local encore run.

First implementation steps (TDD)

  1. Failing test first: work-ledger — vitest integration test: claim-unit is atomic under 2 concurrent claimers (Docker PG, advisory lock); then the projection test: replayed workflow-management event stream produces exactly-once ledger transitions.
  2. Walking skeleton: ledger service with 3 endpoints (claim / transition / block-with-question) + Grafana panel reading its queue-depth view — deployable local-first, no billing gate dependency.
  3. Monitor v1: docker-compose OTel+Prometheus+Grafana with forked claude-code-otel config; OTEL_RESOURCE_ATTRIBUTES per-worktree tags; dashboard 25255 imported; first custom panel = cost/merged-unit fed by ledger.
  4. Triage v1: contract test — same normalized question fingerprint twice → second auto-resolves from first’s answer (exact-match dedup); only then the TaskType PR to Tomas.
  5. Harness v1: characterization kernel test — record fixture from legacy lab (one EOL Rails endpoint in a pinned container), replay against a stub, diff = zero; then wire the Orders-domain adapter against Zaruba’s oracle interfaces.
  6. Send research/14 answer (Robert approves first) with the A1 ask embedded — the lane, OWNERSHIP.md, and the one TaskType PR as the only three asks.