anvil Strategic Backlog — moat execution track¶
Added 2026-07-02 from a full product / product-market-fit / moat review (repo docs + code + external landscape), with every code-level claim below verified against shipped source by a five-stream fact-check pass. This doc is a strategic overlay: it sequences and sharpens existing items from
roadmap.md(SL/WF waves) andanvil-backlog.md(E1–E13 / B-items) rather than replacing them. Items are numberedS*to avoid colliding with either. The same review corrected ~25 stale doc claims (docs said several shipped features were unbuilt — B46, B47, B48, replay, multi-provider LLM); see the PR that introduced this file.
The one-sentence strategy¶
Status is downstream of proof — make that literally true, measure it, then
distribute it. The commoditized pillars (durable state, orchestration) are
conceded per _positioning.md; the defensible position
is the closed verification loop: typed evidence → enforced gate → signed
proof → independent re-execution → countersigned, portable, replayable
artifact. Nobody in the researched landscape ships that fusion: agentic-os /
EviBound have hard gates but no portable artifact; AGEF / Proof-of-Insight /
Pipelock have signed artifacts but no task/claim/state binding; Temporal has
leases but no evidence concept; Beads has memory but deliberately no
governance. The moat is the loop, plus two compounding assets: the verified
history accumulating in every .anvil/ (switching cost) and measured,
honest numbers (credibility).
Where the loop stands today (verified 2026-07-02):
| Link in the loop | Status |
|---|---|
Typed proofs (CommandProof w/ exit code, DiffProof, LinkProof, AssertionProof) |
Shipped (B48 pt 1) |
Signed portable AcceptanceProof, auto-minted on approve, off-host anvil proof verify |
Shipped (B48 pt 2) |
Unified actor identity (ANVIL_ACTOR resolver, signer-fingerprint fallback) |
Shipped (B47) |
Lease hardening (max-claim-age cutoff in renew()) |
Shipped (B46) |
Replay guarantee proven in CI (anvil replay + tests/test_replay_equivalence.py) |
Shipped (SL-1) |
| Evidence gate enforced by default | Open — S2 (advisory: strict_evidence defaults False) |
| Independent re-execution of proof commands | Open — S1 (nothing re-executes; proofs trust the hook writer) |
| The numbers that prove it works (gaming benchmark, real critic false-pass) | Open — S5/S6 |
Wave 1 — Make the headline claim true¶
The README's headline promise ("no completed work without proof") holds only
under opt-in strict mode, and a proof's authenticity rests on a trusted hook
writer (state/models.py TRUST BOUNDARY; review/gates.py
_proof_satisfies docstring: "A harness in which the agent can write the
evidence buffer can still fabricate a passing CommandProof"). Wave 1 closes
those two gaps. Everything else in this doc is downstream of them.
S1 — anvil verify <task>: independent re-execution + countersigned AcceptanceProof¶
- Priority: P0 Effort: L Type: feature Status: OPEN
- Rationale: The single highest-leverage product move. A signed proof of
an agent-recorded result is laundered trust:
CommandProof.output_sha256is recorded, never re-verified, and no code path anywhere re-executes a submitted command (the only fresh command execution in the tree isrun-workflow's executor, which is not re-verification). Re-execution converts recorded proof into reproduced proof — the one link no competitor indocs/research/2026-06-20-competitive-analysis-agent-state-tools.mdships (hard gates without artifacts, artifacts without binding, neither with re-execution). It is also the safety prerequisite E13's fleet thesis names for letting weak/unsupervised local runners pull work, and the literal implementation of "status is downstream of proof." - Acceptance:
anvil verify <task-id> [--json](1) resolves the task's accepted evidence and itsCommandProofs; (2) re-runs each pinned command in a clean checkout of the claim's branch (fresh worktree by default;--in-placeescape hatch), honoring a configurable timeout; (3) compares exit codes againstpassing_exit_codes(output hashes compared when present, reported as informational — command output need not be deterministic); (4) on success appends a verifier countersignature block (separate keypair identity via the existingsigningmodule) to the proof JSON under<state>/proofs/, andanvil proof verifyvalidates worker + verifier signatures and reports both; (5) on mismatch exits non-zero with a per-command report and appends atask.verification_failedevent (additive; replay stays green — proof files themselves remain outside replayable state, as today). Works headless so CI can run it. Docs: a "verify" section incli-reference.md+ the trust-boundary paragraph indesign.mdupdated to point at it. - Likely files:
bin/src/anvil/cli/verify.py(new),bin/src/anvil/signing.py,bin/src/anvil/state/models.py(AcceptanceProof.countersignatures),bin/src/anvil/git_ops/,bin/src/anvil/cli/proof.py,tests/ - Depends on: — (B48 both parts shipped)
S2 — Enforce the evidence gate by default for tasks that declare typed proofs¶
- Priority: P0 Effort: M Type: modify Status: OPEN
- Rationale: As shipped,
strict_evidencedefaultsFalse(config.py), the defaultapply --approvepath approves with a failing or absent gate, the SQLitetask.appliedwrite path performs needs_review→accepted→done with no evidence check at all, and the enforcingtransitions.task_needs_review_to_accepted()exists but has no caller. Meanwhile the direct competitors for the verification wedge hard-block (agentic-os unbypassable CI gate; EviBound's measured 100%→0% hallucinated completion). Flipping the default only where the planner emitted typedrequired_proofskeeps quick-fix/legacy flows unbroken while making the README headline true for every engine-created task. - Acceptance:
strict_evidencegains anautomode and it becomes the default: strict ifftask.verification.required_proofsis non-empty; explicittrue/false/--strict/--no-strict/$ANVIL_STRICT_EVIDENCEstill win (existing precedence preserved). Applies identically to CLIapplyand MCPapply_review. MCPsubmit_completion_evidence(which today runs no gate at all) returns the advisory gate verdict in its envelope so agents see missing items at submit time, before review. Belt-and-braces: the apply write path routes through (or asserts the same predicate as)task_needs_review_to_acceptedso the gate cannot be bypassed by a front door that forgets to check. CHANGELOG + migration note;design.md§gate updated; the README headline sentence restored to its strong form once this lands. - Likely files:
bin/src/anvil/config.py,bin/src/anvil/cli/packet_apply.py,bin/src/anvil/mcp_server.py,bin/src/anvil/state/transitions.py,bin/src/anvil/state/sqlite.py,tests/ - Depends on: — (pairs naturally with S1: verify-then-accept flows)
S3 — Name the replay-equivalence CI check and surface the audit guarantee¶
- Priority: P2 Effort: S Type: infra Status: OPEN
- Rationale: SL-1 shipped (v1.19.0) but runs anonymously inside the main pytest job, so the strongest credibility artifact anvil has — "replaying the log reconstructs the database, proven on every PR" — is invisible to an evaluating user. A named status check + one README sentence converts an existing engineering fact into a marketing fact at near-zero cost.
- Acceptance: CI exposes a separately-named
replay-equivalencecheck (job or step-level reporting) runningtests/test_replay_equivalence.py; README's audit-guarantee paragraph links to it. No engine changes. - Likely files:
.github/workflows/ci.yml,README.md - Depends on: —
S4 — Proof-format interop: export AcceptanceProof to an existing portable envelope¶
- Priority: P2 Effort: M Type: feature Status: OPEN
- Rationale: The competitive analysis recommends adopt, don't invent:
AGEF / Proof-of-Insight / Pipelock already define portable proof
envelopes with ecosystems forming around them. anvil's differentiator is
the binding (task + claim + actor + event range), not the envelope
format. An
anvil proof export --format <fmt>adapter keeps the moat (binding + verify loop) while neutralizing "yet another proprietary proof file" as an adoption objection — and positions anvil to ingest others' proofs later. Also the hedge the positioning doc's kill-trigger names: if a platform standardizes a proof format, anvil collapses into schema + adapters. - Acceptance: Pick one target format after a short spike (spike doc in
docs/research/);anvil proof exportemits it losslessly enough that a third-party verifier of that format accepts the signature; round-trip test. - Likely files:
bin/src/anvil/cli/proof.py,bin/src/anvil/state/models.py,tests/ - Depends on: S1 (export is most valuable once proofs can carry countersignatures)
Wave 2 — Make it measurable¶
A verification product with unmeasured verification is an unverified claim.
Today the only committed numbers are the coordination benchmark's single
scenario (overlapping_files, 8 actors, 1 trial, seed 42, measured on
v0.0.8 — two-plus releases old) and the mock critic's 0.25 false-pass
baseline. The scenario that IS the thesis — evidence_gaming — has a harness
but no committed numbers, and the real-LLM critic number is an explicit
placeholder.
S5 — Commit evidence_gaming benchmark numbers across gate modes; refresh the headline on current code¶
- Priority: P1 Effort: M Type: infra Status: OPEN
- Rationale: EviBound markets "hallucinated completion 100%→0% at ~8.3%
overhead." anvil needs its own honestly-caveated sentence of that shape,
and the harness already exists (
benchmarks/harness/scenarios.pydefinesevidence_gaming; RESULTS.md just never populated it). Run it in three arms — advisory (today's default), strict (S2), strict+verify (S1) — and the resulting table is simultaneously the release gate for S1/S2 and the centerpiece of the launch post. Re-runoverlapping_fileson current code at the same time so the committed headline stops citing v0.0.8. - Acceptance:
benchmarks/RESULTS.mdgains a populatedevidence_gamingsection (gamed-submission catch/refuse rates per arm, seeds/trials stated) generated on the current version, plus a refreshedoverlapping_filesrun; README/status quotes the strongest defensible sentence with its caveats. Multi-trial (not 1 trial) so the number survives scrutiny. - Likely files:
benchmarks/harness/,benchmarks/RESULTS.md,README.md - Depends on: S2 (strict arm); S1 (verify arm — can land as a follow-up arm)
S6 — Measure the real critic false-pass rate (retire the placeholder)¶
- Priority: P1 Effort: M Type: infra Status: OPEN
- Rationale:
docs/critic-false-pass-baseline.mdships a committed number only for the mock backend (false-pass 1/4 = 0.25); the real-LLM table is marked "PLACEHOLDER — not yet measured" and theapibackend raisesNotImplementedError. The critic gate is a marketed differentiator; its central quality number cannot stay TBD. Maps to roadmap SL-2. - Acceptance: The
apicritic backend is wired (default via the Agent SDK subscription path, consistent with--use-llm); the fault-injection suite runs against it (opt-in/costed, likeevals/); the baseline doc's placeholder table is replaced with measured false-pass / false-fail rates and the measurement recipe; a CI-adjacent (manual-trigger) workflow keeps it re-runnable. - Likely files:
bin/src/anvil/planning/critic_falsepass.py(or current home),docs/critic-false-pass-baseline.md,evals/ - Depends on: —
S7 — Run the B50 capacity bake-off before building more of E13¶
- Priority: P1 Effort: M Type: research Status: OPEN
- Rationale: E13 (fleet/capacity coordination) is the boldest strategic
bet and its economic premise — pools throttle often enough that draining
several flat-rate pools + local spillover matters — rests on
a
docs/research/results write-up (seedocs/plans/bake-off.md) that has not been run yet. The positioning doc already defines kill/pivot triggers; they can only fire if the experiment runs. Two loops, two weeks, publish whatever it says — either it funds E13 or it saves a quarter of misdirected work. - Acceptance: The bake-off doc is populated with the measured throttle
frequency, spillover behavior, and packet-quality observations from ≥2
concurrent pools on real work; an explicit go / narrow / kill call on E13
scope is recorded in
anvil-backlog.mdE13 header. - Likely files:
docs/research/(new results write-up),docs/backlog/anvil-backlog.md - Depends on: — (S1/S2 strengthen the "safe to let weak runners pull" arm but aren't blockers)
Wave 3 — Distribution: be the proof layer, not another tracker¶
S8 — Beads/Gas Town interop: "Beads remembers; anvil proves"¶
- Priority: P1 Effort: M Type: feature Status: OPEN
- Rationale: Beads (~18.7k stars) owns backlog/memory mindshare and
deliberately omits governance — close is a manual one-liner,
--claimis one-shot with no lease/heartbeat, and there is no evidence concept. Gas Town runs 20–30 parallel agents whose merge queue trusts unverified worker output. That is anvil's exact wedge, adjacent to the category's largest audience. Interop turns the mindshare leader into a distribution channel instead of a competitor: a bd↔anvil sync provider (thedocs/sync-providers.mdcontributor path exists for exactly this) or, at minimum, a documented recipe wiring anvil's gate + proofs under a Beads-managed backlog. Positioning sentence: "Beads remembers what your agents did; anvil proves they did it." - Acceptance: Either (a) a
beadssync provider mapping bd issues ↔ anvil tasks (evidence/proofs stay anvil-side; status projects both ways, same shape as the GitHub provider), or (b) a how-to + glue commands demonstrating a Gas Town/Beads loop where completion requiresanvil applystrict-gate acceptance — chosen after a 1-day spike on bd's current CLI/JSONL surface. A short post/README section announces the integration. - Likely files:
bin/src/anvil/sync/(provider),docs/how-to/,docs/sync-providers.md - Depends on: S2 (the recipe's value is the enforced gate)
S9 — Surface the measured claims where evaluators look¶
- Priority: P2 Effort: S Type: docs Status: PARTLY DONE (this PR)
- Rationale: anvil's strongest evaluator-facing facts were buried:
measured always-on context cost (~2,433 tokens, execution surface, vs
spec-kit's community-reported ~18.6k always-on tax — the loudest complaint
class in
competitor-issue-analysis.md) lived only inbenchmarks/CONTEXT_AUDIT.md; the replay guarantee ran unnamed in CI (S3); several shipped verification features were still described as unbuilt in anvil's own docs. The PR introducing this file fixed the context-cost README row and ~25 stale claims; the rest lands with S3/S5. - Acceptance: README comparison table carries the context-cost row (done); benchmark + false-pass numbers quoted with caveats once S5/S6 land; a "what's measured" index section in README Status linking RESULTS.md / CONTEXT_AUDIT.md / critic baseline / replay check.
- Likely files:
README.md,docs/_positioning.md - Depends on: S3, S5, S6
S10 — Ride MCP Tasks (SEP-1686) as a transport for claims/packets¶
- Priority: P2 Effort: M Type: feature Status: OPEN
- Rationale: Already the competitive analysis' recommendation: MCP Tasks is the transport the platforms are converging on; anvil should be the durable, governed backend behind it rather than a parallel vocabulary. Neutral-transport interop is also the best defense against the platform-absorption failure mode the positioning doc documents.
- Acceptance: Spike doc mapping anvil's claim/packet/evidence lifecycle onto the MCP Tasks spec as it stabilizes; implement the mapping behind a flag if the spec is stable enough; otherwise record the go/no-go and revisit trigger.
- Likely files:
bin/src/anvil/mcp_server.py,docs/research/ - Depends on: —
S11 — Sequencing guard: brownfield ingest (E5) starts only after S1/S2/S5¶
- Priority: P2 Effort: — Type: decision Status: OPEN
- Rationale: E5 (scan/ingest an existing repo; non-feature task types) is the biggest TAM expansion — the "underserved 75% of real work" — and the most tempting next build. But it widens the product before the central claim is true and measured; every new surface built on an advisory gate compounds the say/do gap that Wave 1 exists to close. This item exists to make the deferral explicit and reviewable rather than accidental.
- Acceptance: E5 work items stay unstarted until S1, S2, and S5 are DONE or this item is consciously overridden with a note here.
- Depends on: S1, S2, S5
Process items¶
S12 — One canonical planning surface (and it should be anvil)¶
- Priority: P2 Effort: M Type: process Status: OPEN
- Rationale: Two unreconciled planning artifacts (
roadmap.md's SL/WF waves with legacy v1.x/v2.x buckets;anvil-backlog.md's E/B items) plus this overlay is one too many. Agents plan from these files; divergence is compounding. anvil now supports multi-PRD projects — dogfooding the roadmap as anvil state (aplanningPRD whose tasks are the S/B items) is both the fix and a product demo. Also: this review found ~25 doc claims contradicted by shipped code, all of one class — prose restating facts the code owns. Where cheap, assert doc-stated counts in tests (hook count, tool count, provider count, schema version) the waytest_version_sync.pyalready pins versions. - Acceptance: One doc (or anvil project) is declared canonical for
sequencing, the others reduced to reference/index; a small
tests/test_doc_sync.pyasserts the highest-drift counts; stale-claim class tracked intech-debt-backlog.mdif not closed. - Likely files:
docs/roadmap.md,docs/backlog/*.md,tests/test_doc_sync.py(new) - Depends on: —
S13 — Skill-vs-CLI drift eval (pull forward postmortem action (a))¶
- Priority: P1 Effort: M Type: infra Status: OPEN
- Rationale: The 2026-06-22 init-loop postmortem's deep cause — SKILL.md
prose duplicating operational knowledge the CLI owns, with nothing keeping
them in sync — has already produced one high-severity agent-facing outage
and names sibling risks in
claim,plan,execute,resolve-decisions. Agents are anvil's primary users today; this is the highest-recurrence-risk gap in the product's actual UX. The postmortem lists the eval as "being built next" — schedule it, don't intend it. - Acceptance: An
evals/(or plain pytest, where offline-checkable) case per skill exercising its documented flow against the default workspace layout, failing on path/command drift like the 06-22 incident; wired to CI where offline,evals/where costed. - Likely files:
evals/,tests/,skills/ - Depends on: —
Explicitly deprioritized (until Waves 1–2 land)¶
Per the roadmap's own principle — "makes the product wider, not the central claim more true":
- Sync-provider expansion (Linear P9B-1, Monday P9B-2, Jira P9B-3, GitHub Projects P9B-4, webhook sync P9B-5) — except the Beads provider (S8), which is distribution for the moat itself, not breadth.
- The 56-item
P11-*audit batch — hardening that matters, but none of it changes what anvil is; drain opportunistically. - WF-3 declarative workflow runner — stays spec-first per roadmap; orchestration is the churn layer anvil deliberately sits beneath.
- E11 backlog-platform build-out — S12 dogfoods the need first; the research's own warning ("stay the governed substrate under the loop, not a PM platform") stands.
Kill / pivot triggers (unchanged, restated for this track)¶
From _positioning.md: if a platform ships a portable, exportable,
vendor-neutral proof+state format that off-cloud runtimes can read and
write, collapse into (i) the schema spec and (ii) emit/ingest adapters — S4
is the pre-positioning for exactly that outcome. S7's bake-off carries E13's
kill trigger. For a solo author the win condition remains personal
infrastructure that survives churn, not market share.