Skip to content
GRPNR.

06 — Open-source foundation analysis

Answers brief §8. Question under test: is building work-ledger from scratch strategically irrational, given what’s on the shelf? Prior (01-initial-recommendation.md): no — it’s a schema + 3 endpoints on an existing substrate (Encore.ts + Postgres + Temporal), and every foundation candidate costs more integration than the build. This doc attacks that prior before conceding it. All repo metadata below (license, stars, forks, last-push) was pulled live via gh api on 2026-07-18 — confirmed fact, not carried from the research digest (that digest’s per-candidate detail did not survive into this pass; see ## Digest gap at the end). PR/issue narratives for beads were fetched the same way — confirmed fact with direct links, not inference.

The 5-part scope (the rubric every candidate is scored against)

Work-ledger’s mandate, per 01-initial-recommendation.md and the architect addendum (plans/001-factory-apps-validation/00-SYNTHESIS.md):

# Part What it means
1 Atomic claim Race-free, exactly-once assignment of a work unit to one agent, durable across process restarts
2 State machine Enum-constrained transitions, illegal moves rejected by the DB not by convention
3 Dispositions MIGRATED / DROPPED / DEFERRED / DEAD taxonomy + evidence link, durable for months (kill gates M4/M8/M12)
4 Cost attribution Per-unit, per-attempt cost column with durable roll-up
5 Projection One-way, exactly-once ingestion from Temporal’s execution stream for workflow-modelled units

No candidate below covers more than ~30% of this (~30% = Temporal-native/hatchet ceiling; every other candidate ≤25%) — confirmed by direct inspection, not just the research digest’s summary claim.

1. Candidates table

Candidate Repo License Last push (verified 2026-07-18) Health Coverage of 5-part scope Classification
beads gastownhall/beads MIT 2026-07-18 (same day) 25.4k★, 1.7k forks, 502 open issues, created 2025-10-12 ~25% (claim: partial; state: partial; disposition/cost/projection: none) Not recommended as work-ledger’s substrate; viable only for its original, separate role (cross-fleet claim queue, research/12 §1.2) — see §3
pgmq pgmq/pgmq PostgreSQL license 2026-07-15 5.0k★ ~18% (claim only, job-shaped not entity-shaped) Useful component (pattern, not dependency)
graphile-worker graphile/worker MIT 2026-07-08 2.3k★ ~18% (claim only) Useful component (pattern, not dependency)
river riverqueue/river MPL-2.0 2026-07-17 5.4k★ ~18% (claim only; Go — wrong runtime for Encore.ts) Useful component (pattern, not dependency)
hatchet hatchet-dev/hatchet MIT 2026-07-18 (same day) 7.5k★ ~30% (claim + workflow state, no disposition/cost/projection concept) Not recommended — disqualified by redundancy with the already-built, already-running Temporal platform, not by missing features
Temporal (as work-ledger’s own foundation, not glue) already adopted MIT (self-hosted) live in-house ~30% (per plans/002-work-ledger-product Temporal-native digest: 55% claim / 10% dispositions / 45% cost / 15% kill-gate-evidence / 40% cross-fleet — blended) Adopt the parts that already fit (execution truth, workflow-id-uniqueness claim for workflow-modelled units); do not push business fields into it — see §2.3
Backlog.md MrLesk/Backlog.md MIT 2026-07-18 (same day) 6.2k★ ~10%, and the claim credit is net-negative High-risk dependency — git-native file board; directly implicated by dated evidence of silent data loss under concurrent writes (§4)
Vibe Kanban BloopAI/vibe-kanban Apache-2.0 (relicensed after shutdown) 2026-04-24 27.4k★ ~5% Not applicable — it’s a UI/board product; work-ledger has no UI by design (01-initial-recommendation.md §“What to remove”)
claude-flow ruvnet/claude-flow MIT 2026-07-18 (same day) 65.0k★ ~8% (in-memory coordination, not durable) Not recommended — durability failure by construction (in-memory), plus self-reported/unverified benchmark claims (competitive digest)

2. Deep dives

2.1 beads — the named candidate (must-address per brief)

Steve Yegge’s agent-task tracker, Dolt-backed by design, hash-based IDs so two fleets on different branches don’t collide on a sequence counter — this is exactly why research/12 §1.2 named it as the claim-queue candidate. It is also, as of literally today, mid-collapse of an 8-day experiment in becoming Postgres-native, which is the single most decisive piece of evidence in this whole document. Timeline, reconstructed from GitHub’s own PR/issue history (gh api, verified 2026-07-18):

Date Event Source
2026-07-06 → 07-10 PR #4601 “choose your own storage backend (Postgres / MySQL / SQLite behind the Dolt-parity seam)” — merged gastownhall/beads#4601
2026-07-10 PR #4712 — post-merge security remediation: bd init --backend=postgres --pg-url ... leaked the DB password into the bd.args telemetry span (both URL-query and libpq keyword/value DSN forms) — “major” severity, fixed by routing DSN values through a dedicated scrubber gastownhall/beads#4712
2026-07-11 PR #4723 “serialize concurrent schema initialization” + PR #4725 “serialize InitSchema DDL with a per-schema advisory lock” — race conditions in first-boot schema provisioning on the new Postgres backend gastownhall/beads#4723, #4725
2026-07-11 PR #4732 “resolve metadata/note merges inside the mutation transaction (concurrent lost-update)” — a lost-update bug on the very data path work-ledger cares about most gastownhall/beads#4732
2026-07-16 PR #4847 “roll back the direct PostgreSQL and MySQL adapters” — merged 22:44 UTC. Maintainer’s own words: “due to the complexity of supporting additional general-purpose server databases at production quality.” Existing PG/MySQL workspaces fail closed. gastownhall/beads#4847
2026-07-17 PR #4857 briefly reverts the rollback inferred from #4881 body (“re-lands #4847, reverted in #4857”)
2026-07-18 (today, hours before this research) PR #4881 “consolidate storage on Dolt” — merged 03:21 UTC. Re-lands the rollback AND removes the SQLite backend too: “bd now ships with a single storage engine — Dolt.” Explicitly notes none of the removed backends ever shipped in a release — the whole Postgres detour lived and died on main. gastownhall/beads#4881

This is not the research digest’s second-hand summary — it is the primary source, and it says the same thing more precisely: beads tried Postgres, found it not production-quality within 8 days, and is Dolt-only again as of this morning. That is the tool research/12 proposed as the claim-layer candidate. Separately, gastownhall/beads issue #2466 (multi-machine bd dolt pull merge conflicts on the metadata table, closed but recurring-by-design given Dolt’s merge model) and #2765 (open, zero chaos-test coverage on the Dolt backend, tracking real production failures in SQL type coercion and doctor/recovery) show the surviving backend has its own open concurrency gaps.

Verdict: not a foundation for work-ledger, on any backend. Health metrics are genuinely excellent (25.4k★, pushed same-day, 502 open issues = an actively triaged, high-velocity project, MIT-licensed) — this is not a dead or abandoned tool, which sharpens rather than softens the conclusion: even a well-resourced, fast-moving, MIT-licensed project with strong adoption could not keep a second production-quality SQL backend alive for two weeks. Beads remains viable only for its originally-scoped, separate role — the cross-fleet claim-before-work queue between Tomas’s and Robert’s fleets (research/12 §1.2) — as a peer system Robert’s fleet talks to over its CLI/API, never as a dependency inside work-ledger’s own process or schema.

2.2 pgmq / graphile-worker / river — the claim-atomicity component class

All three implement the same primitive: FOR UPDATE SKIP LOCKED (or Postgres LISTEN/NOTIFY plus it) to hand a row to exactly one consumer without blocking others. That primitive is real and battle-tested (Que, Oban, pg-boss share the lineage) — and it is also the one slice of work-ledger’s scope any of them actually cover.

pgmq graphile-worker river
Shape Postgres extension (Rust/plpgsql), SQS-like read/archive/delete Node/TS library, own migration set, LISTEN/NOTIFY + SKIP LOCKED Go library, own migration set, SKIP LOCKED, batch insert via COPY
Runtime fit vs Encore.ts (TS, Drizzle-migrated) Requires extension install privileges on the Postgres instance — friction inside Encore’s provisioned SQLDatabase Best runtime match (Node/TS) but ships its own migration files, parallel to Drizzle’s generated ones — reintroduces the two-migration-system problem the repo’s drizzle convention exists to prevent (repoRecon digest §4) Wrong language for an Encore.ts shop — a second runtime to operate for one primitive
Data-lifecycle model Message is archived/deleted after processing Job row is deleted (or archived to a separate history table) on completion Job row transitions to a terminal state then is prunable
The structural mismatch All three model jobs: ephemeral, queue-then-gone, retried-on-failure. Work-ledger models entities: permanent rows that acquire a disposition and a cost history and never disappear. Bolting a disposition/cost/evidence schema onto any of their job tables is building work-ledger’s schema, plus a dependency to keep patched and version-matched.

Verdict: useful component, not foundation, on all three — matches the digest’s framing exactly and is confirmed by inspection: the claim slice is real and worth stealing the idea from, but every artifact above the claim (state machine, disposition, cost, projection) is unclaimed territory none of them touch.

2.3 Temporal-as-foundation

Already covered in depth by this plan’s Temporal-native digest (see 05-competitive-analysis.md §4.1): self-hosted retention is 72h, kill gates need months-later reads, so dispositions/cost/evidence purge before they’re needed; search-attribute budget (28/namespace, 2KB/255-char values) can’t hold the schema; v1’s external Claude sessions aren’t workflows so claim-by-workflow-id doesn’t cover them. Blended coverage ~30%. This is already the house answer — Temporal supplies execution truth and claim for workflow-modelled units; work-ledger owns only the business columns Temporal purges. Restated here only because it belongs in the candidates table for completeness — it is not a rejected option, it is a partially-adopted one, which is the correct amount of adoption.

2.4 hatchet — the “just run a second workflow engine” option

MIT-licensed, Postgres-backed durable task/workflow orchestrator, pushed the same day as this research (2026-07-18), 7.5k★ — genuinely healthy, not a ghost project. Its pitch is close to Temporal’s: durable execution, retries, DAGs, an observability UI, all on Postgres, easier to self-host than Temporal’s Cassandra/ES-heavy defaults. On paper it scores similarly to Temporal-native (~30%: claim + workflow state, nothing for disposition/cost/projection because there’s nothing upstream of it to project from — it would replace, not sit on top of, the execution engine).

It is disqualified by redundancy, not by missing features — the same framing the research digest gives it. Groupon already has a self-hosted Temporal cluster provisioned for the internal plane (monorepo-infra, Helm 0.62.0, numHistoryShards: 512, sized in advance for the fleet). Standing up Hatchet alongside it means two durable-execution engines, two sets of ops runbooks, two things to secure and monitor, for a tool that — like Temporal — still has no native concept of MIGRATED/DROPPED/DEFERRED/DEAD or per-unit dollar cost. Notably, hatchet’s own team publishes hatchet-dev/durable-execution-the-hard-way — a repo that teaches building a durable-execution engine from raw Postgres with zero dependencies. Even the vendor selling the “just adopt a platform” answer demonstrates in public that the underlying primitive is small enough to hand-roll.

2.5 Backlog.md / Vibe Kanban / claude-flow — quick dispositions

  • Backlog.md (MrLesk/Backlog.md, MIT, 6.2k★, pushed today): git-native markdown board, the lighter alternative research/12 named alongside beads. This plan’s own prior-art research (arXiv:2606.19616, June 2026) directly implicates exactly this shape of tool: under concurrent agent writes to different fields of the same record, a file-based last-writer-wins tracker keeps one write and silently discards the other. That is not a hypothetical failure mode for Backlog.md, it is the documented behavior of the class of tool it belongs to, and it is precisely the failure work-ledger’s atomic claim exists to close. High-risk dependency; not recommended at any concurrency above 1.
  • Vibe Kanban (BloopAI/vibe-kanban, Apache-2.0 after Bloop’s April 2026 shutdown, 27.4k★, last pushed 2026-04-24 — three months stale, consistent with “community-maintained, remote services died 30 days after the shutdown announcement” per the competitive digest): a UI/board product. Not applicable — work-ledger has no UI in scope, by an explicit, already-made decision (01-initial-recommendation.md §“What to remove from the initial concept”: “Any custom web UI, kanban, or board view — Grafana + Temporal’s admin UI cover reads”). Scoring it against the 5-part rubric is almost beside the point; it answers a question this plan already closed.
  • claude-flow (ruvnet/claude-flow, MIT, 65.0k★, pushed today — by star count the single most popular repo in this whole table): in-memory swarm/hive-mind coordination for multi-agent orchestration. Genuinely large adoption, genuinely wrong shape — its claim/coordination state does not survive a process restart, which fails work-ledger’s durability requirement by construction before any feature comparison starts. Its own performance/ranking claims are self-reported and unverified (competitive digest). Not recommended.

3. Dolt dependency risk for beads, specifically

House constitution [E10]: Postgres-first. Beads’ own maintainers just spent eight days proving why that constitution earns its keep — they built a second-database (Postgres/MySQL) option, hit a security bug (DSN passwords in telemetry, §2.1), hit real concurrency bugs (schema-init races, lost updates), and reversed course twice inside a week, landing back on Dolt-only, as of literally this morning (PR #4881, merged 2026-07-18 03:21 UTC — hours before this document was written).

Concretely, adopting beads for anything means adopting Dolt: a MySQL-wire-compatible, version-controlled database with branch/merge/commit semantics fundamentally unlike the Postgres-everywhere stack already running (Encore’s SQLDatabase binding, Drizzle-generated migrations, Cloud SQL). That is a second database technology to provision, back up, secure, and staff for — and its own issue tracker documents live gaps on exactly the axis work-ledger cares about: #2466 (multi-machine merge conflicts on the metadata table — Dolt’s merge model makes this a structural recurrence, not a one-off bug) and #2765 (open, zero chaos-test coverage on the Dolt backend, tracking real production SQL-coercion and recovery failures). Standing up a claim substrate whose maintainers openly document zero chaos-test coverage on its own concurrency path, in the same week this research runs, is a house-constitution violation, not a taste call.

Verdict: beads-on-Dolt is disqualified as any part of work-ledger’s substrate — no shared runtime, no shared schema, no import. If beads is later adopted for its originally-scoped role (the cross-fleet claim-before-work queue between Tomas’s and Robert’s fleets, a genuinely different problem than work-ledger’s — see research/12 §1.2), it stays a peer system Robert’s fleet talks to over its own CLI, never a dependency pulled into work-ledger’s process.

4. The “component not foundation” pattern

Every candidate examined tops out around the claim slice — none of them, including the two highest scorers (Temporal-native, hatchet, both disqualified for redundancy rather than missing features), reach the state-machine + disposition + cost + projection four-fifths of the scope. That four-fifths is schema and business logic no library sells, because — per this plan’s competitive and prior-art research — no market category for “agent-fleet work ledger” exists yet to sell it. The claim slice itself is small enough that adopting a dependency for it costs more than writing it:

-- ponytail: this is the entire "claim" primitive. No queue library needed.
UPDATE work_unit
SET claimed_by = $1, claimed_at = now(), state = 'claimed'
WHERE id = (
  SELECT id FROM work_unit
  WHERE state = 'queued'
  ORDER BY created_at
  FOR UPDATE SKIP LOCKED
  LIMIT 1
)
RETURNING *;

Roughly 20 lines with the Drizzle/BaseRepository wrapper (repoRecon digest §4) — one query, one transaction, same row that carries disposition/cost/evidence. No second migration system, no second runtime, no DSN to scrub, no chaos-test gap inherited from someone else’s roadmap. This is the pattern worth taking from pgmq/graphile-worker/river/beads: the idea of SKIP LOCKED as the claim primitive, implemented directly, not the library.

5. Security / maintenance posture

Candidate Security posture Maintenance signal (2026-07-18) What it means for work-ledger
beads Shipped a password-in-telemetry leak on its Postgres adapter, fixed same week (#4712) — real finding, real fix, but real evidence that adding a server-DB backend to an agent-facing CLI introduces credential-handling surface by default 25.4k★, pushed today, 502 open issues (high but actively worked) High-velocity, well-run project — and still couldn’t hold a second backend at “production quality” for 2 weeks. Confirms the risk is structural (general-purpose server-DB support is hard to do well), not a maintenance-quality problem
pgmq/graphile-worker/river No incidents surfaced in this pass; mature, narrow-scope libraries (single primitive) All pushed within the last two weeks, all non-archived Low security surface because scope is narrow — exactly why they’re a pattern to copy, not a dependency to add
Temporal Already the house execution-truth platform; self-hosted, WIF-only ops discipline already in place (monorepo-infra) In-house, actively used No new security surface — this is the “adopt what’s already there” case
hatchet No incidents surfaced; MIT, self-hostable Pushed today, 7.5k★ Healthy, but adopting it adds a second durable-execution engine’s attack surface (secrets, network exposure, RBAC) for zero net capability over what Temporal already provides
Backlog.md No CVE-class finding, but the architecture itself (git-native files) is the documented failure mode (arXiv:2606.19616) — the “vulnerability” is silent concurrent data loss, not a patchable bug Pushed today, 6.2k★ Structural risk, not a patch-away risk — disqualifying at any real concurrency
Vibe Kanban N/A — company defunct, community-maintained since Apr 2026 Last push 2026-04-24, 3 months stale Not evaluated further — out of scope by the no-UI decision
claude-flow Self-reported, unverified capability/benchmark claims (competitive digest) 65.0k★, pushed today — largest community by far Popularity is not evidence of fit; in-memory durability failure is disqualifying regardless of community size

6. Answer to the brief’s question

Is building work-ledger from scratch strategically irrational? No — attacked honestly, then confirmed, not just asserted.

Steelman for adopting something, given the strongest fair case each candidate offers:

  • beads has 25.4k★, MIT, daily commits, a maintainer team responsive enough to ship and revert a whole storage backend inside a week, and it was already the named candidate in research/12. That’s a real, mature, battle-tested claim substrate with genuine agent-fleet DNA (hash-based IDs designed for exactly the two-fleet-collision problem Groupon has).
  • graphile-worker is the closest runtime match to Encore.ts (Node/TS), MIT, actively maintained, and its SKIP LOCKED implementation is more battle-tested than anything work-ledger would write in week one. The honest steelman: “just add disposition, cost, evidence_url columns to its jobs table and stop the delete-on-completion behavior.”
  • Temporal/hatchet both already solve durable execution + retries + a claim primitive at production grade; the honest steelman is “put the business fields on search attributes / a sidecar table this engine already tracks, don’t build a second system of record.”

Why each steelman breaks, with dated evidence, not priors:

  • beads: the maintainers themselves tried the “add a server-DB backend” move and reversed it twice in eight days, ending on Dolt-only hours before this research — the single cleanest, most current, most on-point disproof available. If beads’ own team can’t sustain a second backend at production quality in a fast-moving, well-staffed project, “adopt beads and extend it” costs more engineering than plan 001’s entire work-ledger scope (1 schema, 3 endpoints, 2 views).
  • graphile-worker: inverting its delete-on-completion job model into a permanent-entity model, while running its own migration set alongside Drizzle’s generated ones (repoRecon digest §4 — the exact two-migration-system anti-pattern the house convention exists to prevent), is not “adding three columns” — it’s rewriting the library’s core lifecycle assumption while fighting the house’s own tooling conventions the whole way.
  • Temporal/hatchet: the retention wall (72h self-hosted) and search-attribute budget (28/namespace, 255-char values) make Temporal mechanically incapable of holding month-durable disposition/cost/evidence data, independent of engineering effort — this isn’t a “try harder” problem, it’s a hard ceiling the house’s own Temporal-native investigation already quantified. Hatchet duplicates Temporal without solving that ceiling either.

Conceded, with the sharpest evidence found rather than the softest: no candidate — named or discovered — covers more than ~30% of the 5-part scope, and the two highest scorers (Temporal-native, hatchet) are disqualified by redundancy with an already-running platform, not by missing features. The one purpose-built agent-task tracker actually in reach (beads) demonstrated, in the eight days immediately preceding this research and concluding hours before it, that even its own maintainers could not keep a second production-quality SQL backend alive — direct, dated, verifiable evidence that “extend an existing OSS tool” costs more real engineering than plan 001’s from-scratch scope. Building from scratch is not the fallback position after nothing else works; it is the cheaper path, confirmed against live evidence, not just argued from priors.

Digest gap (flag, not a finding)

The OSS research digest supplied to this task returned only a synthesized verdict paragraph, not itemized per-candidate detail (license/stars/activity for pgmq, graphile-worker, river, hatchet). Every specific fact in this document — beads’ PR/issue timeline, all license/star/last-push figures in §1, the DSN-leak and lost-update bugs — was independently re-verified via gh api on 2026-07-18 rather than carried from that digest, per the plan’s no-fabrication rule. The digest’s own headline claims (beads’ Postgres rollback, the ~20-25% coverage ceiling, the SKIP-LOCKED-is-cheaper-than-a-dependency conclusion) all survived independent verification and, in the beads case, turned out to be understated relative to what the primary source shows.