docs_muse_coordination.html
html
sha256:9f5112c9a633a22e53a831c6d3af637093d0ceb914393749905187046f1c548a
chore: renumber phases — merge is 05, shift harmony through…
Sonnet 4.6
patch
1 day ago
| 1 | {% extends "musehub/base.html" %} |
| 2 | |
| 3 | {% block container_extra_class %} page-container{% endblock %} |
| 4 | {% block title %}Agent Coordination — Muse Developer Docs{% endblock %} |
| 5 | {% block page_json %}{"page":"docs-coordination"}{% endblock %} |
| 6 | |
| 7 | {% block content %} |
| 8 | <div class="devdocs"> |
| 9 | <div class="devdocs-layout"> |
| 10 | |
| 11 | {# ── Sidebar ─────────────────────────────────────────────────────────────── #} |
| 12 | <aside class="devdocs-sidebar"> |
| 13 | <nav class="devdocs-nav" aria-label="Docs navigation"> |
| 14 | <div class="devdocs-nav-group"> |
| 15 | <div class="devdocs-nav-group-label">Sections</div> |
| 16 | {% for slug, num, title, desc in phases %} |
| 17 | <a class="devdocs-nav-link devdocs-nav-link--phase{% if slug == current %} devdocs-nav-link--active{% endif %}" |
| 18 | href="/muse/{{ slug }}">{{ num }} {{ title }}</a> |
| 19 | {% endfor %} |
| 20 | </div> |
| 21 | <div class="devdocs-nav-group"> |
| 22 | <div class="devdocs-nav-group-label">On this page</div> |
| 23 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#overview">Overview</a> |
| 24 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#scenario">Multi-agent scenario</a> |
| 25 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#queues">Work queues</a> |
| 26 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#lifecycle">Claim lifecycle</a> |
| 27 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#reservations">Reservations</a> |
| 28 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#heartbeat">Heartbeat</a> |
| 29 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#dag">Dependency DAG</a> |
| 30 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#sharding">Sharding</a> |
| 31 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#forecast">Forecasting</a> |
| 32 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#watch">Watch</a> |
| 33 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#sync">Sync</a> |
| 34 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#storage">Storage layout</a> |
| 35 | <a class="devdocs-nav-link devdocs-nav-link--sub" href="#cli">CLI reference</a> |
| 36 | </div> |
| 37 | </nav> |
| 38 | </aside> |
| 39 | |
| 40 | {# ── Main content ─────────────────────────────────────────────────────────── #} |
| 41 | <main class="devdocs-content" id="main-content"> |
| 42 | |
| 43 | <div class="devdocs-breadcrumb"> |
| 44 | <a href="/muse">Developer Docs</a> |
| 45 | <span>›</span> |
| 46 | <span>Agent Coordination</span> |
| 47 | </div> |
| 48 | |
| 49 | <div class="devdocs-phase-header"> |
| 50 | <span class="devdocs-phase-num">PHASE 07</span> |
| 51 | <h1 class="devdocs-phase-title">Agent Coordination</h1> |
| 52 | <p class="devdocs-phase-desc"> |
| 53 | Muse's coordination layer lets multiple agents work a shared codebase in parallel |
| 54 | without stepping on each other. It provides file-backed work queues with POSIX-atomic |
| 55 | claiming, advisory symbol reservations with heartbeat keep-alive, a dependency DAG for |
| 56 | ordering, import-graph sharding for zero-interference parallelism, and a three-pass |
| 57 | conflict forecast before a single line of code changes. |
| 58 | </p> |
| 59 | </div> |
| 60 | |
| 61 | <div class="devdocs-callout"> |
| 62 | {{ icon("info", 16, "devdocs-callout-icon") }} |
| 63 | <div> |
| 64 | <strong>File-backed, no broker required</strong> — all coordination state lives in |
| 65 | <code>.muse/coordination/</code> as content-addressed JSON. Remote sync to MuseHub |
| 66 | is optional; everything works offline. |
| 67 | </div> |
| 68 | </div> |
| 69 | |
| 70 | {# ── Overview ──────────────────────────────────────────────────────────── #} |
| 71 | <section class="devdocs-section" id="overview"> |
| 72 | <h2>Overview</h2> |
| 73 | <p> |
| 74 | Two independent primitives compose the system: |
| 75 | </p> |
| 76 | <table class="devdocs-table"> |
| 77 | <thead><tr><th>Primitive</th><th>Purpose</th><th>Atomicity</th></tr></thead> |
| 78 | <tbody> |
| 79 | <tr> |
| 80 | <td><strong>Task queue</strong></td> |
| 81 | <td>Distribute discrete units of work across agents. Producer enqueues; consumers race to claim; winner completes or fails.</td> |
| 82 | <td><code>open(O_CREAT|O_EXCL)</code> — POSIX-guaranteed single winner</td> |
| 83 | </tr> |
| 84 | <tr> |
| 85 | <td><strong>Reservations</strong></td> |
| 86 | <td>Advisory symbol-address leases. Agents declare intent before touching a symbol; <code>muse coord forecast</code> detects overlaps before they happen.</td> |
| 87 | <td><code>write_text_atomic()</code> (atomic rename); content-addressed ID prevents duplicates</td> |
| 88 | </tr> |
| 89 | </tbody> |
| 90 | </table> |
| 91 | |
| 92 | <p> |
| 93 | A typical multi-agent pipeline: |
| 94 | </p> |
| 95 | <div class="devdocs-code-block"> |
| 96 | <pre><span class="tok-cmt"># Orchestrator shards the repo into N parallel zones</span> |
| 97 | <span class="tok-fn">muse</span> coord shard <span class="tok-kw">--agents</span> <span class="tok-num">4</span> <span class="tok-kw">--json</span> |
| 98 | |
| 99 | <span class="tok-cmt"># Orchestrator enqueues one task per shard</span> |
| 100 | <span class="tok-fn">muse</span> coord enqueue <span class="tok-str">"Refactor shard 1"</span> <span class="tok-kw">--queue</span> refactor \ |
| 101 | <span class="tok-kw">--payload</span> <span class="tok-str">'{"files":["src/billing.py","src/models.py"]}'</span> \ |
| 102 | <span class="tok-kw">--run-id</span> orchestrator <span class="tok-kw">--json</span> |
| 103 | |
| 104 | <span class="tok-cmt"># Each worker claims one task</span> |
| 105 | <span class="tok-fn">muse</span> coord claim <span class="tok-kw">--queue</span> refactor <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> |
| 106 | |
| 107 | <span class="tok-cmt"># Worker reserves the symbols it will touch</span> |
| 108 | <span class="tok-fn">muse</span> coord reserve <span class="tok-str">"src/billing.py::compute_total"</span> \ |
| 109 | <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--op</span> modify <span class="tok-kw">--ttl</span> <span class="tok-num">7200</span> <span class="tok-kw">--json</span> |
| 110 | |
| 111 | <span class="tok-cmt"># Forecast before any writes</span> |
| 112 | <span class="tok-fn">muse</span> coord forecast <span class="tok-kw">--json</span> |
| 113 | |
| 114 | <span class="tok-cmt"># Work … commit … then release and complete</span> |
| 115 | <span class="tok-fn">muse</span> coord release <reservation_id> <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> |
| 116 | <span class="tok-fn">muse</span> coord complete <task_id> <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span></pre> |
| 117 | </div> |
| 118 | </section> |
| 119 | |
| 120 | {# ── Multi-agent scenario ─────────────────────────────────────────────── #} |
| 121 | <section class="devdocs-section" id="scenario"> |
| 122 | <h2 class="devdocs-section-title"><a href="#scenario">Multi-agent scenario — orchestrator + 3 workers</a></h2> |
| 123 | |
| 124 | <p> |
| 125 | This scenario shows a complete pipeline: one orchestrator shards the repo, enqueues |
| 126 | three tasks, three workers race to claim them, a forecast catches a symbol overlap |
| 127 | before any code changes, and the orchestrator collects all results. |
| 128 | </p> |
| 129 | |
| 130 | <h3 class="devdocs-subsection-title"><a href="#scenario-shard">Step 1 — shard and enqueue</a></h3> |
| 131 | |
| 132 | <div class="devdocs-code-block"> |
| 133 | <div class="devdocs-code-header"> |
| 134 | <span class="devdocs-code-lang">bash</span> |
| 135 | <span class="devdocs-code-label">orchestrator — shard into 3 zones, enqueue 3 tasks</span> |
| 136 | </div> |
| 137 | <pre><code><span class="tok-cmt"># 1. Split the import graph into 3 parallel-safe zones</span> |
| 138 | muse coord shard --agents <span class="tok-num">3</span> --json \ |
| 139 | | python3 -c "import sys,json; [print(s['shard'], s['files']) for s in json.load(sys.stdin)['shards']]" |
| 140 | <span class="tok-cmt"># 1 ['src/billing.py', 'src/models.py']</span> |
| 141 | <span class="tok-cmt"># 2 ['src/auth.py', 'src/tokens.py']</span> |
| 142 | <span class="tok-cmt"># 3 ['src/api.py', 'src/middleware.py']</span> |
| 143 | |
| 144 | <span class="tok-cmt"># 2. Enqueue one task per shard</span> |
| 145 | muse coord enqueue "Refactor shard 1" --queue refactor --priority <span class="tok-num">10</span> \ |
| 146 | --payload '{"files":["src/billing.py","src/models.py"]}' \ |
| 147 | --run-id orchestrator --json |
| 148 | muse coord enqueue "Refactor shard 2" --queue refactor --priority <span class="tok-num">10</span> \ |
| 149 | --payload '{"files":["src/auth.py","src/tokens.py"]}' \ |
| 150 | --run-id orchestrator --json |
| 151 | muse coord enqueue "Refactor shard 3" --queue refactor --priority <span class="tok-num">10</span> \ |
| 152 | --payload '{"files":["src/api.py","src/middleware.py"]}' \ |
| 153 | --run-id orchestrator --json</code></pre> |
| 154 | </div> |
| 155 | |
| 156 | <h3 class="devdocs-subsection-title"><a href="#scenario-claim">Step 2 — workers race to claim</a></h3> |
| 157 | |
| 158 | <div class="devdocs-code-block"> |
| 159 | <div class="devdocs-code-header"> |
| 160 | <span class="devdocs-code-lang">bash</span> |
| 161 | <span class="devdocs-code-label">three workers run this concurrently — each gets a different task</span> |
| 162 | </div> |
| 163 | <pre><code><span class="tok-cmt"># Each worker claims from the refactor queue (POSIX-atomic, one winner per task)</span> |
| 164 | TASK=$(muse coord claim --queue refactor --run-id agent-1 --json) |
| 165 | TASK_ID=$(echo "$TASK" | python3 -c "import sys,json; print(json.load(sys.stdin)['task_id'])") |
| 166 | FILES=$(echo "$TASK" | python3 -c "import sys,json; print(json.load(sys.stdin)['payload']['files'])")</code></pre> |
| 167 | </div> |
| 168 | <div class="devdocs-code-block devdocs-code-block--output"> |
| 169 | <div class="devdocs-code-header"> |
| 170 | <span class="devdocs-code-lang">json</span> |
| 171 | <span class="devdocs-code-label">agent-1 claim response</span> |
| 172 | </div> |
| 173 | <pre><code>{ |
| 174 | <span class="tok-key">"task_id"</span>: <span class="tok-str">"sha256:4e8b2f1a3c7d..."</span>, |
| 175 | <span class="tok-key">"title"</span>: <span class="tok-str">"Refactor shard 1"</span>, |
| 176 | <span class="tok-key">"queue"</span>: <span class="tok-str">"refactor"</span>, |
| 177 | <span class="tok-key">"claimer_run_id"</span>: <span class="tok-str">"agent-1"</span>, |
| 178 | <span class="tok-key">"claimed_at"</span>: <span class="tok-str">"2026-04-21T16:00:00Z"</span>, |
| 179 | <span class="tok-key">"expires_at"</span>: <span class="tok-str">"2026-04-21T17:00:00Z"</span>, |
| 180 | <span class="tok-key">"payload"</span>: { <span class="tok-key">"files"</span>: [<span class="tok-str">"src/billing.py"</span>, <span class="tok-str">"src/models.py"</span>] } |
| 181 | }</code></pre> |
| 182 | </div> |
| 183 | |
| 184 | <h3 class="devdocs-subsection-title"><a href="#scenario-reserve">Step 3 — reserve symbols, run forecast</a></h3> |
| 185 | |
| 186 | <div class="devdocs-code-block"> |
| 187 | <div class="devdocs-code-header"> |
| 188 | <span class="devdocs-code-lang">bash</span> |
| 189 | <span class="devdocs-code-label">each worker reserves before touching — agent-1 and agent-2 both reserve compute_total</span> |
| 190 | </div> |
| 191 | <pre><code><span class="tok-cmt"># agent-1: billing shard</span> |
| 192 | muse coord reserve "src/billing.py::compute_total" \ |
| 193 | --run-id agent-1 --op modify --ttl <span class="tok-num">7200</span> --json |
| 194 | |
| 195 | <span class="tok-cmt"># agent-2: auth shard — also touches compute_total (import chain)</span> |
| 196 | muse coord reserve "src/billing.py::compute_total" \ |
| 197 | --run-id agent-2 --op rename --ttl <span class="tok-num">7200</span> --json |
| 198 | |
| 199 | <span class="tok-cmt"># orchestrator: forecast before any agent writes code</span> |
| 200 | muse coord forecast --json</code></pre> |
| 201 | </div> |
| 202 | <div class="devdocs-code-block devdocs-code-block--output"> |
| 203 | <div class="devdocs-code-header"> |
| 204 | <span class="devdocs-code-lang">json</span> |
| 205 | <span class="devdocs-code-label">forecast — overlap detected</span> |
| 206 | </div> |
| 207 | <pre><code>{ |
| 208 | <span class="tok-key">"active_reservations"</span>: <span class="tok-num">3</span>, |
| 209 | <span class="tok-key">"call_graph_available"</span>: <span class="tok-kw">true</span>, |
| 210 | <span class="tok-key">"partial_forecast"</span>: <span class="tok-kw">false</span>, |
| 211 | <span class="tok-key">"conflicts"</span>: [ |
| 212 | { |
| 213 | <span class="tok-key">"conflict_type"</span>: <span class="tok-str">"address_overlap"</span>, |
| 214 | <span class="tok-key">"addresses"</span>: [<span class="tok-str">"src/billing.py::compute_total"</span>], |
| 215 | <span class="tok-key">"agents"</span>: [<span class="tok-str">"agent-1@feat/refactor"</span>, <span class="tok-str">"agent-2@feat/auth"</span>], |
| 216 | <span class="tok-key">"confidence"</span>: <span class="tok-num">1.0</span>, |
| 217 | <span class="tok-key">"description"</span>: <span class="tok-str">"Symbol reserved by 2 agents on different branches"</span> |
| 218 | }, |
| 219 | { |
| 220 | <span class="tok-key">"conflict_type"</span>: <span class="tok-str">"operation_conflict"</span>, |
| 221 | <span class="tok-key">"addresses"</span>: [<span class="tok-str">"src/billing.py::compute_total"</span>], |
| 222 | <span class="tok-key">"agents"</span>: [<span class="tok-str">"agent-1@feat/refactor"</span>, <span class="tok-str">"agent-2@feat/auth"</span>], |
| 223 | <span class="tok-key">"confidence"</span>: <span class="tok-num">0.9</span>, |
| 224 | <span class="tok-key">"description"</span>: <span class="tok-str">"Incompatible operations: modify vs rename on same symbol"</span> |
| 225 | } |
| 226 | ], |
| 227 | <span class="tok-key">"high_risk"</span>: <span class="tok-num">2</span>, |
| 228 | <span class="tok-key">"medium_risk"</span>: <span class="tok-num">0</span>, |
| 229 | <span class="tok-key">"low_risk"</span>: <span class="tok-num">0</span> |
| 230 | }</code></pre> |
| 231 | </div> |
| 232 | |
| 233 | <p> |
| 234 | Orchestrator sees the conflict and serializes the work: agent-1 renames first, |
| 235 | agent-2's reservation is updated to <code>depends-on</code> agent-1's: |
| 236 | </p> |
| 237 | |
| 238 | <div class="devdocs-code-block"> |
| 239 | <div class="devdocs-code-header"> |
| 240 | <span class="devdocs-code-lang">bash</span> |
| 241 | <span class="devdocs-code-label">orchestrator resolves the overlap — serialize via depends-on</span> |
| 242 | </div> |
| 243 | <pre><code><span class="tok-cmt"># Agent-2 releases its reservation and re-reserves with depends-on</span> |
| 244 | muse coord release <agent-2-reservation-id> --run-id agent-2 --json |
| 245 | muse coord reserve "src/billing.py::compute_total" \ |
| 246 | --run-id agent-2 --op modify --ttl <span class="tok-num">7200</span> \ |
| 247 | --depends-on <agent-1-reservation-id> --json</code></pre> |
| 248 | </div> |
| 249 | |
| 250 | <h3 class="devdocs-subsection-title"><a href="#scenario-complete">Step 4 — work, commit, release, complete</a></h3> |
| 251 | |
| 252 | <div class="devdocs-code-block"> |
| 253 | <div class="devdocs-code-header"> |
| 254 | <span class="devdocs-code-lang">bash</span> |
| 255 | <span class="devdocs-code-label">each worker — do work, commit, then release and complete</span> |
| 256 | </div> |
| 257 | <pre><code><span class="tok-cmt"># ... agent does work on its branch ...</span> |
| 258 | muse commit -m "refactor: shard 1 billing module" \ |
| 259 | --agent-id claude-code --model-id claude-sonnet-4-6 --sign |
| 260 | |
| 261 | <span class="tok-cmt"># Release all reservations for this run</span> |
| 262 | muse coord release --all-for-run agent-1 --run-id agent-1 --json |
| 263 | |
| 264 | <span class="tok-cmt"># Complete the task with a structured result</span> |
| 265 | muse coord complete sha256:4e8b2f1a3c7d... \ |
| 266 | --run-id agent-1 \ |
| 267 | --result '{"symbols_modified": 12, "tests_passing": true}' \ |
| 268 | --json</code></pre> |
| 269 | </div> |
| 270 | |
| 271 | <h3 class="devdocs-subsection-title"><a href="#scenario-collect">Step 5 — orchestrator collects results</a></h3> |
| 272 | |
| 273 | <div class="devdocs-code-block"> |
| 274 | <div class="devdocs-code-header"> |
| 275 | <span class="devdocs-code-lang">bash</span> |
| 276 | <span class="devdocs-code-label">orchestrator — wait for all tasks to complete</span> |
| 277 | </div> |
| 278 | <pre><code>muse coord tasks --queue refactor --status completed --json \ |
| 279 | | python3 -c " |
| 280 | import sys, json |
| 281 | data = json.load(sys.stdin) |
| 282 | for t in data['tasks']: |
| 283 | print(t['title'], '→', t['result']) |
| 284 | "</code></pre> |
| 285 | </div> |
| 286 | <div class="devdocs-code-block devdocs-code-block--output"> |
| 287 | <div class="devdocs-code-header"> |
| 288 | <span class="devdocs-code-lang">text</span> |
| 289 | <span class="devdocs-code-label">output</span> |
| 290 | </div> |
| 291 | <pre><code>Refactor shard 1 → {'symbols_modified': 12, 'tests_passing': True} |
| 292 | Refactor shard 2 → {'symbols_modified': 8, 'tests_passing': True} |
| 293 | Refactor shard 3 → {'symbols_modified': 5, 'tests_passing': True}</code></pre> |
| 294 | </div> |
| 295 | </section> |
| 296 | |
| 297 | {# ── Work queues ───────────────────────────────────────────────────────── #} |
| 298 | <section class="devdocs-section" id="queues"> |
| 299 | <h2>Work queues</h2> |
| 300 | <p> |
| 301 | Tasks are content-addressed by their inputs — same title + queue + payload + priority |
| 302 | + creator always produces the same <code>task_id</code>. Queues are named strings; |
| 303 | any alphanumeric-plus-hyphen-or-underscore name up to 64 chars is valid. |
| 304 | </p> |
| 305 | |
| 306 | <h3>TaskRecord fields</h3> |
| 307 | <table class="devdocs-table"> |
| 308 | <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead> |
| 309 | <tbody> |
| 310 | <tr><td><code>task_id</code></td><td>str (sha256:…)</td><td>Content-addressed; same inputs → same ID (idempotent enqueue)</td></tr> |
| 311 | <tr><td><code>title</code></td><td>str ≤256</td><td>Short human-readable description of the work unit</td></tr> |
| 312 | <tr><td><code>payload</code></td><td>dict</td><td>Arbitrary JSON — agent-specific data passed to the claimer</td></tr> |
| 313 | <tr><td><code>priority</code></td><td>int</td><td>Higher = claimed first; ties broken by <code>created_at</code> FIFO</td></tr> |
| 314 | <tr><td><code>queue</code></td><td>str ≤64</td><td>Queue name <code>[a-zA-Z0-9_-]+</code>; default <code>"default"</code></td></tr> |
| 315 | <tr><td><code>created_at</code></td><td>datetime (UTC)</td><td>Enqueue timestamp</td></tr> |
| 316 | <tr><td><code>created_by</code></td><td>str ≤256</td><td><code>run_id</code> of the enqueuing agent</td></tr> |
| 317 | <tr><td><code>ttl_seconds</code></td><td>int</td><td>Seconds from <code>created_at</code> before pending task expires; default 86400 (24h)</td></tr> |
| 318 | <tr><td><code>tags</code></td><td>list[str]</td><td>Up to 32 tags, each ≤64 chars; for filtering and grouping</td></tr> |
| 319 | </tbody> |
| 320 | </table> |
| 321 | |
| 322 | <div class="devdocs-code-block"> |
| 323 | <pre><span class="tok-fn">muse</span> coord enqueue <span class="tok-str">"Lint billing module"</span> \ |
| 324 | <span class="tok-kw">--queue</span> lint \ |
| 325 | <span class="tok-kw">--priority</span> <span class="tok-num">10</span> \ |
| 326 | <span class="tok-kw">--payload</span> <span class="tok-str">'{"file": "src/billing.py"}'</span> \ |
| 327 | <span class="tok-kw">--ttl</span> <span class="tok-num">3600</span> \ |
| 328 | <span class="tok-kw">--tags</span> billing python \ |
| 329 | <span class="tok-kw">--run-id</span> orch <span class="tok-kw">--json</span></pre> |
| 330 | </div> |
| 331 | </section> |
| 332 | |
| 333 | {# ── Claim lifecycle ───────────────────────────────────────────────────── #} |
| 334 | <section class="devdocs-section" id="lifecycle"> |
| 335 | <h2>Claim lifecycle</h2> |
| 336 | <p> |
| 337 | Task state is derived from two files: the task record (immutable) and the claim record |
| 338 | (mutable). The claim file is created with <code>open(O_CREAT|O_EXCL)</code> — POSIX |
| 339 | guarantees exactly one agent wins the race; all others get <code>FileExistsError</code>. |
| 340 | </p> |
| 341 | |
| 342 | <div class="devdocs-code-block"> |
| 343 | <pre>pending ──[O_CREAT|O_EXCL, one winner]──→ claimed |
| 344 | │ |
| 345 | ┌───────────────────────────┼────────────────┐ |
| 346 | ↓ ↓ ↓ |
| 347 | completed failed cancelled |
| 348 | (result set) (error set) |
| 349 | |
| 350 | ↑ Re-claim path (timed_out): |
| 351 | claimed ──[expires_at ≤ now]──→ timed_out ──[write_atomic + nonce]──→ claimed</pre> |
| 352 | </div> |
| 353 | |
| 354 | <h3>ClaimRecord fields</h3> |
| 355 | <table class="devdocs-table"> |
| 356 | <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead> |
| 357 | <tbody> |
| 358 | <tr><td><code>task_id</code></td><td>str (sha256:…)</td><td>Links to the immutable TaskRecord</td></tr> |
| 359 | <tr><td><code>claimer_run_id</code></td><td>str ≤256</td><td>Agent that claimed the task</td></tr> |
| 360 | <tr><td><code>claimed_at</code></td><td>datetime (UTC)</td><td>Initial claim timestamp</td></tr> |
| 361 | <tr><td><code>expires_at</code></td><td>datetime (UTC)</td><td>Claim TTL; default 3600s; extended by heartbeat</td></tr> |
| 362 | <tr><td><code>status</code></td><td>str</td><td><code>"claimed"</code> <code>"completed"</code> <code>"failed"</code> <code>"cancelled"</code> <code>"timed_out"</code></td></tr> |
| 363 | <tr><td><code>heartbeat_at</code></td><td>datetime (UTC)</td><td>Last heartbeat timestamp</td></tr> |
| 364 | <tr><td><code>claim_nonce</code></td><td>str</td><td>Optimistic concurrency token for re-claim race resolution</td></tr> |
| 365 | <tr><td><code>result</code></td><td>dict | null</td><td>Set by <code>muse coord complete --result JSON</code></td></tr> |
| 366 | <tr><td><code>error</code></td><td>str | null</td><td>Set by <code>muse coord fail-task --error MSG</code></td></tr> |
| 367 | </tbody> |
| 368 | </table> |
| 369 | |
| 370 | <h3>Claiming and re-claiming</h3> |
| 371 | <p> |
| 372 | <code>muse coord claim</code> scans the queue sorted by <em>priority descending, then |
| 373 | created_at ascending</em> (FIFO within a priority band). It tries each candidate in |
| 374 | order: |
| 375 | </p> |
| 376 | <ol> |
| 377 | <li><strong>Pending task</strong> — <code>open(O_CREAT|O_EXCL)</code>. POSIX-atomic. First agent to call wins; rest loop to the next candidate.</li> |
| 378 | <li><strong>Timed-out task</strong> — <code>write_text_atomic</code> with a fresh <code>claim_nonce</code>, then read back. If the nonce matches, the agent won the re-claim race. If not, another agent beat it; loop to the next candidate.</li> |
| 379 | </ol> |
| 380 | |
| 381 | <div class="devdocs-code-block"> |
| 382 | <pre><span class="tok-fn">muse</span> coord claim <span class="tok-kw">--queue</span> refactor <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> |
| 383 | |
| 384 | <span class="tok-fn">muse</span> coord complete <task_id> <span class="tok-kw">--run-id</span> agent-1 \ |
| 385 | <span class="tok-kw">--result</span> <span class="tok-str">'{"symbols_renamed": 7}'</span> <span class="tok-kw">--json</span> |
| 386 | |
| 387 | <span class="tok-fn">muse</span> coord fail-task <task_id> <span class="tok-kw">--run-id</span> agent-1 \ |
| 388 | <span class="tok-kw">--error</span> <span class="tok-str">"AST parse failed on line 42"</span> <span class="tok-kw">--json</span> |
| 389 | |
| 390 | <span class="tok-fn">muse</span> coord cancel-task <task_id> <span class="tok-kw">--run-id</span> orch <span class="tok-kw">--json</span> |
| 391 | |
| 392 | <span class="tok-fn">muse</span> coord tasks <span class="tok-kw">--queue</span> refactor <span class="tok-kw">--status</span> pending <span class="tok-kw">--json</span></pre> |
| 393 | </div> |
| 394 | |
| 395 | <div class="devdocs-callout"> |
| 396 | {{ icon("info", 16, "devdocs-callout-icon") }} |
| 397 | <div> |
| 398 | Only the <code>claimer_run_id</code> may call <code>complete</code>, <code>fail-task</code>, |
| 399 | or <code>heartbeat</code> on a claim. Orchestrators use <code>cancel-task</code>. |
| 400 | </div> |
| 401 | </div> |
| 402 | </section> |
| 403 | |
| 404 | {# ── Reservations ──────────────────────────────────────────────────────── #} |
| 405 | <section class="devdocs-section" id="reservations"> |
| 406 | <h2>Reservations</h2> |
| 407 | <p> |
| 408 | A reservation is an advisory symbol-address lease. It does not prevent writes |
| 409 | (no lock server required) — it signals intent so <code>muse coord forecast</code> |
| 410 | can detect overlaps before they produce merge conflicts. |
| 411 | </p> |
| 412 | |
| 413 | <h3>ReservationRecord fields</h3> |
| 414 | <table class="devdocs-table"> |
| 415 | <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead> |
| 416 | <tbody> |
| 417 | <tr><td><code>reservation_id</code></td><td>str (sha256:…)</td><td>Content-addressed: <code>SHA-256(run_id, branch, sorted(addresses), operation)</code></td></tr> |
| 418 | <tr><td><code>run_id</code></td><td>str ≤256</td><td>Agent identifier — use your pipeline ID</td></tr> |
| 419 | <tr><td><code>branch</code></td><td>str</td><td>Branch this agent is working on</td></tr> |
| 420 | <tr><td><code>addresses</code></td><td>list[str]</td><td>Symbol addresses; <code>fnmatch</code> glob patterns supported (<code>"billing.py::*"</code>)</td></tr> |
| 421 | <tr><td><code>created_at</code></td><td>datetime (UTC)</td><td>Reservation creation time</td></tr> |
| 422 | <tr><td><code>expires_at</code></td><td>datetime (UTC)</td><td>Hard TTL expiry; extended by heartbeat</td></tr> |
| 423 | <tr><td><code>operation</code></td><td>str | null</td><td><code>"modify"</code> <code>"rename"</code> <code>"delete"</code> <code>"extract"</code> <code>"move"</code> — used by Tier 3 forecast</td></tr> |
| 424 | </tbody> |
| 425 | </table> |
| 426 | |
| 427 | <div class="devdocs-callout"> |
| 428 | {{ icon("info", 16, "devdocs-callout-icon") }} |
| 429 | <div> |
| 430 | The reservation ID is <em>content-addressed</em> — calling <code>muse coord reserve</code> |
| 431 | twice with the same arguments returns the same ID and writes a single file. Idempotent. |
| 432 | </div> |
| 433 | </div> |
| 434 | |
| 435 | <div class="devdocs-code-block"> |
| 436 | <pre><span class="tok-cmt"># reserve one symbol</span> |
| 437 | <span class="tok-fn">muse</span> coord reserve <span class="tok-str">"src/billing.py::compute_total"</span> \ |
| 438 | <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--op</span> modify <span class="tok-kw">--ttl</span> <span class="tok-num">7200</span> <span class="tok-kw">--json</span> |
| 439 | |
| 440 | <span class="tok-cmt"># reserve an entire file with a glob</span> |
| 441 | <span class="tok-fn">muse</span> coord reserve <span class="tok-str">"src/billing.py::*"</span> \ |
| 442 | <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--op</span> rename <span class="tok-kw">--json</span> |
| 443 | |
| 444 | <span class="tok-cmt"># release when done</span> |
| 445 | <span class="tok-fn">muse</span> coord release <reservation_id> <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> |
| 446 | |
| 447 | <span class="tok-cmt"># release everything for a run (end of pipeline)</span> |
| 448 | <span class="tok-fn">muse</span> coord release <span class="tok-kw">--all-for-run</span> agent-1 <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span></pre> |
| 449 | </div> |
| 450 | |
| 451 | <h3>Active-reservation check</h3> |
| 452 | <p>A reservation is active when <em>all</em> of the following hold:</p> |
| 453 | <ol> |
| 454 | <li>No release tombstone exists for <code>reservation_id</code></li> |
| 455 | <li><code>now < max(expires_at, heartbeat.extended_expires_at)</code></li> |
| 456 | </ol> |
| 457 | <p> |
| 458 | Once a release tombstone is written it is checked before full JSON parsing — the |
| 459 | stem-only scan makes active-check O(n) in file count, not file size. |
| 460 | </p> |
| 461 | </section> |
| 462 | |
| 463 | {# ── Heartbeat ─────────────────────────────────────────────────────────── #} |
| 464 | <section class="devdocs-section" id="heartbeat"> |
| 465 | <h2>Heartbeat — TTL extension</h2> |
| 466 | <p> |
| 467 | Long-running agents extend their reservations and claim TTLs with heartbeats. |
| 468 | Each heartbeat atomically rewrites a single file with a new <code>extended_expires_at</code>. |
| 469 | </p> |
| 470 | |
| 471 | <table class="devdocs-table"> |
| 472 | <thead><tr><th>Field</th><th>Description</th></tr></thead> |
| 473 | <tbody> |
| 474 | <tr><td><code>reservation_id</code></td><td>Which reservation this keep-alive extends</td></tr> |
| 475 | <tr><td><code>run_id</code></td><td>Must match the reservation's <code>run_id</code></td></tr> |
| 476 | <tr><td><code>last_beat_at</code></td><td>This heartbeat's timestamp</td></tr> |
| 477 | <tr><td><code>extended_expires_at</code></td><td><code>now + extension_seconds</code> — new effective expiry</td></tr> |
| 478 | </tbody> |
| 479 | </table> |
| 480 | |
| 481 | <div class="devdocs-code-block"> |
| 482 | <pre><span class="tok-cmt"># extend reservation TTL</span> |
| 483 | <span class="tok-fn">muse</span> coord heartbeat <reservation_id> <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> |
| 484 | |
| 485 | <span class="tok-cmt"># extend task claim TTL</span> |
| 486 | <span class="tok-fn">muse</span> coord heartbeat <task_id> <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> |
| 487 | |
| 488 | <span class="tok-cmt"># recommended poll interval: extension_seconds / 2</span> |
| 489 | <span class="tok-cmt"># default extension: 3600s → heartbeat every ~1800s</span></pre> |
| 490 | </div> |
| 491 | |
| 492 | <p> |
| 493 | Reservation TTL defaults: 3600s. Claim TTL defaults: 3600s. Task pending TTL defaults: 86400s |
| 494 | (24h). Extension range: 1s – 31,536,000s (1 year). |
| 495 | </p> |
| 496 | </section> |
| 497 | |
| 498 | {# ── Dependency DAG ────────────────────────────────────────────────────── #} |
| 499 | <section class="devdocs-section" id="dag"> |
| 500 | <h2>Dependency DAG</h2> |
| 501 | <p> |
| 502 | Reservations can declare dependencies on other reservations — "I cannot start until |
| 503 | agent-B's rename is complete." The DAG enforces ordering without a central scheduler. |
| 504 | Acyclicity is checked at write time; cycles are structurally impossible on disk. |
| 505 | </p> |
| 506 | |
| 507 | <h3 class="devdocs-subsection-title"><a href="#dag-depends-on">Three-step dependency chain</a></h3> |
| 508 | |
| 509 | <p> |
| 510 | A rename must happen before modify, which must happen before the test update. |
| 511 | Each reservation declares its predecessor with <code>--depends-on</code>: |
| 512 | </p> |
| 513 | |
| 514 | <div class="devdocs-code-block"> |
| 515 | <div class="devdocs-code-header"> |
| 516 | <span class="devdocs-code-lang">bash</span> |
| 517 | <span class="devdocs-code-label">build a rename → modify → test chain</span> |
| 518 | </div> |
| 519 | <pre><code><span class="tok-cmt"># Step 1 — agent-A renames the symbol (no dependency)</span> |
| 520 | RENAME_ID=$(muse coord reserve "src/billing.py::compute_total" \ |
| 521 | --run-id agent-A --op rename --json \ |
| 522 | | python3 -c "import sys,json; print(json.load(sys.stdin)['reservation_id'])") |
| 523 | |
| 524 | <span class="tok-cmt"># Step 2 — agent-B modifies the renamed symbol (waits for step 1)</span> |
| 525 | MODIFY_ID=$(muse coord reserve "src/billing.py::compute_invoice_total" \ |
| 526 | --run-id agent-B --op modify --depends-on $RENAME_ID --json \ |
| 527 | | python3 -c "import sys,json; print(json.load(sys.stdin)['reservation_id'])") |
| 528 | |
| 529 | <span class="tok-cmt"># Step 3 — agent-C updates tests (waits for step 2)</span> |
| 530 | muse coord reserve "tests/test_billing.py::test_total" \ |
| 531 | --run-id agent-C --op modify --depends-on $MODIFY_ID --json</code></pre> |
| 532 | </div> |
| 533 | |
| 534 | <p> |
| 535 | Check whether agent-B is blocked before it starts work: |
| 536 | </p> |
| 537 | |
| 538 | <div class="devdocs-code-block"> |
| 539 | <div class="devdocs-code-header"> |
| 540 | <span class="devdocs-code-lang">bash</span> |
| 541 | <span class="devdocs-code-label">inspect the DAG</span> |
| 542 | </div> |
| 543 | <pre><code>muse coord dag --json</code></pre> |
| 544 | </div> |
| 545 | <div class="devdocs-code-block devdocs-code-block--output"> |
| 546 | <div class="devdocs-code-header"> |
| 547 | <span class="devdocs-code-lang">json</span> |
| 548 | <span class="devdocs-code-label">DAG output — topological order and blocked nodes</span> |
| 549 | </div> |
| 550 | <pre><code>{ |
| 551 | <span class="tok-key">"nodes"</span>: [ |
| 552 | { <span class="tok-key">"reservation_id"</span>: <span class="tok-str">"a1b2c3..."</span>, <span class="tok-key">"run_id"</span>: <span class="tok-str">"agent-A"</span>, |
| 553 | <span class="tok-key">"addresses"</span>: [<span class="tok-str">"src/billing.py::compute_total"</span>], |
| 554 | <span class="tok-key">"operation"</span>: <span class="tok-str">"rename"</span>, <span class="tok-key">"status"</span>: <span class="tok-str">"active"</span>, <span class="tok-key">"depends_on"</span>: [] }, |
| 555 | { <span class="tok-key">"reservation_id"</span>: <span class="tok-str">"d4e5f6..."</span>, <span class="tok-key">"run_id"</span>: <span class="tok-str">"agent-B"</span>, |
| 556 | <span class="tok-key">"addresses"</span>: [<span class="tok-str">"src/billing.py::compute_invoice_total"</span>], |
| 557 | <span class="tok-key">"operation"</span>: <span class="tok-str">"modify"</span>, <span class="tok-key">"status"</span>: <span class="tok-str">"blocked"</span>, <span class="tok-key">"depends_on"</span>: [<span class="tok-str">"a1b2c3..."</span>] }, |
| 558 | { <span class="tok-key">"reservation_id"</span>: <span class="tok-str">"g7h8i9..."</span>, <span class="tok-key">"run_id"</span>: <span class="tok-str">"agent-C"</span>, |
| 559 | <span class="tok-key">"addresses"</span>: [<span class="tok-str">"tests/test_billing.py::test_total"</span>], |
| 560 | <span class="tok-key">"operation"</span>: <span class="tok-str">"modify"</span>, <span class="tok-key">"status"</span>: <span class="tok-str">"blocked"</span>, <span class="tok-key">"depends_on"</span>: [<span class="tok-str">"d4e5f6..."</span>] } |
| 561 | ], |
| 562 | <span class="tok-key">"edges"</span>: [ |
| 563 | { <span class="tok-key">"from"</span>: <span class="tok-str">"a1b2c3..."</span>, <span class="tok-key">"to"</span>: <span class="tok-str">"d4e5f6..."</span>, <span class="tok-key">"reason"</span>: <span class="tok-str">"depends_on"</span> }, |
| 564 | { <span class="tok-key">"from"</span>: <span class="tok-str">"d4e5f6..."</span>, <span class="tok-key">"to"</span>: <span class="tok-str">"g7h8i9..."</span>, <span class="tok-key">"reason"</span>: <span class="tok-str">"depends_on"</span> } |
| 565 | ], |
| 566 | <span class="tok-key">"topological_order"</span>: [<span class="tok-str">"a1b2c3..."</span>, <span class="tok-str">"d4e5f6..."</span>, <span class="tok-str">"g7h8i9..."</span>], |
| 567 | <span class="tok-key">"cycles"</span>: [], |
| 568 | <span class="tok-key">"active_count"</span>: <span class="tok-num">1</span>, |
| 569 | <span class="tok-key">"blocked_count"</span>: <span class="tok-num">2</span> |
| 570 | }</code></pre> |
| 571 | </div> |
| 572 | |
| 573 | <div class="devdocs-code-block"> |
| 574 | <div class="devdocs-code-header"> |
| 575 | <span class="devdocs-code-lang">text</span> |
| 576 | <span class="devdocs-code-label">visual (topological order — safe execution sequence)</span> |
| 577 | </div> |
| 578 | <pre><code>agent-A rename compute_total [ACTIVE] |
| 579 | └─[depends_on]──▶ agent-B modify compute_invoice_total [BLOCKED] |
| 580 | └─[depends_on]──▶ agent-C modify test_total [BLOCKED]</code></pre> |
| 581 | </div> |
| 582 | |
| 583 | <div class="devdocs-code-block"> |
| 584 | <div class="devdocs-code-header"> |
| 585 | <span class="devdocs-code-lang">bash</span> |
| 586 | <span class="devdocs-code-label">inspect and release commands</span> |
| 587 | </div> |
| 588 | <pre><code><span class="tok-cmt"># inspect the full DAG</span> |
| 589 | <span class="tok-fn">muse</span> coord dag <span class="tok-kw">--json</span> |
| 590 | |
| 591 | <span class="tok-cmt"># show only active (unblocked) nodes</span> |
| 592 | <span class="tok-fn">muse</span> coord dag <span class="tok-kw">--active-only</span> <span class="tok-kw">--json</span></code></pre> |
| 593 | </div> |
| 594 | |
| 595 | <h3 class="devdocs-subsection-title"><a href="#dag-record">DependencyRecord fields</a></h3> |
| 596 | <table class="devdocs-table"> |
| 597 | <thead><tr><th>Field</th><th>Description</th></tr></thead> |
| 598 | <tbody> |
| 599 | <tr><td><code>reservation_id</code></td><td>The dependent — this reservation waits</td></tr> |
| 600 | <tr><td><code>depends_on</code></td><td>List of reservation IDs it waits for (max 256)</td></tr> |
| 601 | <tr><td><code>created_at</code></td><td>UTC timestamp</td></tr> |
| 602 | </tbody> |
| 603 | </table> |
| 604 | |
| 605 | <p> |
| 606 | <code>is_blocked(reservation_id)</code> returns <code>true</code> if any dependency |
| 607 | is still active. <code>get_blocking(reservation_id)</code> returns the list of active |
| 608 | dependencies. DAG output includes topological order (dependencies before dependents — |
| 609 | safe execution sequence) and detects cycles if one somehow exists. |
| 610 | </p> |
| 611 | </section> |
| 612 | |
| 613 | {# ── Sharding ──────────────────────────────────────────────────────────── #} |
| 614 | <section class="devdocs-section" id="sharding"> |
| 615 | <h2>Sharding</h2> |
| 616 | <p> |
| 617 | <code>muse coord shard</code> partitions the import graph into parallel-safe work zones. |
| 618 | Agents assigned to different shards never touch the same files, eliminating merge |
| 619 | conflicts by construction. |
| 620 | </p> |
| 621 | |
| 622 | <h3>Algorithm</h3> |
| 623 | <ol> |
| 624 | <li>Build the import graph from the HEAD snapshot: file → file edges from import pseudo-symbols.</li> |
| 625 | <li>Compute weakly-connected components via DFS.</li> |
| 626 | <li>Apply LPT (Longest Processing Time) heuristic: greedily assign the largest component to the shard with the fewest symbols so far, minimizing makespan.</li> |
| 627 | <li>Count <code>cross_shard_edges</code>: imports that cross shard boundaries (lower = better isolation).</li> |
| 628 | </ol> |
| 629 | |
| 630 | <div class="devdocs-code-block"> |
| 631 | <pre><span class="tok-fn">muse</span> coord shard <span class="tok-kw">--agents</span> <span class="tok-num">4</span> <span class="tok-kw">--json</span> |
| 632 | <span class="tok-fn">muse</span> coord shard <span class="tok-kw">--agents</span> <span class="tok-num">8</span> <span class="tok-kw">--language</span> Python <span class="tok-kw">--json</span> |
| 633 | <span class="tok-fn">muse</span> coord shard <span class="tok-kw">--agents</span> <span class="tok-num">4</span> <span class="tok-kw">--commit</span> HEAD~10 <span class="tok-kw">--json</span></pre> |
| 634 | </div> |
| 635 | |
| 636 | <h3>ShardPlan output</h3> |
| 637 | <div class="devdocs-code-block"> |
| 638 | <pre>{ |
| 639 | <span class="tok-key">"commit"</span>: <span class="tok-str">"f3c14a89"</span>, <span class="tok-cmt">// 8-char display ID</span> |
| 640 | <span class="tok-key">"full_commit_id"</span>: <span class="tok-str">"sha256:f3c14a89cd37…"</span>, |
| 641 | <span class="tok-key">"agents"</span>: <span class="tok-num">4</span>, |
| 642 | <span class="tok-key">"shards_created"</span>: <span class="tok-num">4</span>, <span class="tok-cmt">// may be < agents if repo has fewer components</span> |
| 643 | <span class="tok-key">"total_files"</span>: <span class="tok-num">87</span>, |
| 644 | <span class="tok-key">"total_symbols"</span>: <span class="tok-num">1204</span>, |
| 645 | <span class="tok-key">"cross_shard_edges"</span>: <span class="tok-num">3</span>, <span class="tok-cmt">// lower = better isolation</span> |
| 646 | <span class="tok-key">"shards"</span>: [ |
| 647 | { |
| 648 | <span class="tok-key">"shard"</span>: <span class="tok-num">1</span>, |
| 649 | <span class="tok-key">"files"</span>: [<span class="tok-str">"src/billing.py"</span>, <span class="tok-str">"src/models.py"</span>], |
| 650 | <span class="tok-key">"symbol_count"</span>: <span class="tok-num">312</span>, |
| 651 | <span class="tok-key">"coupling_score"</span>: <span class="tok-num">1</span> <span class="tok-cmt">// edges from this shard to others</span> |
| 652 | } |
| 653 | ] |
| 654 | }</pre> |
| 655 | </div> |
| 656 | |
| 657 | <div class="devdocs-callout"> |
| 658 | {{ icon("info", 16, "devdocs-callout-icon") }} |
| 659 | <div> |
| 660 | If the repo has fewer weakly-connected components than <code>--agents</code>, |
| 661 | <code>shards_created < agents</code>. The extra agents have nothing to claim and |
| 662 | should idle or handle a different queue. |
| 663 | </div> |
| 664 | </div> |
| 665 | </section> |
| 666 | |
| 667 | {# ── Forecasting ───────────────────────────────────────────────────────── #} |
| 668 | <section class="devdocs-section" id="forecast"> |
| 669 | <h2>Conflict forecasting</h2> |
| 670 | <p> |
| 671 | <code>muse coord forecast</code> predicts merge conflicts before any agent writes code. |
| 672 | It runs three independent passes over active reservations and intents, each with a |
| 673 | calibrated confidence score. |
| 674 | </p> |
| 675 | |
| 676 | <table class="devdocs-table"> |
| 677 | <thead><tr><th>Pass</th><th>Conflict type</th><th>Confidence</th><th>Condition</th></tr></thead> |
| 678 | <tbody> |
| 679 | <tr> |
| 680 | <td>1</td> |
| 681 | <td><code>"address_overlap"</code></td> |
| 682 | <td>1.0</td> |
| 683 | <td>Same symbol address reserved by two or more different <code>run_id</code>s</td> |
| 684 | </tr> |
| 685 | <tr> |
| 686 | <td>2</td> |
| 687 | <td><code>"blast_radius_overlap"</code></td> |
| 688 | <td>0.75</td> |
| 689 | <td>One reserved address is a transitive caller of another (requires <code>muse code index</code>); skipped if call graph unavailable</td> |
| 690 | </tr> |
| 691 | <tr> |
| 692 | <td>3</td> |
| 693 | <td><code>"operation_conflict"</code></td> |
| 694 | <td>0.9</td> |
| 695 | <td>Intents on the same address combine incompatible operations: <code>delete</code> vs <code>modify</code>, <code>rename</code>, or <code>extract</code></td> |
| 696 | </tr> |
| 697 | </tbody> |
| 698 | </table> |
| 699 | |
| 700 | <div class="devdocs-code-block"> |
| 701 | <pre><span class="tok-fn">muse</span> coord forecast <span class="tok-kw">--json</span> |
| 702 | <span class="tok-fn">muse</span> coord forecast <span class="tok-kw">--branch</span> feat/auth <span class="tok-kw">--json</span> |
| 703 | <span class="tok-fn">muse</span> coord forecast <span class="tok-kw">--min-confidence</span> <span class="tok-num">0.9</span> <span class="tok-kw">--json</span> <span class="tok-cmt"># high-risk only</span></pre> |
| 704 | </div> |
| 705 | |
| 706 | <h3>Output</h3> |
| 707 | <div class="devdocs-code-block"> |
| 708 | <pre>{ |
| 709 | <span class="tok-key">"active_reservations"</span>: <span class="tok-num">12</span>, |
| 710 | <span class="tok-key">"call_graph_available"</span>: <span class="tok-kw">true</span>, |
| 711 | <span class="tok-key">"partial_forecast"</span>: <span class="tok-kw">false</span>, |
| 712 | <span class="tok-key">"conflicts"</span>: [ |
| 713 | { |
| 714 | <span class="tok-key">"conflict_type"</span>: <span class="tok-str">"address_overlap"</span>, |
| 715 | <span class="tok-key">"addresses"</span>: [<span class="tok-str">"src/billing.py::compute_total"</span>], |
| 716 | <span class="tok-key">"agents"</span>: [<span class="tok-str">"agent-1@feat/auth"</span>, <span class="tok-str">"agent-2@feat/payments"</span>], |
| 717 | <span class="tok-key">"confidence"</span>: <span class="tok-num">1.0</span>, |
| 718 | <span class="tok-key">"description"</span>: <span class="tok-str">"Symbol reserved by 2 agents on different branches"</span> |
| 719 | } |
| 720 | ], |
| 721 | <span class="tok-key">"high_risk"</span>: <span class="tok-num">1</span>, <span class="tok-cmt">// confidence >= 0.9</span> |
| 722 | <span class="tok-key">"medium_risk"</span>: <span class="tok-num">0</span>, <span class="tok-cmt">// 0.5 <= confidence < 0.9</span> |
| 723 | <span class="tok-key">"low_risk"</span>: <span class="tok-num">0</span> <span class="tok-cmt">// confidence < 0.5</span> |
| 724 | }</pre> |
| 725 | </div> |
| 726 | |
| 727 | <p> |
| 728 | Agents are shown as <code>"run_id@branch"</code>. Pass 2 is silently skipped when the |
| 729 | code intelligence index is unavailable, setting <code>partial_forecast: true</code>. |
| 730 | </p> |
| 731 | </section> |
| 732 | |
| 733 | {# ── Watch ─────────────────────────────────────────────────────────────── #} |
| 734 | <section class="devdocs-section" id="watch"> |
| 735 | <h2>Watch — real-time event stream</h2> |
| 736 | <p> |
| 737 | <code>muse coord watch</code> streams coordination events as NDJSON (one JSON object |
| 738 | per line). On macOS/BSD it uses kqueue (zero CPU between events); on Linux it falls |
| 739 | back to polling at a configurable interval (default 1.0s). |
| 740 | </p> |
| 741 | |
| 742 | <div class="devdocs-code-block"> |
| 743 | <pre><span class="tok-fn">muse</span> coord watch <span class="tok-kw">--json</span> |
| 744 | <span class="tok-fn">muse</span> coord watch <span class="tok-kw">--once</span> <span class="tok-kw">--json</span> <span class="tok-cmt"># exit after first event</span> |
| 745 | <span class="tok-fn">muse</span> coord watch <span class="tok-kw">--kind</span> reservation <span class="tok-kw">--json</span> <span class="tok-cmt"># filter by kind</span> |
| 746 | <span class="tok-fn">muse</span> coord watch <span class="tok-kw">--run-id</span> agent-1 <span class="tok-kw">--json</span> <span class="tok-cmt"># filter by agent</span></pre> |
| 747 | </div> |
| 748 | |
| 749 | <h3>Event types and fields</h3> |
| 750 | <table class="devdocs-table"> |
| 751 | <thead><tr><th>Field</th><th>Values</th><th>Description</th></tr></thead> |
| 752 | <tbody> |
| 753 | <tr><td><code>event_type</code></td><td><code>"snapshot"</code> <code>"added"</code> <code>"modified"</code> <code>"removed"</code> <code>"expired"</code></td><td>What happened to the record</td></tr> |
| 754 | <tr><td><code>kind</code></td><td><code>"reservation"</code> <code>"intent"</code> <code>"release"</code> <code>"heartbeat"</code></td><td>Type of coordination record</td></tr> |
| 755 | <tr><td><code>id</code></td><td>str</td><td>ID of the changed record</td></tr> |
| 756 | <tr><td><code>timestamp</code></td><td>datetime (UTC)</td><td>When the event occurred</td></tr> |
| 757 | <tr><td><code>data</code></td><td>dict</td><td>Parsed record content; empty dict for removed records with no cache</td></tr> |
| 758 | </tbody> |
| 759 | </table> |
| 760 | |
| 761 | <p> |
| 762 | <code>"snapshot"</code> events fire on startup for each existing record — useful for |
| 763 | replaying current state before watching for changes. |
| 764 | </p> |
| 765 | </section> |
| 766 | |
| 767 | {# ── Sync ──────────────────────────────────────────────────────────────── #} |
| 768 | <section class="devdocs-section" id="sync"> |
| 769 | <h2>Remote sync</h2> |
| 770 | <p> |
| 771 | Coordination state can be pushed to and pulled from MuseHub, enabling multi-machine |
| 772 | agent pipelines. Remote records are stored read-only under |
| 773 | <code>.muse/coordination/remote/</code>. |
| 774 | </p> |
| 775 | |
| 776 | <div class="devdocs-code-block"> |
| 777 | <pre><span class="tok-cmt"># push local coordination records to MuseHub (batches of 500)</span> |
| 778 | <span class="tok-fn">muse</span> coord sync push \ |
| 779 | <span class="tok-kw">--hub</span> {{ site_base_url() }} \ |
| 780 | <span class="tok-kw">--owner</span> gabriel <span class="tok-kw">--slug</span> muse <span class="tok-kw">--json</span> |
| 781 | |
| 782 | <span class="tok-cmt"># pull remote records (paginated, use --since-id for incremental)</span> |
| 783 | <span class="tok-fn">muse</span> coord sync pull \ |
| 784 | <span class="tok-kw">--hub</span> {{ site_base_url() }} \ |
| 785 | <span class="tok-kw">--owner</span> gabriel <span class="tok-kw">--slug</span> muse \ |
| 786 | <span class="tok-kw">--since-id</span> 1024 <span class="tok-kw">--limit</span> <span class="tok-num">500</span> <span class="tok-kw">--json</span></pre> |
| 787 | </div> |
| 788 | |
| 789 | <p> |
| 790 | Push is idempotent: the hub responds with <code>{"inserted": N, "skipped": M}</code>. |
| 791 | Pull returns a <code>cursor</code> to use as <code>--since-id</code> in the next call. |
| 792 | Supported kinds: <code>reservation</code>, <code>intent</code>, <code>release</code>, |
| 793 | <code>heartbeat</code>, <code>task</code>, <code>claim</code>, <code>dependency</code>. |
| 794 | </p> |
| 795 | </section> |
| 796 | |
| 797 | {# ── Storage layout ────────────────────────────────────────────────────── #} |
| 798 | <section class="devdocs-section" id="storage"> |
| 799 | <h2>Storage layout</h2> |
| 800 | <p> |
| 801 | All coordination state is file-backed under <code>.muse/coordination/</code>. Every |
| 802 | file is JSON. IDs are content-addressed sha256 hex strings used directly as filenames. |
| 803 | </p> |
| 804 | |
| 805 | <div class="devdocs-code-block"> |
| 806 | <pre>.muse/coordination/ |
| 807 | reservations/<sha256-hex>.json <span class="tok-cmt">← write-once advisory symbol lease</span> |
| 808 | intents/<sha256-hex>.json <span class="tok-cmt">← write-once operation declaration (no TTL)</span> |
| 809 | releases/<sha256-hex>.json <span class="tok-cmt">← write-once tombstone (completed/cancelled)</span> |
| 810 | heartbeats/<sha256-hex>.json <span class="tok-cmt">← mutable keep-alive; atomically overwritten</span> |
| 811 | tasks/<sha256-hex>.json <span class="tok-cmt">← immutable task definition</span> |
| 812 | claims/<sha256-hex>.json <span class="tok-cmt">← mutable claim state (O_CREAT|O_EXCL then atomic)</span> |
| 813 | dependencies/<sha256-hex>.json <span class="tok-cmt">← write-once DAG edges</span> |
| 814 | remote/ <span class="tok-cmt">← records synced from MuseHub (read-only)</span></pre> |
| 815 | </div> |
| 816 | |
| 817 | <h3>GC policy</h3> |
| 818 | <p> |
| 819 | <code>muse coord gc</code> collects stale records after a configurable grace period |
| 820 | (default 300s = 5 min, protecting against agents mid-read). Run dry-run first: |
| 821 | </p> |
| 822 | <div class="devdocs-code-block"> |
| 823 | <pre><span class="tok-fn">muse</span> coord gc <span class="tok-kw">--json</span> <span class="tok-cmt"># dry-run, shows what would be collected</span> |
| 824 | <span class="tok-fn">muse</span> coord gc <span class="tok-kw">--execute</span> <span class="tok-kw">--json</span> <span class="tok-cmt"># actually delete</span></pre> |
| 825 | </div> |
| 826 | <table class="devdocs-table"> |
| 827 | <thead><tr><th>Collected</th><th>Condition</th></tr></thead> |
| 828 | <tbody> |
| 829 | <tr><td>Expired reservations</td><td>TTL exhausted (including heartbeat extensions), past grace period</td></tr> |
| 830 | <tr><td>Released reservations</td><td>Release tombstone present, older than grace period</td></tr> |
| 831 | <tr><td>Orphaned releases</td><td>Tombstone exists but reservation file gone</td></tr> |
| 832 | <tr><td>Orphaned heartbeats</td><td>Heartbeat exists but reservation file gone</td></tr> |
| 833 | <tr><td>Old intents (opt-in)</td><td><code>--include-intents</code>; older than <code>--max-intent-age</code> (default 7 days)</td></tr> |
| 834 | </tbody> |
| 835 | </table> |
| 836 | </section> |
| 837 | |
| 838 | {# ── CLI Reference ─────────────────────────────────────────────────────── #} |
| 839 | <section class="devdocs-section" id="cli"> |
| 840 | <h2>CLI reference</h2> |
| 841 | <p>All commands accept <code>--json</code>.</p> |
| 842 | |
| 843 | <table class="devdocs-table"> |
| 844 | <thead><tr><th>Task</th><th>Command</th></tr></thead> |
| 845 | <tbody> |
| 846 | <tr><td>Enqueue a task</td><td><code>muse coord enqueue "title" --run-id orch --json</code></td></tr> |
| 847 | <tr><td>Claim next pending task</td><td><code>muse coord claim --run-id agent-1 --json</code></td></tr> |
| 848 | <tr><td>Claim from specific queue</td><td><code>muse coord claim --queue refactor --run-id agent-1 --json</code></td></tr> |
| 849 | <tr><td>Complete a task</td><td><code>muse coord complete <task_id> --run-id agent-1 --json</code></td></tr> |
| 850 | <tr><td>Fail a task</td><td><code>muse coord fail-task <task_id> --run-id agent-1 --error "msg" --json</code></td></tr> |
| 851 | <tr><td>Cancel a task</td><td><code>muse coord cancel-task <task_id> --run-id orch --json</code></td></tr> |
| 852 | <tr><td>List tasks</td><td><code>muse coord tasks --status pending --json</code></td></tr> |
| 853 | <tr><td>Reserve symbol addresses</td><td><code>muse coord reserve "billing.py::Fn" --run-id agent-1 --op modify --json</code></td></tr> |
| 854 | <tr><td>Reserve with TTL</td><td><code>muse coord reserve "billing.py::Fn" --run-id agent-1 --ttl 7200 --json</code></td></tr> |
| 855 | <tr><td>Reserve with dependency</td><td><code>muse coord reserve "billing.py::Fn" --run-id agent-B --depends-on <id> --json</code></td></tr> |
| 856 | <tr><td>Release a reservation</td><td><code>muse coord release <id> --run-id agent-1 --json</code></td></tr> |
| 857 | <tr><td>Release all for a run</td><td><code>muse coord release --all-for-run agent-1 --run-id agent-1 --json</code></td></tr> |
| 858 | <tr><td>Heartbeat (reservation)</td><td><code>muse coord heartbeat <reservation_id> --run-id agent-1 --json</code></td></tr> |
| 859 | <tr><td>Heartbeat (claim)</td><td><code>muse coord heartbeat <task_id> --run-id agent-1 --json</code></td></tr> |
| 860 | <tr><td>List active reservations</td><td><code>muse coord list --json</code></td></tr> |
| 861 | <tr><td>Inspect DAG</td><td><code>muse coord dag --json</code></td></tr> |
| 862 | <tr><td>Shard codebase</td><td><code>muse coord shard --agents 4 --json</code></td></tr> |
| 863 | <tr><td>Forecast conflicts</td><td><code>muse coord forecast --json</code></td></tr> |
| 864 | <tr><td>Reconcile merge order</td><td><code>muse coord reconcile --json</code></td></tr> |
| 865 | <tr><td>Watch events</td><td><code>muse coord watch --json</code></td></tr> |
| 866 | <tr><td>Push to MuseHub</td><td><code>muse coord sync push --hub URL --owner O --slug S --json</code></td></tr> |
| 867 | <tr><td>Pull from MuseHub</td><td><code>muse coord sync pull --hub URL --owner O --slug S --json</code></td></tr> |
| 868 | <tr><td>GC stale records (dry-run)</td><td><code>muse coord gc --json</code></td></tr> |
| 869 | <tr><td>GC (execute)</td><td><code>muse coord gc --execute --json</code></td></tr> |
| 870 | </tbody> |
| 871 | </table> |
| 872 | |
| 873 | <h3>Default TTLs</h3> |
| 874 | <table class="devdocs-table"> |
| 875 | <thead><tr><th>Entity</th><th>Default TTL</th></tr></thead> |
| 876 | <tbody> |
| 877 | <tr><td>Reservation</td><td>3600s (1h)</td></tr> |
| 878 | <tr><td>Task claim</td><td>3600s (1h)</td></tr> |
| 879 | <tr><td>Task pending</td><td>86400s (24h)</td></tr> |
| 880 | <tr><td>Heartbeat extension</td><td>3600s</td></tr> |
| 881 | <tr><td>Intent</td><td>No TTL — permanent until GC with <code>--include-intents</code></td></tr> |
| 882 | <tr><td>GC grace period</td><td>300s (5 min)</td></tr> |
| 883 | </tbody> |
| 884 | </table> |
| 885 | |
| 886 | <nav class="devdocs-phase-nav" aria-label="Phase navigation"> |
| 887 | <a class="devdocs-phase-nav-btn devdocs-phase-nav-btn--prev" href="/muse/harmony"> |
| 888 | {{ icon("arrow-left", 14) }} |
| 889 | Phase 06: Harmony |
| 890 | </a> |
| 891 | <a class="devdocs-phase-nav-btn devdocs-phase-nav-btn--next" href="/muse/mcp"> |
| 892 | Phase 08: MCP Tooling |
| 893 | {{ icon("arrow-right", 14) }} |
| 894 | </a> |
| 895 | </nav> |
| 896 | </section> |
| 897 | |
| 898 | </main> |
| 899 | </div>{# /.devdocs-layout #} |
| 900 | </div>{# /.devdocs #} |
| 901 | {% endblock %} |
| 902 | |
| 903 | {% block page_scripts %} |
| 904 | {% endblock %} |
File History
1 commit
sha256:9f5112c9a633a22e53a831c6d3af637093d0ceb914393749905187046f1c548a
chore: renumber phases — merge is 05, shift harmony through…
Sonnet 4.6
patch
1 day ago