11 — Build vs buy
Sourcing decision for work-ledger. Eight options scored; one recommendation, one runner-up, one flip trigger. Reconciled explicitly with plan 001’s BUILD THIN verdict and the advisory board’s pushback. All external claims dated; evidence tiered (confirmed fact / supported interpretation / hypothesis / assumption / unknown). Cost is measured in Robert-hours and tokens — his review capacity is the scarce resource [E8], not dollars.
Bottom line
Build-thin — own the durable core, compose the existing substrate. One migration + ~150 LOC + 2 SQL views: the durable business-columns table (unit × state × disposition × cost × attempts × evidence × blocking-ref), an atomic FOR UPDATE SKIP LOCKED claim guard wrapped in typed endpoints, and two read views. It reuses Temporal for execution truth, the existing workflow_executions projection, tasks INFORMATION_REQUEST for blocking questions, and Grafana for reads — building none of that. Weighted score 4.53, ahead of the runner-up (concierge v0, 3.19) by 1.34; robust to every realistic weight change.
This is plan 001’s BUILD THIN verdict, sharpened and narrowed in three specific ways (§8): scope shrinks below plan 001 (own only what Temporal purges, not a parallel state machine); packaging drops the “fourth product” framing (ship it as the schema behind Robert’s committed fleet monitor, which needs the identical table on day one); and sequencing is resolved (monitor and ledger-table ship together, table-first).
The eight options
| # | Option | One-liner | Verdict |
|---|---|---|---|
| 1 | Build from scratch (thin) | Thin Encore service, internal plane; own the durable table + claim guard + views, reuse Temporal/tasks/Grafana | ✓ RECOMMENDED |
| 2 | Build on OSS | Adopt beads / pgmq / graphile-worker / task-master as the foundation | ✗ Eliminated |
| 3 | Buy (COTS) | License a commercial product (Linear / Jules / Devin / GitHub / Jira) | ✗ N/A — nothing sold covers the bundle |
| 4 | Integrate multiple existing | Temporal search-attributes + visibility + tasks + Grafana + bare SKIP LOCKED — no new durable table |
✗ Fails the retention wall |
| 5 | Partner / white-label | External vendor co-builds/rebrands | ✗ N/A — internal, 2 human users |
| 6 | Concierge (managed-svc + light software) | Robert hand-curates a markdown/CSV ledger; SKIP LOCKED convention for claim |
✗ Runner-up; fatal ongoing cost |
| 7 | Process / convention only | Worktree discipline + OWNERSHIP.md + git as the record; no software |
✗ Documented worst case at concurrency |
| 8 | Do nothing / status quo | Ship the monitor, track nothing per-unit; defer | ✗ Leaves the failure mode unmitigated |
Why seven fall out (elimination logic)
flowchart TD
Start[8 sourcing options] --> Q1{A product sold that covers the bundle?<br/>claim + disposition + cost + one-way projection}
Q1 -- No product exists in 2026 --> EB[✗ 3 Buy — nothing to license<br/>nearest adjacents fail ≥2 of 4 required props]
Q1 -- Internal, 2 users --> EP[✗ 5 Partner/white-label]
Q1 -- Foundation only --> Q2{Cheaper to own than to depend on?}
Q2 -- SKIP LOCKED = one SQL clause<br/>beads PG backend rolled back 2026-07-16 --> EO[✗ 2 Build-on-OSS<br/>≤25% scope coverage, dependency churn]
Q2 -- Yes, own it --> Q3{Durable past Temporal's 72h retention wall?}
Q3 -- No, SA purged + poverty B1/B2 --> EI[✗ 4 Integrate-only]
Q3 -- Yes --> Q4{Safe under concurrent agent writes?}
Q4 -- No, last-writer-wins silent drop --> EC[✗ 6 Concierge / ✗ 7 Process-only<br/>arXiv:2606.19616]
Q4 -- No, estate keeps rotting --> EN[✗ 8 Do nothing<br/>two stranglers already [E6]]
Q4 -- Yes, DB-enforced --> WIN[✓ 1 Build-thin<br/>own the durable core, compose the substrate]
Per-option treatment (terse)
| # | What it is | Fatal flaw / why not | Evidence |
|---|---|---|---|
| 1 | Thin durable table + typed SKIP LOCKED claim endpoints + 5 views, riding Temporal/tasks/Grafana |
None fatal. The one option that clears all four gates. Cost: days of one agent’s work, one small PR to review. | oss/temporalCase digests; [E10] Postgres-first |
| 2 | beads (Dolt) / pgmq / graphile-worker / river / task-master as foundation | Confirmed fact: no candidate exceeds ~25% of the 5-part scope; the atomic-claim slice they cover well is a single SKIP LOCKED clause — cheaper to write than to integrate. beads’ Postgres backend merged 2026-07-10 (PR #4601), rolled back 2026-07-16 (PR #4847, two days before this research), Dolt-only consolidation 2026-07-18 (PR #4881, same day); beads has live multi-machine merge-conflict bugs (#2466/#2765) and zero chaos-test coverage on its concurrency path. Dolt alongside a Postgres-first constitution is constitutional friction. task-master: unknown — no evidence in any research pass. |
06-open-source-analysis.md; oss/priorArt digests |
| 3 | License Linear / Jules / Devin / GitHub Issues / Jira | Confirmed fact: nothing sold is a work-ledger. Nearest adjacents each fail ≥2 of the four required properties (durable past 72h / disposition taxonomy / per-unit cost / DB-enforced claim). Linear (closest licensable) = optimistic assignee, not compare-and-swap → race-prone at 64; no per-unit cost; re-encodes attempt/disposition/evidence as custom fields inside an “issue” model whose vocabulary this plan bans; external SaaS holding PII/PCI evidence links. Jules has the closest concurrency ceiling (60, Ultra $124.99/mo, 2026) but is a coding-agent runner, not a ledger. GitHub Issues as the claim substrate is a documented 2026 anti-pattern (5k REST req/hr → 8.4× overrun at 50 sessions). |
05-competitive-analysis.md; competitive digest (b) |
| 4 | Temporal SA + visibility + tasks + Grafana + bare SKIP LOCKED (no new durable table) |
Confirmed fact: blended ~30% coverage; two structural breakpoints. B1 retention wall — 72h is the operate-skill’s namespace-create example; nothing is provisioned yet — retention is a tuning knob, and the structural purge argument stands at any realistic setting; Temporal purges closed-workflow data from persistence and visibility within days; Archival is replay-only blobs, not a queryable table. Kill gates are M4/M8/M12 (months). B2 SA poverty — 28 custom attrs/namespace, 2 KB / 255 chars; an evidence-URL list overruns 255 chars. Also needs shell unit-workflows + a retention-racing snapshot cron — more moving parts than the table it avoids, and still fails the read. | temporalCase digest B1–B5 |
| 5 | Partner / white-label | N/A — internal tool, two human users, no commercial surface. | README §scope |
| 6 | Robert hand-curates a markdown/CSV ledger; SKIP LOCKED convention for claim (the concierge v0) |
Cheapest upfront (hours) and zero A1 footprint — a genuine week-0 spike. But ongoing cost scales O(units) in Robert-hours — fatal at ~1000 units, and his hours are the scarce resource [E8]. Agents cannot safely write a shared markdown file concurrently: last-writer-wins silently drops one agent’s field write (arXiv:2606.19616). Good as a v0 probe (see runner-up); not a v1. | priorArt digest §2; [E8] |
| 7 | OWNERSHIP.md + worktree discipline + git as the record; no software |
Confirmed fact: advisory-lease/lock-only produced the highest redundant-rediscovery count of any tested condition (180 instances) — mutual exclusion stops collision but not re-doing finished work; you need a durable “already done” record. Component-tier dispositions fit the existing ADR/decision log well; unit-tier (~1000 rows with cost + evidence) does not — hand-authoring an ADR per unit is the “worse Linear” smell. | priorArt digest §2, §4; temporalCase §process-substitutes |
| 8 | Ship the monitor, track nothing per-unit | Leaves exactly the failure mode work-ledger exists to close — the estate already carries two half-finished stranglers rotting untracked [E6]. And the monitor’s own thin layer (rework rate, cost/merged-unit, queue depth, stall) needs a durable per-unit store the fleet writes to on day one — so “do nothing” isn’t even coherent with the committed deliverable. | [E6]; research/12 §2; temporalCase §crossover |
Weighted decision matrix
Weights reflect the scarce resource (Robert-hours) and the load-bearing requirements (reliability at 64-concurrent, agent-facing UX, A1 politics). Scores 1 (poor) – 5 (excellent). Option 5 (Partner) excluded — N/A.
| Option | TTV .12 | Cost↑ .12 | Maint .10 | Flex .08 | UX-A .12 | Rel64 .15 | Sec .06 | Dep .08 | Port .05 | Rev .05 | A1 .07 | Total |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 Build-thin | 4 | 4 | 4 | 5 | 5 | 5 | 4 | 5 | 5 | 5 | 4 | 4.53 |
| 6 Concierge | 5 | 5 | 1 | 3 | 1 | 1 | 3 | 5 | 4 | 5 | 5 | 3.19 |
| 7 Process-only | 5 | 5 | 2 | 2 | 1 | 1 | 4 | 5 | 3 | 5 | 5 | 3.22 |
| 8 Do nothing | 1 | 5 | 5 | 1 | 1 | 1 | 5 | 5 | 1 | 5 | 3 | 2.78 |
| 4 Integrate-only | 4 | 3 | 2 | 1 | 2 | 2 | 4 | 4 | 2 | 3 | 2 | 2.61 |
| 2 Build-on-OSS | 2 | 3 | 2 | 2 | 3 | 3 | 3 | 2 | 2 | 3 | 3 | 2.57 |
| 3 Buy | 2 | 3 | 3 | 2 | 2 | 1 | 2 | 2 | 1 | 2 | 2 | 2.02 |
Legend: TTV = time to value; Cost↑ = upfront (Robert-hrs + tokens); Maint = ongoing maintenance; Flex = flexibility; UX-A = UX for agents; Rel64 = reliability at 64-concurrent; Sec = security; Dep = vendor/dependency risk; Port = data portability; Rev = reversibility; A1 = strategic fit with the A1 lane politics.
Read of the matrix. Build-thin wins by dominating exactly the two criteria the product exists for — reliability at 64-concurrent (DB-enforced SKIP LOCKED + durable attempt log) and agent UX (typed endpoints, full-state responses, DB-rejected illegal transitions, errors that name the violated invariant [07-ux-ui.md]) — while staying near-top on cost and reversibility. The cheap non-software options (concierge, process-only) score 2nd/3rd only by inflating on cheapness (TTV, upfront, reversibility, A1-fit) and collapsing on the two load-bearing columns. Buy scores worst: it pays procurement cost to acquire a race-prone, non-durable, wrong-vocabulary substrate.
Sensitivity
The 1.34 lead survives every realistic reweighting. Concretely: halving the reliability-at-64 weight (15→7.5%, spread onto TTV/upfront/vendor) leaves Build-thin at 4.16 vs concierge 3.49 — no flip. The winner flips only if reliability-at-64-concurrent and agent-UX are together driven to near-zero weight — i.e., only if the concurrent-collision problem is not real. That is not a weight-tuning question; it is the runner-up’s flip trigger (below). So the sensitivity analysis and the flip trigger are the same fact: Build-thin is correct unless the collision failure mode never manifests at scale.
The recommendation in detail
Build the irreducible core, compose everything else.
Build (own it):
- One durable table —
unit_id, state (enum), disposition (enum, null until terminal), cost, attempts, evidence_url, blocking_question_ref+ explicit append-only columns / keyset pagination per theauditloghouse precedent (repoRecon §4). This is the store Temporal purges at 72h and the store the fleet monitor needs on day one — the same table. - Atomic claim guard —
FOR UPDATE SKIP LOCKED(~20 lines), wrapped in a typed endpoint. Battle-tested pattern (Oban/pg-boss/graphile); irrelevant scaling caveats at 10→64 [priorArt §4]. - Typed endpoints (register / claim / heartbeat / transition / block / unblock / cost / dispose + reads — 10-architecture §6) [07-ux-ui.md]. Agents cannot safely run raw
SKIP LOCKEDSQL at concurrency without the guard wrapped in a full-state-returning, invariant-naming API — that wrapper is the agent UX, and in Encore a wrapper with endpoints requiresregisterInternalService. - 5 SQL views —
v_queue_depth,v_stall_list,v_cost_per_unit,v_rework,v_disposition_summary— read by the monitor’s Grafana; queue-depth and cost/unit are the two monitor-critical ones; the ledger owns the views, not the dashboards.
Compose (build none of it):
- Temporal = execution truth (industry-validated one-way-projection pattern: Netflix Conductor worker-listener, Vitess topology-aggregation [priorArt §implications]).
- Existing
workflow_executionsprojection = live execution state. - v1 keeps blocking questions in work-ledger’s own
blocking_questionstable with theunblockendpoint (10-architecture D7);tasksINFORMATION_REQUEST+ free-textassigneeGroupSlug“agent-questions” queue is the post-MVP path — no exposed endpoint exists today for creating one from outside Core — and stays zero core-plane PRs when it lands (confirmed free-textvarchar(128), no schema change — repoRecon §2). - Grafana (already mandated) = the entire human read surface [E5; research/12 §2].
grpn_-prefixed Bearer token, single header = external-agent auth, zero build (confirmed current as of same-day commiteada68c— repoRecon §6).
Do NOT build (explicit non-scope, matching 01-initial-recommendation §“what to remove” + the advisory board’s subtractions): no web UI / kanban / board; no parallel lifecycle state machine mirroring Temporal (that is pre-mortem #3, two sources of truth); no attempt-level cost until a measured rework-rate number demands the breakdown (ship unit-level sum first); no projection-from-Temporal-events in v1 (no real fleet-unit workflows exist yet — only one toy example; wiring it means editing Tomas’s hardcoded interceptor, an explicit v2+ workflow-proxy ask, not a passive read — repoRecon §3).
Runner-up + flip trigger
Runner-up: Concierge v0 (Option 6, 3.19). It edges process-only (3.22 in raw score) on the tiebreaker that matters: a hand-curated CSV/markdown ledger produces a structured artifact that migrates into the real table, whereas process-only leaves nothing to migrate. It is the cheapest possible test of the one assumption the entire advisory board (Jobs, Ive, Cagan, Torres, Christensen) flagged as untested: has a real collision or duplicate-claim actually happened at the fleet’s current ~10-agent scale, or is the 64-concurrent failure mode entirely modeled?
Flip trigger — keep the concierge v0 instead of building the table if, within a 1–2 week instrumented spike at current concurrency: (a) zero collisions / zero duplicate-work observed, and (b) Tomas confirms in writing he will read the ADR/decision log — not a Postgres table — at the M4 gate (closing open question #3, assumption, still untested).
Why it is the runner-up and not the pick: the trigger is unlikely to hold, because the fleet monitor — Robert’s committed, first-to-ship deliverable — needs the identical durable per-unit table on day one regardless of any collision (supported interpretation, high confidence; temporalCase §crossover, research/12 §2). So in practice the concierge spike is not an alternative to building — it is week-0 of the build: run the markdown probe this week to satisfy the board’s “test the riskiest assumption first,” and stand up the table in the same sprint because the monitor forces it anyway. The runner-up therefore collapses into the recommendation’s timeline rather than competing with it.
Reconciliation with plan 001 BUILD THIN + advisory pushback
I agree with plan 001’s BUILD THIN — the matrix confirms it decisively (4.53 vs 3.2 runner-up). No sourcing option other than build satisfies the requirement bundle: nothing to buy, OSS coverage tops out at ~30% (Temporal-native/hatchet), every other candidate ≤25%, integrate-only fails the retention wall, process/concierge fail concurrency-safety. Where I sharpen or mildly diverge from plan 001’s literal framing:
| Plan 001 said | I say | Why |
|---|---|---|
| “One Encore service, three endpoints” | Build-thin, 4 endpoints, and scope below plan 001: own only the business columns Temporal purges (disposition, cost, attempts, evidence, blocking-ref) — not a parallel state machine | temporalCase: mirroring Temporal’s lifecycle is the two-sources-of-truth pre-mortem #3. Claim/execution truth already have homes (workflow_executions, workflow-id uniqueness). |
| Framed as a “product” (§1 “accounting system, not an app”) | A durable schema + a claim API, packaged as the store behind Robert’s committed fleet monitor — not a separately-roadmapped fourth product with its own North Star and versioning | The monitor needs the identical table day one; the market has no “AI-fleet ledger” category because the value is the data model, not a service wrapper (competitive digest). This absorbs the Jobs/Christensen/prosecutor pushback. |
| Open question #4: “ledger before monitor, or monitor first?” | Resolved: same sprint, table-first. The monitor cannot compute cost/merged-unit / queue-depth / stall without the ledger table | temporalCase §crossover; research/12 §2 |
Where I explicitly disagree with the strongest advisory position (Jobs / prosecutor: “kill the standalone service — it’s four columns and a claim function on the monitor’s schema, no service needed”): they are right that “product” is inflated language and that the table must live with the monitor — I adopt both. They are wrong that no service wrapper is needed. Agents cannot safely execute raw SKIP LOCKED SQL at 64-concurrent; the atomic guard must be wrapped in a typed endpoint returning full state with invariant-naming errors (07-ux-ui.md), and in Encore that wrapper is a registerInternalService registration. So the disagreement resolves to a naming/packaging correction, not a build/no-build reversal: build endpoints-on-the-monitor’s-schema, don’t build a fourth roadmapped product. The ~150 LOC gets written either way (all pro-build and prosecution digests concede the durable table “gets built regardless”); the only real decision — standalone service vs monitor’s schema — is the org-boundary call I resolve in favor of the shared schema until a second fleet actually claims through it (Count 7 of the prosecution, assumption, untested).
The board’s unanimous demand — a real observed collision before full-scope build — is honored, not overridden: the concierge/instrumented spike (runner-up = week-0) tests it cheaply; disposition scope stays provisional and attempt-level cost is deferred until measured usage asks for it; but the table itself is not gated on the spike because the monitor forces it regardless.
Confidence & evidence tiers
- Confirmed fact: nothing sold covers the bundle (three research passes); Temporal 72h retention purges dispositions/cost/evidence; beads’ Postgres backend merged 2026-07-10, rolled back 2026-07-16, Dolt-only consolidation 2026-07-18; atomic claim = one
SKIP LOCKEDclause;tasks/grpn_-auth/Grafana reuse is zero-build (repoRecon). - Supported interpretation (high): Build-thin wins the matrix robustly; the durable table lives best as the fleet monitor’s schema, not a fourth product; the standalone-service-vs-schema question is an org-boundary call, not build/no-build (all pro-build and prosecution digests converge on this).
- Hypothesis: the concierge spike would reveal whether collisions occur at current ~10-agent scale — grounded in arXiv:2606.19616 (single unreviewed June-2026 preprint; high relevance, low peer-validation).
- Assumption (untested): A1 lane granted (fallback A-lite: Robert-owned repo, API-only); Tomas reads the disposition table at gates (open question #3).
- Unknown: task-master’s actual capability (no evidence in any pass); whether Linear’s
assigneewrite path offers any optimistic-concurrency guard (not found in docs).