diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 703973a..24170ae 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -83,7 +83,14 @@ {"id":"bb-bvn.3","title":"Add blocked-chain highlighting and cycle anomaly signaling","description":"Add analysis overlays for blocker triage and anomaly visibility.\n\nScope:\n- Compute and highlight blocked chains from selected node.\n- Show concise blocker summary:\n - open blocker count\n - in-progress blocker count\n - first actionable blocker\n- Cycle/anomaly signaling:\n - detect cycles in dependency graph\n - mark involved nodes/edges with warning style and explanation text\n\nUI behavior:\n- \"Show blocking path only\" toggle to reduce noise.\n- Hovering a node/edge highlights direct dependency chain.\n- Keep styling subtle and readable; avoid visual overload.\n\nRules:\n- Analysis is read-only and derived from current graph model.\n- Must not fail hard on malformed dependency data; degrade with warnings.\n\nTest plan:\n- Unit tests for blocked-chain derivation and cycle detection logic.\n- UI tests for toggle behavior and warning visibility.\n- Screenshot verification for normal and anomaly cases.\r\n","acceptance_criteria":"- Selected issue can display clear blocked-chain context.\n- Cycle/anomaly conditions are detected and visibly flagged.\n- Blocking-path-only mode materially reduces graph noise.\n- Analysis features remain performant and do not break base graph rendering.\n- Tests and screenshots verify normal + anomaly paths.\r\n","notes":"Addressed review P1 in detectDependencyCycles: removed early-return DFS behavior that leaked recStack/path state; traversal now always unwinds and collects cycles without contaminating predecessor nodes. Added regression test in tests/lib/graph-view.test.ts: detectDependencyCycles does not mark non-cycle predecessor as cyclic. Verification: node --import tsx --test tests/lib/graph-view.test.ts (pass), npm run typecheck (pass), npm run test (pass).","status":"closed","priority":2,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-11T17:12:11.687878-08:00","created_by":"zenchantlive","updated_at":"2026-02-12T18:57:24.8694169-08:00","closed_at":"2026-02-12T18:57:24.8694169-08:00","close_reason":"Implemented blocked-chain analysis, blocking-path emphasis, and cycle anomaly signaling with regression coverage; tests/typecheck are green.","labels":["analysis","graph"],"dependencies":[{"issue_id":"bb-bvn.3","depends_on_id":"bb-bvn","type":"parent-child","created_at":"2026-02-11T17:12:11.6890831-08:00","created_by":"zenchantlive"},{"issue_id":"bb-bvn.3","depends_on_id":"bb-bvn.2","type":"blocks","created_at":"2026-02-11T17:12:37.378326-08:00","created_by":"zenchantlive"},{"issue_id":"bb-bvn.3","depends_on_id":"bb-bvn.4","type":"blocks","created_at":"2026-02-11T20:10:03.6326727-08:00","created_by":"zenchantlive"}]} {"id":"bb-bvn.4","title":"Epic Design Gate: scope, decisions, and acceptance contract","description":"Design/discovery gate for bb-bvn before further implementation.\n\nMust capture:\n- Product intent and user outcomes for this epic\n- Explicit architecture decisions and tradeoffs\n- API/data contracts and edge cases\n- Windows-specific constraints and path/process assumptions\n- Test strategy and verification commands\n- Non-goals and out-of-scope boundaries\n\nCompletion rule:\nDo not start new implementation tasks in this epic until this gate is closed with agreed decisions.","acceptance_criteria":"A written execution-grade plan exists for this epic and all child task descriptions are updated with concrete implementation details, dependencies, and testable acceptance criteria.","notes":"Graph design gate completed: agreed React Flow deterministic UX, default 2-hop depth controls, mobile simplified fallback, typed edge semantics, and verification contract (tests + screenshots + smoke). Child tasks bb-bvn.1/.2/.3 updated with execution-grade details.","status":"closed","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-11T20:09:40.290642-08:00","created_by":"zenchantlive","updated_at":"2026-02-11T20:59:12.4823711-08:00","closed_at":"2026-02-11T20:59:12.4823711-08:00","close_reason":"Design gate complete: bb-bvn child tasks now contain concrete scope, contracts, dependencies, and testable acceptance criteria.","dependencies":[{"issue_id":"bb-bvn.4","depends_on_id":"bb-bvn","type":"parent-child","created_at":"2026-02-11T20:09:40.2922349-08:00","created_by":"zenchantlive"}]} {"id":"bb-dcv","title":"Agent Communication \u0026 Coordination Patterns","description":"Agents need a standardized way to coordinate (handoffs, help requests, blockers) without breaking flow. We are opting for a **Issue-Centric** communication model (using Comments) rather than an Inbox-Centric model. This epic defines the protocols, patterns, and tool support needed to make that robust.\n\nGoals:\n- Define 'Protocol' for agent-to-agent comments (prefixes, structure).\n- Establish Identity standards (how agents refer to themselves).\n- Ensure CLI support for all protocol actions (commenting, signaling).\n\nDeliverables:\n- RFC-001: Agent Coordination Protocol.\n- Skill: beadboard-driver (teaching the protocol).\n\nThis epic blocks bb-u6f (Agent Sessions) because session attribution relies on the Identity standards defined here.","status":"open","priority":1,"issue_type":"epic","owner":"jordanlive121@gmail.com","created_at":"2026-02-12T21:35:07.1826787-08:00","created_by":"zenchantlive","updated_at":"2026-02-12T21:36:56.4424829-08:00"} -{"id":"bb-dcv.1","title":"Research \u0026 RFC: Agent Skills and Handoff Protocols","description":"Conduct research and draft a 'Request for Comments' (RFC) document that defines the standard operating procedures for agent-to-agent interaction within Beadboard.\n\nKey Questions to Answer:\n1. Identity: How do we consistently identify an agent? (e.g. assignee formats).\n2. Handoff Protocol: Structure of a handoff comment (e.g. [HANDOFF]).\n3. Blocker Signaling: How to raise a flag (e.g. [BLOCKED]).\n4. Parsing: Can/should we have bd parse-comments?\n\nDeliverables:\n- docs/RFC-001-Agent-Coordination.md: Finalized spec.\n- skills/beadboard-driver/SKILL.md (Draft): Prototype skill.\n- Gap Analysis: Missing CLI commands.\n\nAcceptance Criteria:\n- RFC document created and committed.\n- Protocol covers: Identity, Handoff, Blocker, Assignment.\n- Gap analysis lists required code changes.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-12T21:37:32.9086915-08:00","created_by":"zenchantlive","updated_at":"2026-02-12T21:37:32.9086915-08:00","dependencies":[{"issue_id":"bb-dcv.1","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-12T21:37:32.9107758-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.1","title":"Research \u0026 RFC: Agent Skills and Handoff Protocols","description":"Conduct research and draft a 'Request for Comments' (RFC) document that defines the standard operating procedures for agent-to-agent interaction within Beadboard.\n\nKey Questions to Answer:\n1. Identity: How do we consistently identify an agent? (e.g. assignee formats).\n2. Handoff Protocol: Structure of a handoff comment (e.g. [HANDOFF]).\n3. Blocker Signaling: How to raise a flag (e.g. [BLOCKED]).\n4. Parsing: Can/should we have bd parse-comments?\n\nDeliverables:\n- docs/RFC-001-Agent-Coordination.md: Finalized spec.\n- skills/beadboard-driver/SKILL.md (Draft): Prototype skill.\n- Gap Analysis: Missing CLI commands.\n\nAcceptance Criteria:\n- RFC document created and committed.\n- Protocol covers: Identity, Handoff, Blocker, Assignment.\n- Gap analysis lists required code changes.","notes":"Drafted RFC and skill artifacts: docs/RFC-001-Agent-Coordination.md and skills/beadboard-driver/SKILL.md. Includes identity, handoff/blocker/assignment protocol and gap analysis for bb agent CLI.\nSkill deliverable moved to a dedicated later task so RFC scope remains protocol/spec + gap analysis only.","status":"in_progress","priority":1,"issue_type":"task","assignee":"zenchantlive","owner":"jordanlive121@gmail.com","created_at":"2026-02-12T21:37:32.9086915-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T14:45:05.8683583-08:00","dependencies":[{"issue_id":"bb-dcv.1","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-12T21:37:32.9107758-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.2","title":"Define bb agent CLI contract and storage schema","description":"Write the thin-layer CLI contract for bb agent commands and the on-disk schema under .beadboard/agent. Include command I/O examples and validation rules. This is implementation input, not a refactor.","acceptance_criteria":"Spec includes: register/list/show, send/inbox/read/ack, reserve/release/status; JSON schema and file layout are documented; failure modes are defined.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T12:56:41.2806805-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T12:56:41.2806805-08:00","labels":["agents","cli","design"],"dependencies":[{"issue_id":"bb-dcv.2","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T12:56:41.2822506-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.2","depends_on_id":"bb-dcv.1","type":"blocks","created_at":"2026-02-13T12:57:08.7260185-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.3","title":"Final verification and readiness sweep for agent CLI","description":"Run full verification for the thin-layer agent CLI and publish evidence from tests/lint/typecheck plus dependency sanity.","acceptance_criteria":"typecheck/test/lint pass; dependency graph is acyclic and reflects plan; readiness summary posted.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T12:56:55.8190789-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T12:56:55.8190789-08:00","labels":["agents","qa","verification"],"dependencies":[{"issue_id":"bb-dcv.3","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T12:56:55.8211858-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.3","depends_on_id":"bb-dcv.5","type":"blocks","created_at":"2026-02-13T12:57:13.0099035-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.3","depends_on_id":"bb-dcv.8","type":"blocks","created_at":"2026-02-13T14:45:12.4489854-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.4","title":"Implement reservation commands with TTL","description":"Implement reserve/release/status commands for work surfaces with TTL expiry and stale ownership handling.","acceptance_criteria":"reserve/release/status work; expired reservations are surfaced/cleared; conflict scenarios are test-covered.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T12:56:56.3114764-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T12:56:56.3114764-08:00","labels":["agents","cli","reservations"],"dependencies":[{"issue_id":"bb-dcv.4","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T12:56:56.3130569-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.4","depends_on_id":"bb-dcv.2","type":"blocks","created_at":"2026-02-13T12:57:10.8788059-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.4","depends_on_id":"bb-dcv.7","type":"blocks","created_at":"2026-02-13T12:57:11.4135844-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.5","title":"Integrate bb agent UX with bd claim workflow","description":"Add CLI UX layer and docs so bb agent flows pair cleanly with bd update --claim, without direct JSONL writes.","acceptance_criteria":"Docs include canonical session flow; commands produce operator-friendly output; no direct JSONL writes introduced.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T12:56:56.7418732-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T12:56:56.7418732-08:00","labels":["agents","cli","workflow"],"dependencies":[{"issue_id":"bb-dcv.5","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T12:56:56.7434894-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.5","depends_on_id":"bb-dcv.6","type":"blocks","created_at":"2026-02-13T12:57:11.9292114-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.5","depends_on_id":"bb-dcv.4","type":"blocks","created_at":"2026-02-13T12:57:12.4685539-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.6","title":"Implement agent mail commands (send/inbox/read/ack)","description":"Implement file-backed message transport for registered agents with unread/read/acked states and bead-linked thread context.","acceptance_criteria":"send/inbox/read/ack commands work end-to-end; sender/recipient must be registered; message lifecycle is test-covered.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T12:56:57.2090515-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T12:56:57.2090515-08:00","labels":["agents","cli","mail"],"dependencies":[{"issue_id":"bb-dcv.6","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T12:56:57.210616-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.6","depends_on_id":"bb-dcv.2","type":"blocks","created_at":"2026-02-13T12:57:09.7811635-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.6","depends_on_id":"bb-dcv.7","type":"blocks","created_at":"2026-02-13T12:57:10.3349432-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.7","title":"Implement agent identity registry commands","description":"Implement bb agent register/list/show with unique-name enforcement and stable metadata files under .beadboard/agent/agents.","acceptance_criteria":"register/list/show commands work; duplicate names fail with clear error; tests cover happy/error paths.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T12:56:57.6944409-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T12:56:57.6944409-08:00","labels":["agents","cli","identity"],"dependencies":[{"issue_id":"bb-dcv.7","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T12:56:57.6961264-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.7","depends_on_id":"bb-dcv.2","type":"blocks","created_at":"2026-02-13T12:57:09.2534901-08:00","created_by":"zenchantlive"}]} +{"id":"bb-dcv.8","title":"Create beadboard-driver skill from implemented bb agent workflows","description":"Use skill-creator workflow to produce the beadboard-driver skill only after bb agent identity, mail, reservation, and workflow commands are implemented and verified.","acceptance_criteria":"SKILL.md matches implemented CLI behavior; trigger language is explicit; no speculative commands included; quick validation performed.","status":"open","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-13T14:45:05.4433258-08:00","created_by":"zenchantlive","updated_at":"2026-02-13T14:45:05.4433258-08:00","labels":["agents","docs","skill"],"dependencies":[{"issue_id":"bb-dcv.8","depends_on_id":"bb-dcv","type":"parent-child","created_at":"2026-02-13T14:45:05.4449006-08:00","created_by":"zenchantlive"},{"issue_id":"bb-dcv.8","depends_on_id":"bb-dcv.5","type":"blocks","created_at":"2026-02-13T14:45:11.9133726-08:00","created_by":"zenchantlive"}]} {"id":"bb-n7p","title":"Swimlane status model: ready + dependency-derived blocked","notes":"Implemented new swimlane model: removed deferred lane from board usage; added ready lane and dependency-derived blocked lane. Lane rules: closed-\u003eDone; blocked-\u003eBlocked if explicit status blocked OR has active incoming blocker edge; in_progress/review-\u003eIn Progress; otherwise Ready. Added laneToMutationStatus to map board lane writes to bead statuses (ready-\u003eopen). Updated board labels/colors, drag-drop lane source tracking, and controls stat label Open-\u003eReady. TDD: updated tests/lib/kanban.test.ts for ready/blocked semantics. Verification: node --import tsx --test tests/lib/kanban.test.ts (pass), npm run typecheck (pass), npm run test (pass).","status":"closed","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-12T17:55:04.1851993-08:00","created_by":"zenchantlive","updated_at":"2026-02-12T18:40:08.0620089-08:00","closed_at":"2026-02-12T18:40:08.0620089-08:00","close_reason":"Implemented ready/blocked swimlane model, blocked-tree deep links to lane focus, and verification passed (kanban tests, typecheck, full test suite).","labels":["kanban","status","swimlane"]} {"id":"bb-q1s","title":"UI Bead Editing Across Kanban + Graph","description":"Objective:\nAdd true UI editing for bead fields across both detail panels (Kanban + Graph) using one shared edit core so behavior stays consistent.\n\nWhy:\nWrite-back infrastructure exists, but users currently cannot edit bead content from UI detail panels.\n\nScope:\n- Shared edit validation + mutation adapter.\n- Reusable editor UI block for issue fields.\n- Integration into both Kanban and Graph detail panels.\n- Verification for responsive behavior and mutation safety.\n\nOut of scope:\n- Dependency relation editing.\n- AI content generation.\n- Bulk editing.","acceptance_criteria":"- Users can edit core bead fields from both Kanban and Graph detail panels.\n- Both surfaces use the same validation and update path.\n- Save/cancel/error states are consistent across both surfaces.\n- Typecheck/tests/guards pass and no direct JSONL writes are introduced.","notes":"Execution order enforced through child dependencies.\nExecution order: bb-q1s.1 shared core -\u003e bb-q1s.2 kanban + bb-q1s.3 graph (parallel) -\u003e bb-q1s.4 verification/polish.","status":"closed","priority":1,"issue_type":"epic","owner":"jordanlive121@gmail.com","created_at":"2026-02-12T20:50:12.3431904-08:00","created_by":"zenchantlive","updated_at":"2026-02-12T21:11:43.1747329-08:00","closed_at":"2026-02-12T21:11:43.1747329-08:00","close_reason":"Shared UI bead editing shipped across Kanban and Graph with verification evidence.","labels":["editing","mutation","ui"]} {"id":"bb-q1s.1","title":"Shared edit core: schema + update adapter + state machine","description":"Build shared edit core used by both detail panels.\n\nIncludes:\n- editable field schema\n- validation rules\n- payload adapter for /api/beads/update\n- form state model: pristine/dirty/saving/error","acceptance_criteria":"- Shared edit core is framework-agnostic and reused by both UIs.\n- Validation covers title/priority/labels/assignee/owner/description.\n- Adapter emits stable update payload.","notes":"Implemented shared edit core in src/lib/issue-editor.ts with draft schema, validation, diff-to-update adapter, label parsing, and edit-state classifier. Added tests in tests/lib/issue-editor.test.ts and expanded mutation adapter to support issueType updates.","status":"closed","priority":1,"issue_type":"task","owner":"jordanlive121@gmail.com","created_at":"2026-02-12T20:50:31.668852-08:00","created_by":"zenchantlive","updated_at":"2026-02-12T21:10:59.9315015-08:00","closed_at":"2026-02-12T21:10:59.9315015-08:00","close_reason":"Shared edit core delivered and validated via unit tests + typecheck.","labels":["editing","lib","shared"],"dependencies":[{"issue_id":"bb-q1s.1","depends_on_id":"bb-q1s","type":"parent-child","created_at":"2026-02-12T20:50:31.6709483-08:00","created_by":"zenchantlive"}]} diff --git a/docs/RFC-001-Agent-Coordination.md b/docs/RFC-001-Agent-Coordination.md new file mode 100644 index 0000000..f7164c0 --- /dev/null +++ b/docs/RFC-001-Agent-Coordination.md @@ -0,0 +1,171 @@ +# RFC-001 Agent Coordination Protocol + +Date: 2026-02-13 +Owner: `bb-dcv.1` +Status: Draft +Scope: Define a CLI-first, issue-linked protocol for multi-agent coordination in BeadBoard. + +## 0) Reference Position + +`mcp_agent_mail` is used as a pattern reference only (identity registration, inbox/ack workflow, reservations). +We are not integrating MCP mail transport into BeadBoard for this phase. + +Decision: +1. Borrow concepts. +2. Implement a local thin-layer `bb agent` CLI. +3. Keep Beads as issue/dependency system of record. + +## 1) Problem Statement + +BeadBoard needs reliable agent-to-agent coordination that does not depend on chat context and does not mutate `.beads/issues.jsonl` directly. Today, ownership is partially covered by Beads (`bd update --claim`), but directed communication, acknowledgements, and short-lived work-surface reservations are not standardized. + +This RFC defines: +1. Agent identity conventions. +2. Handoff/blocker/decision communication protocol. +3. Assignment/claim expectations. +4. The required thin-layer `bb agent` CLI capabilities. + +## 2) Design Goals and Non-Goals + +Goals: +1. Keep Beads as source of truth for issue lifecycle and dependencies. +2. Add durable coordination metadata with clear auditability. +3. Require explicit bead linkage for coordination events. +4. Support parallel work without accidental overlap. + +Non-goals: +1. Replacing Beads issue state with a new tracker. +2. Direct writes to Beads JSONL outside `bd`. +3. Introducing MCP requirements for core local workflows. + +## 3) Identity Standard + +Each automation participant uses a stable `agent_id` (example: `agent-ui-1`, `agent-graph-1`). + +Rules: +1. `agent_id` is globally unique within a repo. +2. `agent_id` is immutable after registration. +3. Operator identity and agent identity are both retained in logs. + +Canonical fields: +1. `agent_id` +2. `display_name` +3. `role` (`ui`, `graph`, `infra`, `qa`, etc.) +4. `created_at` +5. `last_seen_at` +6. `status` (`idle`, `working`, `blocked`, `done`) + +## 4) Assignment and Ownership Protocol + +Ownership remains Beads-native: +1. Claim issue with `bd update --claim`. +2. Move lifecycle through normal `bd` commands. +3. Use Beads dependencies as execution truth. + +Agent protocol requirements: +1. Every coordination message includes `bead_id`. +2. Any handoff sets a clear next owner or recipient. +3. Any blocker message includes a requested action and urgency. + +## 5) Communication Protocol + +Message categories: +1. `HANDOFF`: work transition with current state and next action. +2. `BLOCKED`: hard blocker requiring external action. +3. `DECISION`: decision record with rationale. +4. `INFO`: non-blocking operational context. + +Required message envelope: +1. `message_id` +2. `thread_id` (default `bead:`) +3. `bead_id` +4. `from_agent` +5. `to_agent` (or `broadcast`) +6. `category` +7. `subject` +8. `body` +9. `created_at` +10. `state` (`unread`, `read`, `acked`) + +Ack requirement: +1. `BLOCKED` and `HANDOFF` messages require acknowledgement. +2. `DECISION` and `INFO` acknowledgements are optional. + +## 6) Reservation Protocol + +Reservations are advisory by default with TTL: +1. Reserve scope before edits on contested surfaces. +2. Scope examples: `src/components/graph/*`, `kanban-surface`, `api/mutations`. +3. Expired reservations are considered stale and available for takeover. + +Required reservation fields: +1. `reservation_id` +2. `scope` +3. `agent_id` +4. `bead_id` +5. `created_at` +6. `expires_at` +7. `state` (`active`, `released`, `expired`) + +## 7) Gap Analysis (Current vs Required) + +Available now: +1. `bd update --claim` provides atomic issue claim. +2. `bd` dependency graph provides blocked/ready sequencing. +3. `bd agent state/heartbeat` can report liveness for agent beads. + +Missing (must be added): +1. Agent registration and identity registry commands. +2. Directed message transport with unread/read/acked lifecycle. +3. Reservation commands with TTL and stale handling. +4. Unified status view tying claim + message + reservation state. + +Command matrix: +1. Have now: `bd update --claim`, `bd update`, `bd close`, `bd dep`, `bd comments`. +2. Need build: `bb agent register/list/show`. +3. Need build: `bb agent send/inbox/read/ack`. +4. Need build: `bb agent reserve/release/status`. + +Failure modes to handle: +1. Duplicate `agent_id` registration. +2. Unknown sender or recipient in send flow. +3. Missing `bead_id` on required message categories. +4. Reservation conflicts and stale-expiry takeover behavior. +5. Ack on unknown or already terminal message state. + +## 8) Required Thin-Layer CLI Surface + +Proposed commands (`bb agent`): +1. `register`, `list`, `show` +2. `send`, `inbox`, `read`, `ack` +3. `reserve`, `release`, `status` + +Storage: +1. `.beadboard/agent/agents/*.json` +2. `.beadboard/agent/messages/*.jsonl` +3. `.beadboard/agent/reservations/*.json` +4. `.beadboard/agent/index/*.json` (optional fast lookup) + +Safety rules: +1. No direct writes to `.beads/issues.jsonl`. +2. Bead lifecycle mutations remain via `bd`. +3. All timestamps are UTC ISO-8601. + +## 9) Acceptance Criteria Mapping + +Identity covered: Section 3. +Handoff covered: Sections 4 and 5 (`HANDOFF`). +Blocker signaling covered: Sections 5 and 6 (`BLOCKED`, reservation escalation). +Assignment covered: Section 4 (`bd --claim` as ownership source). +Gap analysis covered: Section 7. + +## 10) Rejected Alternatives + +1. Full MCP-native mail integration now. +Reason: unnecessary platform dependency for immediate local workflow goals. + +2. Keep coordination only in ad-hoc bead comments. +Reason: no directed inbox/ack semantics and weak reservation signaling. + +3. Replace Beads lifecycle with custom coordination store. +Reason: duplicates existing dependency/lifecycle truth and raises migration risk. diff --git a/src/components/graph/dependency-graph-page.tsx b/src/components/graph/dependency-graph-page.tsx index e02528d..3dfed5a 100644 --- a/src/components/graph/dependency-graph-page.tsx +++ b/src/components/graph/dependency-graph-page.tsx @@ -553,14 +553,14 @@ export function DependencyGraphPage({ if (dep.type !== 'blocks') continue; // Avoid self-loops if (issue.id === dep.target) continue; - const edgeId = `${issue.id}:blocks:${dep.target}`; + const edgeId = `${dep.target}:blocks:${issue.id}`; const linkedToSelection = selectedId ? issue.id === selectedId || dep.target === selectedId : false; graphEdges.push({ id: edgeId, - source: issue.id, - target: dep.target, + source: dep.target, + target: issue.id, className: linkedToSelection ? 'workflow-edge-selected' : 'workflow-edge-muted', animated: linkedToSelection, label: 'BLOCKS', diff --git a/src/components/kanban/kanban-page.tsx b/src/components/kanban/kanban-page.tsx index 00fae3d..8cf8ab2 100644 --- a/src/components/kanban/kanban-page.tsx +++ b/src/components/kanban/kanban-page.tsx @@ -188,10 +188,8 @@ export function KanbanPage({ } }, [projectRoot]); + // Auto-refresh when issues change on disk (SSE) useEffect(() => { - if (!allowMutations) { - return; - } const source = new EventSource(`/api/events?projectRoot=${encodeURIComponent(projectRoot)}`); const onIssues = () => { void refreshIssues({ silent: true }); @@ -203,7 +201,7 @@ export function KanbanPage({ source.removeEventListener('issues', onIssues as EventListener); source.close(); }; - }, [allowMutations, projectRoot, refreshIssues]); + }, [projectRoot, refreshIssues]); const mutateStatus = async (issue: BeadIssue, targetStatus: KanbanStatus) => { if (!allowMutations) { diff --git a/src/lib/graph.ts b/src/lib/graph.ts index 055a4ec..fae518d 100644 --- a/src/lib/graph.ts +++ b/src/lib/graph.ts @@ -100,7 +100,12 @@ export function buildGraphModel(issues: BeadIssue[], options: BuildGraphModelOpt continue; } - const edgeKey = `${issue.id}::${dependency.type}::${dependency.target}`; + // Beads "blocks" dependency means: issue depends on target, so target blocks issue. + // Normalize graph direction to blocker -> blocked for all blocker analytics and UI signals. + const source = dependency.type === 'blocks' ? dependency.target : issue.id; + const target = dependency.type === 'blocks' ? issue.id : dependency.target; + + const edgeKey = `${source}::${dependency.type}::${target}`; if (edgeKeys.has(edgeKey)) { diagnostics.droppedDuplicates += 1; continue; @@ -108,8 +113,8 @@ export function buildGraphModel(issues: BeadIssue[], options: BuildGraphModelOpt edgeKeys.add(edgeKey); edges.push({ - source: issue.id, - target: dependency.target, + source, + target, type: dependency.type, }); } diff --git a/tests/lib/graph-view.test.ts b/tests/lib/graph-view.test.ts index 0036d1c..72b7652 100644 --- a/tests/lib/graph-view.test.ts +++ b/tests/lib/graph-view.test.ts @@ -43,11 +43,11 @@ test('buildGraphViewModel limits visible nodes by hop depth around focus', () => assert.deepEqual( depth1.nodes.map((x) => x.id), - ['bb-2', 'bb-1', 'bb-3'], + ['bb-2', 'bb-3', 'bb-1'], ); assert.deepEqual( depth2.nodes.map((x) => x.id), - ['bb-2', 'bb-1', 'bb-3', 'bb-4'], + ['bb-2', 'bb-4', 'bb-3', 'bb-1'], ); }); @@ -81,7 +81,7 @@ test('buildGraphViewModel keeps deterministic edge ordering', () => { assert.deepEqual( view.edges.map((x) => `${x.source}|${x.type}|${x.target}`), - ['bb-2|blocks|bb-3', 'bb-2|parent|bb-1'], + ['bb-2|parent|bb-1', 'bb-3|blocks|bb-2'], ); assert.equal(view.nodes.every((x) => Number.isFinite(x.position.x) && Number.isFinite(x.position.y)), true); }); @@ -96,8 +96,8 @@ test('buildPathWorkspace returns upstream/downstream levels around focus', () => const workspace = buildPathWorkspace(model, { focusId: 'bb-2', depth: 2, hideClosed: false }); assert.equal(workspace.focus?.id, 'bb-2'); - assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-1']]); - assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-3']]); + assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-3']]); + assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-1']]); }); test('buildPathWorkspace hides closed nodes when requested', () => { @@ -122,15 +122,15 @@ test('buildPathWorkspace full depth keeps deterministic blocker and dependent le const workspace = buildPathWorkspace(model, { focusId: 'bb-3', depth: 'full', hideClosed: false }); - assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-2'], ['bb-1']]); - assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-4'], ['bb-5']]); + assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-4'], ['bb-5']]); + assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-2'], ['bb-1']]); }); test('analyzeBlockedChain returns blocker counts, first actionable blocker, and chain edges', () => { const model = buildGraphModel([ - issue({ id: 'bb-1', status: 'open', dependencies: [{ type: 'blocks', target: 'bb-2' }] }), - issue({ id: 'bb-2', status: 'in_progress', dependencies: [{ type: 'blocks', target: 'bb-3' }] }), - issue({ id: 'bb-3', status: 'blocked' }), + issue({ id: 'bb-1', status: 'open' }), + issue({ id: 'bb-2', status: 'in_progress', dependencies: [{ type: 'blocks', target: 'bb-1' }] }), + issue({ id: 'bb-3', status: 'blocked', dependencies: [{ type: 'blocks', target: 'bb-2' }] }), ]); const summary = analyzeBlockedChain(model, { focusId: 'bb-3' }); @@ -155,7 +155,7 @@ test('detectDependencyCycles reports cycle nodes and edges for blocks relations' assert.equal(anomaly.cycles.length, 1); assert.deepEqual(anomaly.cycleNodeIds, ['bb-1', 'bb-2', 'bb-3']); - assert.deepEqual(anomaly.cycleEdgeIds, ['bb-1:blocks:bb-2', 'bb-2:blocks:bb-3', 'bb-3:blocks:bb-1']); + assert.deepEqual(anomaly.cycleEdgeIds, ['bb-1:blocks:bb-3', 'bb-2:blocks:bb-1', 'bb-3:blocks:bb-2']); }); test('detectDependencyCycles does not mark non-cycle predecessor as cyclic', () => { @@ -170,5 +170,5 @@ test('detectDependencyCycles does not mark non-cycle predecessor as cyclic', () assert.deepEqual(anomaly.cycleNodeIds, ['bb-a', 'bb-b', 'bb-c']); assert.equal(anomaly.cycleNodeIds.includes('bb-x'), false); - assert.equal(anomaly.cycleEdgeIds.includes('bb-x:blocks:bb-a'), false); + assert.equal(anomaly.cycleEdgeIds.includes('bb-a:blocks:bb-x'), false); }); diff --git a/tests/lib/graph.test.ts b/tests/lib/graph.test.ts index 7e380f8..4fd4147 100644 --- a/tests/lib/graph.test.ts +++ b/tests/lib/graph.test.ts @@ -62,8 +62,8 @@ test('buildGraphModel extracts supported dependency types with deterministic ord model.edges.map((x) => `${x.source}|${x.type}|${x.target}`), [ 'bb-1|supersedes|bb-3', - 'bb-2|blocks|bb-3', 'bb-2|parent|bb-1', + 'bb-3|blocks|bb-2', 'bb-3|duplicates|bb-1', 'bb-3|relates_to|bb-2', ], @@ -119,9 +119,9 @@ test('buildGraphModel builds incoming/outgoing adjacency maps', () => { const model = buildGraphModel(issues); - assert.deepEqual(model.adjacency['bb-1'].outgoing.map((x) => x.target), ['bb-2']); - assert.deepEqual(model.adjacency['bb-1'].incoming.map((x) => x.source), []); - assert.deepEqual(model.adjacency['bb-2'].incoming.map((x) => x.source), ['bb-1']); - assert.deepEqual(model.adjacency['bb-2'].outgoing.map((x) => x.target), ['bb-3']); + assert.deepEqual(model.adjacency['bb-1'].outgoing.map((x) => x.target), []); + assert.deepEqual(model.adjacency['bb-1'].incoming.map((x) => x.source), ['bb-2']); + assert.deepEqual(model.adjacency['bb-2'].incoming.map((x) => x.source), []); + assert.deepEqual(model.adjacency['bb-2'].outgoing.map((x) => x.target), ['bb-1', 'bb-3']); assert.deepEqual(model.adjacency['bb-3'].incoming.map((x) => x.source), ['bb-2']); });