beads_rust
Type: kb/types/note.md · Tags: agent-memory, context-engineering, tool-loop
Evidence basis: first-hand reading of the Dicklesworthstone/beads_rust checkout at commit c807e36f (2026-08-29), covering the README, package and plugin metadata, shipped agent instructions, CLI and optional MCP entry paths, storage and workflow-policy implementation, scheduler and coordination code, sync behavior, and relevant tests. This is a code-grounded review of release 0.5.7. I did not operate br in a live agent host or test its effects on agent behavior.
br is a strong local coordination control plane on its CLI path, not an agent runtime. It retains active work, computes which recorded issues are ready, provides an atomic claim operation, enforces repository workflow rules, and exports collaboration state. The model, coding harness, code execution, Git transport, and live Agent Mail service remain external. This makes the reviewed artifact complete as an agent-facing coordination tool but only one part of a wider agent loop.
The main weakness is internal route parity. The optional MCP server and the shipped instructions give agents legitimate alternate entry paths, but those paths do not preserve all of the claim, policy, close, and context-delivery behavior that makes the CLI route reliable. The result is not merely a documentation discrepancy: the entry path an agent chooses changes which controls apply.
Boundary and ordinary route
The ordinary CLI route is ready or scheduler -> inspect -> update --claim -> external code work -> close -> JSONL auto-flush -> external Git commit or pull. ready selects issues in the configured ready-status group that are unblocked, not time-deferred, not pinned, and not ephemeral; it also filters known external blockers before returning results (ready.rs, ready.rs). scheduler starts from that ready set, then ranks candidates with explicit terms for priority, dependent count, stale-claim evidence, fairness, and domain contention. It returns scored recommendations and rationales; it does not assign work (scheduler.rs, scheduler.rs).
The CLI's claim operation is the strongest concurrency mechanism in the artifact. --claim sets status to in_progress, assigns the resolved actor, and marks the update as expecting an unassigned issue (update.rs). Storage checks that expectation inside the write transaction and adds a compare-and-set predicate to the SQL update, so two contenders cannot both claim the same unassigned row (sqlite.rs, sqlite.rs). This is formal local exclusion, not an instruction that agents should avoid collisions.
Repository policy is also installed at the storage boundary on the CLI route. The configuration-aware opener loads .beads/policy.yaml into storage (config/mod.rs). Status transitions, required fields, revision-scoped gate results, and capacity are then evaluated within the same transaction as the mutation (sqlite.rs, sqlite.rs). The dedicated CLI close route additionally resolves epic and dependency blockers and evaluates close policy before applying an atomic batch (close.rs, close.rs).
The outer boundaries are deliberate. Successful mutations auto-flush SQLite changes to JSONL, but ordinary issue tracking does not implicitly commit, push, pull, import remote changes, start a background service, or perform the issue's code work (README). That separation keeps local state changes inspectable and leaves repository transport and execution authority with the host agent or operator. It also means br alone provides neither distributed claim atomicity across unimported Git branches nor an execution sandbox.
Agent entry paths do not preserve the same controls
| Concern | CLI path | Optional MCP path | Consequence |
|---|---|---|---|
| Database and sync exclusion | Routed workspace lock, storage transaction, auto-flush | Database-family and sync locks, pending-merge refusal, auto-flush | Both paths protect local file and export integrity. |
| Claiming | --claim couples status, assignee, and compare-and-set exclusion |
Generic update_issue accepts field updates; there is no claim-specific operation or unassigned compare-and-set request |
An MCP agent can set in_progress or assignee without acquiring the CLI claim guarantee. |
| Repository workflow policy | Configuration-aware open installs the workflow and capacity policy | Per-request handlers call SqliteStorage::open; direct storage defaults to inactive workflow policies |
Configured transitions, required fields, gates, and capacity are not installed on MCP request storage. |
| Closing | Blockers and close policy are checked before one atomic batch | close_issue commits closed, then reads blockers and emits a warning |
MCP can record a close that the ordinary CLI close route would refuse. |
| Inherited ancestor context | Structured show includes it; text-mode transition into in_progress emits it |
No inherited-ancestor-context assembly in MCP tools, issue resources, or prompts | The task route determines whether retained governing context reaches the agent. |
MCP does share substantial infrastructure with the CLI. A mutation acquires the database-family and sync locks, refuses unresolved merge state, opens storage under write authority, and flushes dirty JSONL before reporting success (mcp/mod.rs). The gap is above that substrate. CLI storage opening explicitly installs repository workflow policy, while MCP handler storage is opened directly and direct storage users default to inactive workflow policies (mcp/mod.rs, sqlite.rs). Server startup briefly opens configured storage, but drops it before constructing a state object that carries paths and actor identity, not the loaded workflow (mcp/mod.rs).
The MCP tools expose the behavioral consequence. update_issue passes a generic IssueUpdate to storage and returns status metadata, without a claim primitive (tools.rs). close_issue first writes the closed status and only afterward asks for blockers to construct a warning (tools.rs). Storage still applies structural issue validation and transactional atomicity; the narrower finding is that repository workflow policy and CLI close semantics do not reach this path.
An optional MCP read cache has a separate time-freshness gap. When BR_MCP_READ_SNAPSHOT enables it, entries are invalidated by database, WAL, SHM, or JSONL file witnesses, with no clock or expiry witness (mcp/mod.rs, mcp/mod.rs). The graph-health resource computes a stale-issue count from the current time, so an unchanged issue can cross the 30-day threshold without invalidating a cached response (resources.rs). This does not affect the default cache-off path, but it prevents a time-faithfulness guarantee when the optimization is enabled.
The main repair should therefore be shared services, not duplicated checks. A policy-aware workspace opener and common claim and close application services should sit below both CLI and MCP projections. MCP should expose a claim-specific compare-and-set operation and return the assignee that won. That would make interface choice a presentation decision rather than a control decision. Time-derived cached resources additionally need a time bucket or expiry in their witness.
Active-work memory is rich, but activation is route-dependent
br retains the right shape for active work: issue purpose and criteria, status, priority, assignee and owner, dependencies and parentage, comments and events, workflow gates and capacity, and sync witnesses. SQLite is the authoritative local working state; JSONL is the explicit Git collaboration representation. ready, show, search, scheduler, coordination status, MCP resources, import, and reconciliation provide several later read-back routes. This is task memory in the broad sense, but it is not retrospective memory or chat history: its main question is what remains live and what can happen next.
The optional inherited-context feature is a direct attempt to prevent cold-start misses, context decay, and stale propagation. It retains canonical JSON in an ancestor's agent_context, walks the parent chain, and selects at most the root or topmost epic plus immediate parent for later delivery (inheritance.rs, inheritance.rs). Structured show --json and TOON attach those blocks to each issue (show.rs, show.rs).
Claim-time delivery is less complete. update emits inherited context only in its human-text branch; JSON and TOON return a compact update record with no inherited-context field (update.rs, update.rs). This conflicts with the official skill's instruction that agents should always use --json (SKILL.md). A JSON-using agent can fetch the context with a subsequent show, but the claim response itself does not push it, so storage and even implemented read-back do not establish claim-time activation.
The shipped behavior views have a second drift. The official skill's quick workflow and the br agents blurb instruct agents to use update --status in_progress, not the stronger --claim operation (SKILL.md, agents.rs). The skill also labels sync “never automatic,” while the current runtime auto-flushes JSONL after successful mutations by default (SKILL.md, README). Git remains explicit, but export does not. Because these files shape agent behavior, they are compiled operating views, not harmless prose. They should be generated or checked against a canonical workflow contract that includes claim semantics, machine-output fields, and current sync behavior.
What br knows and what it only records
The artifact has a useful epistemic architecture when its outputs are kept within their warrant:
| Knowledge object | Status and warrant | Authority downstream |
|---|---|---|
| Issue descriptions, criteria, comments, and imported JSONL | Acquired assertions. Schema and field validation establish shape, not semantic truth or task completion. | They guide agent and operator judgment. |
| Ready and blocked sets, graph counts, sync hashes, and capacity occupancy | Derived from the current local database, configured rules, and captured witnesses. | They can filter or refuse local operations within that recorded model. |
| Scheduler rank | A deterministic policy score over derived facts, not a truth claim that the top issue is globally best. | Advisory only; no automatic assignment follows. |
| Stale or abandoned claim classification | Rule-bound inference from issue age, owner class, and optional offline Agent Mail snapshots. Without a snapshot the system says so; even a reclaim candidate produces suggested commands rather than mutation. | Advisory diagnosis requiring a later actor. |
| Gate result | An externally supplied evaluation bound to a specific transition and status revision. br verifies scope and freshness, not the underlying evidence that made the evaluator pass it. |
A current pass can authorize a configured transition. |
closed status |
A normative workflow disposition recorded by an authorized mutation. | It removes work from the active set; it does not prove the external implementation is correct. |
The scheduler needs one extra qualification. It evaluates assignments with ReservationEvidence::NoSnapshot, then classifies NoMailSnapshot and Ambiguous as stale for scoring (scheduler.rs). That adds both stale and fairness contributions, even while the emitted rationale correctly says missing Mail evidence is not abandonment proof (scheduler.rs, scheduler.rs). The result preserves uncertainty in prose but converts missing or ambiguous evidence into a ranking preference. Because the rank is advisory, this cannot reclaim the issue; it is still a consequential policy choice rather than evidence that work was abandoned.
The coordination code is especially careful about this boundary. It is a pure classifier over supplied issue metadata and optional offline Agent Mail snapshots; no snapshot becomes NoMailSnapshot, and a reclaim recommendation remains advisory rather than automatically taking ownership (coordination.rs, coordination.rs, coordination.rs). By contrast, a generic MCP close can move the normative state before issuing its blocker warning. That path makes the recorded disposition carry more practical authority than its precondition evidence warrants.
Assessment and changes that would alter it
For shell-based agents in one repository, the CLI route is a well-structured external-state service: local-first, inspectable, dependency-aware, transactionally claimed, policy-gated, and explicit about the boundary to Git and actual work. It usefully moves bookkeeping and enforceable workflow rules out of model interpretation and into symbolic checks. Its scheduler and stale-claim analysis expose evidence and keep final choice outside the tool.
For MCP-first agents, the assessment is weaker because the integration is not only another projection of the same operations. It changes their semantics. The highest-value changes are:
- Route CLI and MCP mutations through one policy-aware workspace service, including pre-commit close blockers and repository workflow policy.
- Add a claim-specific MCP operation with the same compare-and-set semantics as
br update --claim. - Return inherited ancestor context in machine-readable claim responses and MCP work-selection or claim responses, then test whether agents actually use it rather than treating delivery as activation.
- Derive the official skill,
br agentsblurb, examples, and interface contract tests from one versioned workflow description so high-authority instructions cannot silently lag runtime behavior.
This review does not establish deployed reliability, throughput gains, agent uptake, or causal benefit from inherited context. It also does not assess the enclosing model, coding harness, filesystem permissions, Git collaboration process, live Agent Mail, or beads_viewer. Those systems determine whether recommendations are followed, remote changes are imported promptly, permissions are safe, and task outcomes are true. The conclusions are therefore about the pinned br artifact and its code-visible routes, not the success of the wider Agent Flywheel system.
Relevant Notes:
- Active work state is not retrospective memory or chat history - rests-on: supplies the active-work distinction used to classify issue and coordination state.
- Agent-runtime analysis should separate scheduling, context assembly, and external state - rests-on: separates
br's ranking and retained-state roles from the external agent runtime and code execution. - Keep Lineage And Compiled Views From Drifting - rests-on: explains why the official skill and injected instruction blurb need lineage to the runtime workflow contract.
- Knowledge storage does not imply contextual activation - rests-on: distinguishes retained
agent_context, delivery in a response, and demonstrated effect on an agent's action. - Moving the interpretation–enforcement boundary requires cross-form coverage - see-also: frames the difference between workflow advice in agent instructions and transactionally enforced claim or transition rules.