brainclaw 1.7.2 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ If you've ever:
14
14
  - watched two coworkers (or two agents) **edit the same files** without knowing it,
15
15
  - or **gave up running multiple agents in parallel** because keeping them in sync was a pain,
16
16
 
17
- brainclaw gives you durable shared state across sessions, agents, and teammates. Plans, claims, handoffs, decisions, and traps live in `.brainclaw/`, work identically across any compatible agent (Claude Code, Codex, Copilot, Cline, OpenCode, Cursor, Windsurf, Kilocode, Roo Code, Continue, Mistral Vibe, Hermes, Antigravity/Gemini CLI, …), and stay accessible whether you orchestrate them in parallel or pick them up one after another.
17
+ brainclaw gives you durable shared state across sessions, agents, and teammates. Plans, claims, handoffs, decisions, and traps live in `.brainclaw/`, work identically across any compatible agent (Claude Code, Codex, Copilot, Cline, OpenCode, Cursor, Windsurf, Kilocode, Roo Code, Continue, Mistral Vibe, Hermes, Antigravity/Gemini CLI, …), and stay accessible whether you orchestrate them in parallel or pick them up one after another.
18
18
 
19
19
  Use it two ways — **together or separately**:
20
20
 
@@ -81,8 +81,8 @@ brainclaw is designed to sit alongside the coding agents teams are already using
81
81
 
82
82
  | Logo | Agent | Tier | What brainclaw configures |
83
83
  |---|---|---|---|
84
- | [![OpenClaw](https://img.shields.io/badge/OpenClaw-FF6B35?logoColor=white)](https://github.com/openclaw/openclaw) | **[OpenClaw](https://github.com/openclaw/openclaw)** | B | MCP + brainclaw skill (SKILL.md) for structured project memory |
85
- | [![Hermes](https://img.shields.io/badge/Hermes-111111?logoColor=white)](https://github.com/NousResearch/hermes-agent) | **[Hermes Agent](https://github.com/NousResearch/hermes-agent)** | B | MCP + universal `.agents/skills/brainclaw/SKILL.md` |
84
+ | [![OpenClaw](https://img.shields.io/badge/OpenClaw-FF6B35?logoColor=white)](https://github.com/openclaw/openclaw) | **[OpenClaw](https://github.com/openclaw/openclaw)** | B | MCP + brainclaw skill (SKILL.md) for structured project memory |
85
+ | [![Hermes](https://img.shields.io/badge/Hermes-111111?logoColor=white)](https://github.com/NousResearch/hermes-agent) | **[Hermes Agent](https://github.com/NousResearch/hermes-agent)** | B | MCP + universal `.agents/skills/brainclaw/SKILL.md` |
86
86
  | [![NanoClaw](https://img.shields.io/badge/NanoClaw-4A90D9?logoColor=white)](https://github.com/qwibitai/nanoclaw) | **[NanoClaw](https://github.com/qwibitai/nanoclaw)** | C | brainclaw skill — messaging agent (WhatsApp, Telegram, Slack) |
87
87
  | [![NemoClaw](https://img.shields.io/badge/NemoClaw-76B900?logo=nvidia&logoColor=white)](https://github.com/NVIDIA/NemoClaw) | **[NemoClaw](https://github.com/NVIDIA/NemoClaw)** | C | brainclaw skill — NVIDIA enterprise agent stack |
88
88
  | [![PicoClaw](https://img.shields.io/badge/PicoClaw-00ADD8?logo=go&logoColor=white)](https://github.com/sipeed/picoclaw) | **[PicoClaw](https://github.com/sipeed/picoclaw)** | C | brainclaw skill — edge/IoT agent (Go, <10MB RAM) |
@@ -109,52 +109,52 @@ If you want the least surprising setup today, use Linux first. If you are on Win
109
109
 
110
110
  ---
111
111
 
112
- ## Get Started
113
-
114
- ### 1. Let your coding agent lead
115
-
116
- The smoothest first-run path is agent-first:
117
-
118
- 1. ask your coding agent to inspect the package and explain what brainclaw does
119
- 2. ask it to install brainclaw and initialize or join the project you're working on
120
- 3. use the CLI yourself when you need an explicit operator or fallback path
121
-
122
- If you want to drive setup manually, use the steps below.
123
-
124
- ### 2. Install
125
-
126
- ```bash
127
- npm install -g brainclaw
128
- ```
129
-
130
- ### 3. Bootstrap this machine
131
-
132
- ```bash
133
- brainclaw setup-machine --yes
134
- ```
135
-
136
- This detects the installed coding agents on the current machine, writes the machine-level MCP and user config Brainclaw manages for that detected set, and does **not** scan or initialize repositories.
137
-
138
- ### 4. Initialize or refresh the current project
139
-
140
- ```bash
141
- cd your-project
142
- brainclaw init
143
- ```
144
-
145
- `brainclaw init` is now safe to rerun. It creates `.brainclaw/` when the project is new, or refreshes the managed Brainclaw and agent integration files when the project already has memory.
146
-
147
- If you are explicitly adding another agent to an existing Brainclaw project, use:
148
-
149
- ```bash
150
- brainclaw enable-agent <agent-name>
151
- ```
152
-
153
- ### 5. Restart your agent
154
-
155
- Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
156
-
157
- ### 6. Start working
112
+ ## Get Started
113
+
114
+ ### 1. Let your coding agent lead
115
+
116
+ The smoothest first-run path is agent-first:
117
+
118
+ 1. ask your coding agent to inspect the package and explain what brainclaw does
119
+ 2. ask it to install brainclaw and initialize or join the project you're working on
120
+ 3. use the CLI yourself when you need an explicit operator or fallback path
121
+
122
+ If you want to drive setup manually, use the steps below.
123
+
124
+ ### 2. Install
125
+
126
+ ```bash
127
+ npm install -g brainclaw
128
+ ```
129
+
130
+ ### 3. Bootstrap this machine
131
+
132
+ ```bash
133
+ brainclaw setup-machine --yes
134
+ ```
135
+
136
+ This detects the installed coding agents on the current machine, writes the machine-level MCP and user config Brainclaw manages for that detected set, and does **not** scan or initialize repositories.
137
+
138
+ ### 4. Initialize or refresh the current project
139
+
140
+ ```bash
141
+ cd your-project
142
+ brainclaw init
143
+ ```
144
+
145
+ `brainclaw init` is now safe to rerun. It creates `.brainclaw/` when the project is new, or refreshes the managed Brainclaw and agent integration files when the project already has memory.
146
+
147
+ If you are explicitly adding another agent to an existing Brainclaw project, use:
148
+
149
+ ```bash
150
+ brainclaw enable-agent <agent-name>
151
+ ```
152
+
153
+ ### 5. Restart your agent
154
+
155
+ Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
156
+
157
+ ### 6. Start working
158
158
 
159
159
  Pick one of the canonical entry points depending on what you're doing:
160
160
 
@@ -186,7 +186,7 @@ For agents without MCP (e.g. Copilot reads `.github/copilot-instructions.md`), r
186
186
  brainclaw export --detect --write
187
187
  ```
188
188
 
189
- ### 7. Verify it works
189
+ ### 7. Verify it works
190
190
 
191
191
  ```bash
192
192
  brainclaw status # see active sessions, claims, plans
@@ -197,11 +197,11 @@ brainclaw agent-board # see what each agent is doing
197
197
 
198
198
  To configure brainclaw for all your repos and agents at once:
199
199
 
200
- ```bash
201
- brainclaw setup --yes
202
- ```
203
-
204
- This is the broader multi-repo wizard. It bootstraps the machine, scans your project roots, and initializes selected repositories in one pass.
200
+ ```bash
201
+ brainclaw setup --yes
202
+ ```
203
+
204
+ This is the broader multi-repo wizard. It bootstraps the machine, scans your project roots, and initializes selected repositories in one pass.
205
205
 
206
206
  ### Existing projects
207
207
 
@@ -232,7 +232,7 @@ Still sharp:
232
232
  1. **Same-checkout concurrent edits** — running two agents in the *same* working tree (no per-claim worktree) is still the wrong answer. Use the dispatch path (auto-worktree per claim) instead of raw concurrent CLI sessions.
233
233
  2. **Cross-machine sync** — federation across machines is on the roadmap, not in v1.x. Today brainclaw's store is local and one-machine-per-project.
234
234
  3. **Spawn-and-forget assumptions** — spawned workers don't always commit their work cleanly. The brief-ack file confirms the spawn started; in the worst case the coordinator harvests open changes.
235
- 4. **Live state for hook-less agents** — Tier B/C agents without lifecycle hooks (Cursor, Cline, Windsurf, Copilot, Continue, Kilocode, Mistral Vibe, Hermes) get live context via `.live.md` companions regenerated on session-end and handoff, not via real-time push.
235
+ 4. **Live state for hook-less agents** — Tier B/C agents without lifecycle hooks (Cursor, Cline, Windsurf, Copilot, Continue, Kilocode, Mistral Vibe, Hermes) get live context via `.live.md` companions regenerated on session-end and handoff, not via real-time push.
236
236
 
237
237
  Recommended use today:
238
238
 
@@ -343,42 +343,72 @@ npm run test:coverage # with coverage report
343
343
 
344
344
  ## Changelog
345
345
 
346
- For older releases (v0.x and the early v1.0 launch series), `git log` on `master` is the source of truth — every release commit follows the `chore(release): bump version to <semver>` convention, and the matching feature/fix commits reference their plan id (e.g. `feat(mcp): self-heal ... (pln#478)`).
347
-
348
- ### v1.7.2
349
-
350
- - **Sequence MCP tools are agent-first by default** sequence creation,
351
- listing, update, and deletion tools are now in the default MCP catalog, with
352
- explicit lane item schemas (`planId`, optional `stepId`, `rank`,
353
- dependencies, lane metadata) and matching canonical CRUD validation for
354
- `entity="sequence"`.
355
-
356
- ### v1.7.1
357
-
358
- - **MCP project context isolation fix** — `bclaw_switch` now keeps MCP switches
359
- session-scoped even when the agent session has to be resolved or created on
360
- the fly. Session lookup honors explicit session IDs, avoids adopting another
361
- live process's session, detects Codex via native `CODEX_*` runtime variables,
362
- and `bclaw_switch(list=true)` reports the session active project with
363
- `active_source`.
364
-
365
- ### v1.7.0
366
-
367
- - **Dispatch reliability + scope-aware dirty guard** evidence-first
368
- `agent_run` reconciliation avoids false terminal states, `bclaw_coordinate`
369
- accepts pinned refs and a scope-aware `allow_dirty` guard, and the Hermes
370
- agent integration joins the supported surfaces.
371
-
372
- ### v1.6.0
373
-
374
- - **Bootstrap loop + cross-project agent workflow** the bootstrap ideation
375
- preset can materialize `PROJECT.md`, `bclaw_init_project` initializes and links
376
- arbitrary project paths, and `project=` routing reaches `bclaw_work` /
377
- `bclaw_loop` for linked-project operations.
378
-
379
- ### v1.5.3
380
-
381
- - **Cross-project canonical grammar + CLI parity** (pln#359, all phases) — the canonical grammar (`bclaw_find / get / create / update / remove / transition`), `bclaw_context`, and `bclaw_coordinate` now accept an optional `project: <name>` argument that routes the operation to a linked project. Two link kinds are recognised: `cross_project_links` (sibling/peer projects in `config.yaml`, `brainclaw link list`) and workspace store-chain children. Arbitrary directory paths are rejected — adoption requires an explicit link, which gives the user a single point of control over what an agent can reach. Identity is sourced from the caller's home registry; entity writes + audit log entries land in the target. Unknown project names throw `validation_error` with a hint listing the configured links — no silent fallback. Cross-project `bclaw_coordinate` is **inbox-only**: claim/assignment/message all land in the target, the target agent picks the brief up async via its own `bclaw_work`, and auto-spawn from the source process is force-disabled because the spawn cwd / worktree are tied to the target's git repo (a warning surfaces in `FacadeResponse.warnings`). The CLI exposes the same as a global `--project <name>` flag, mutually exclusive with `--cwd`. Refs: helper `resolveProjectCwd` in `src/core/cross-project.ts`, MCP write/read handler dispatch in `src/commands/mcp.ts` and `src/commands/mcp-read-handlers.ts`, `--project` plumbing in `src/cli.ts` preAction, surface advertisement in `src/core/instruction-templates.ts`, plus tests in `tests/unit/cross-project.test.ts` (10 unit cases on the helper), `tests/unit/bclaw-coordinate.test.ts` (4 cross-project routing cases), and `tests/cli-cross-project.test.ts` (5 e2e cases). Closes the `--cwd` workaround pattern that had been the day-to-day shape of multi-project sessions.
346
+ For older releases (v0.x and the early v1.0 launch series), `git log` on `master` is the source of truth — every release commit follows the `chore(release): bump version to <semver>` convention, and the matching feature/fix commits reference their plan id (e.g. `feat(mcp): self-heal ... (pln#478)`).
347
+
348
+ ### v1.7.4
349
+
350
+ - **Dispatch observability + worker DX hardening** (from a real cross-project
351
+ field session) `bclaw_dispatch_status` and the reconciler now derive liveness
352
+ from filesystem activity (log + worktree mtime), so a worker actively editing
353
+ files is no longer falsely flagged `stalled`, and known codex boot-failure
354
+ stderr signatures get a targeted diagnosis; briefs are transport-aware (a
355
+ sandboxed agent without MCP/commit gets the file protocol, not instructions it
356
+ can't follow), backed by a derived capability matrix
357
+ (`dispatchHasMcp`/`dispatchCanCommit`); `bclaw_claim` gains an advisory
358
+ (no-worktree) mode; `bclaw_find` payloads are size-bounded with pagination
359
+ metadata; an opt-in per-worktree `tsc --noEmit` pre-commit gate; gated ready
360
+ lanes carry a code-propagation advisory; the reconciler auto-releases the claim
361
+ of a run it infers failed; and `plan.related_paths` is now updatable.
362
+ (pln#479, pln#491, pln#527, pln#528, pln#529, trp#291, trp#431, trp#433, trp#434)
363
+
364
+ ### v1.7.3
365
+
366
+ - **Multi-agent dispatch hardening for JS/TS monorepos** — dispatched worktrees
367
+ junction-link per-package `node_modules` (npm / yarn / pnpm workspaces), not
368
+ just the root, and surface failed links instead of swallowing them; `brainclaw
369
+ worktree clean` now garbage-collects merged worktrees past birth-noise instead
370
+ of skipping them all; the agent inventory reports an agent `spawnable` when its
371
+ binary is on PATH even if `--version` is slow to start; dispatch-verification
372
+ guidance leads with `bclaw_dispatch_status` (not the untrustworthy Windows
373
+ wrapper pid); and a new `LANE-RESULT.json` convention + `brainclaw harvest
374
+ <assignment_id>` give workers a standard, MCP-free result channel. The dispatch
375
+ dirty-guard also ignores `.claude/`, `.cursor/`, and `.codex/` agent-local
376
+ config. (pln#523, pln#524, pln#525, pln#526, trp#371, trp#427, trp#428)
377
+
378
+ ### v1.7.2
379
+
380
+ - **Sequence MCP tools are agent-first by default** — sequence creation,
381
+ listing, update, and deletion tools are now in the default MCP catalog, with
382
+ explicit lane item schemas (`planId`, optional `stepId`, `rank`,
383
+ dependencies, lane metadata) and matching canonical CRUD validation for
384
+ `entity="sequence"`.
385
+
386
+ ### v1.7.1
387
+
388
+ - **MCP project context isolation fix** — `bclaw_switch` now keeps MCP switches
389
+ session-scoped even when the agent session has to be resolved or created on
390
+ the fly. Session lookup honors explicit session IDs, avoids adopting another
391
+ live process's session, detects Codex via native `CODEX_*` runtime variables,
392
+ and `bclaw_switch(list=true)` reports the session active project with
393
+ `active_source`.
394
+
395
+ ### v1.7.0
396
+
397
+ - **Dispatch reliability + scope-aware dirty guard** — evidence-first
398
+ `agent_run` reconciliation avoids false terminal states, `bclaw_coordinate`
399
+ accepts pinned refs and a scope-aware `allow_dirty` guard, and the Hermes
400
+ agent integration joins the supported surfaces.
401
+
402
+ ### v1.6.0
403
+
404
+ - **Bootstrap loop + cross-project agent workflow** — the bootstrap ideation
405
+ preset can materialize `PROJECT.md`, `bclaw_init_project` initializes and links
406
+ arbitrary project paths, and `project=` routing reaches `bclaw_work` /
407
+ `bclaw_loop` for linked-project operations.
408
+
409
+ ### v1.5.3
410
+
411
+ - **Cross-project canonical grammar + CLI parity** (pln#359, all phases) — the canonical grammar (`bclaw_find / get / create / update / remove / transition`), `bclaw_context`, and `bclaw_coordinate` now accept an optional `project: <name>` argument that routes the operation to a linked project. Two link kinds are recognised: `cross_project_links` (sibling/peer projects in `config.yaml`, `brainclaw link list`) and workspace store-chain children. Arbitrary directory paths are rejected — adoption requires an explicit link, which gives the user a single point of control over what an agent can reach. Identity is sourced from the caller's home registry; entity writes + audit log entries land in the target. Unknown project names throw `validation_error` with a hint listing the configured links — no silent fallback. Cross-project `bclaw_coordinate` is **inbox-only**: claim/assignment/message all land in the target, the target agent picks the brief up async via its own `bclaw_work`, and auto-spawn from the source process is force-disabled because the spawn cwd / worktree are tied to the target's git repo (a warning surfaces in `FacadeResponse.warnings`). The CLI exposes the same as a global `--project <name>` flag, mutually exclusive with `--cwd`. Refs: helper `resolveProjectCwd` in `src/core/cross-project.ts`, MCP write/read handler dispatch in `src/commands/mcp.ts` and `src/commands/mcp-read-handlers.ts`, `--project` plumbing in `src/cli.ts` preAction, surface advertisement in `src/core/instruction-templates.ts`, plus tests in `tests/unit/cross-project.test.ts` (10 unit cases on the helper), `tests/unit/bclaw-coordinate.test.ts` (4 cross-project routing cases), and `tests/cli-cross-project.test.ts` (5 e2e cases). Closes the `--cwd` workaround pattern that had been the day-to-day shape of multi-project sessions.
382
412
  - **Site facts contract** (umbrella `pln_7fdfd70d` sprint 0) — new `scripts/emit-site-facts.mjs` emits `dist/facts.{js,json}` from `MCP_TOOL_NAMES` + `ENTITY_NAMES` so the brainclaw-site (and any consumer) can pull live tool/entity counts at build time without forking the values into a hand-maintained config. The package `files` list ships `dist/facts.json`; build:cli runs the emitter as part of the chain.
383
413
 
384
414
  ### v1.5.2
@@ -436,15 +466,15 @@ For older releases (v0.x and the early v1.0 launch series), `git log` on `master
436
466
 
437
467
  ---
438
468
 
439
- ## License
440
-
441
- brainclaw core is published under the [MIT License](LICENSE) — (c) 2024-2026 Juan Berdah.
442
-
443
- The licensing split is simple:
444
-
445
- - the local-first brainclaw core is MIT
446
- - cloud shared-memory, remote collaboration services, advanced dashboards, and related hosted add-ons will live in separate commercial products
447
-
448
- The MIT core covers what makes brainclaw useful inside a repo today: local project memory, local MCP and CLI coordination, onboarding and bootstrap, plans, claims, handoffs, runtime notes, and local agent integrations.
469
+ ## License
470
+
471
+ brainclaw core is published under the [MIT License](LICENSE) — (c) 2024-2026 Juan Berdah.
472
+
473
+ The licensing split is simple:
474
+
475
+ - the local-first brainclaw core is MIT
476
+ - cloud shared-memory, remote collaboration services, advanced dashboards, and related hosted add-ons will live in separate commercial products
477
+
478
+ The MIT core covers what makes brainclaw useful inside a repo today: local project memory, local MCP and CLI coordination, onboarding and bootstrap, plans, claims, handoffs, runtime notes, and local agent integrations.
449
479
 
450
480
  The goal is not to close brainclaw down. The goal is to keep the local-first core open and genuinely useful on its own, while keeping hosted collaboration features separate.
Binary file
package/dist/cli.js CHANGED
@@ -104,7 +104,7 @@ import { runDiscover } from './commands/discover.js';
104
104
  import { runMigrate } from './commands/migrate.js';
105
105
  import { runRunProfile } from './commands/run-profile.js';
106
106
  import { runCompact } from './commands/compact.js';
107
- import { runHarvestCandidates } from './commands/harvest.js';
107
+ import { runHarvestCandidates, runHarvestLane } from './commands/harvest.js';
108
108
  import { runQuestionsCommand } from './commands/questions.js';
109
109
  import { runReplyCommand } from './commands/reply.js';
110
110
  import { requireRegisteredAgentIdentity } from './core/agent-registry.js';
@@ -1009,6 +1009,18 @@ program
1009
1009
  const globalOpts = program.opts();
1010
1010
  runHarvestCandidates({ ...options, cwd: globalOpts.cwd });
1011
1011
  });
1012
+ // --- harvest (lane results, pln#526) ---
1013
+ program
1014
+ .command('harvest [assignment_id]')
1015
+ .description('Harvest a worker LANE-RESULT.json from its worktree into the project (pass an assignment id, or --all)')
1016
+ .option('--all', 'Harvest every lane result found across worktrees')
1017
+ .option('--dry-run', 'Preview without writing events/markers')
1018
+ .option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
1019
+ .option('--json', 'Output as JSON')
1020
+ .action((assignmentId, options) => {
1021
+ const globalOpts = program.opts();
1022
+ runHarvestLane(assignmentId, { ...options, cwd: globalOpts.cwd });
1023
+ });
1012
1024
  // --- prune-candidates ---
1013
1025
  program
1014
1026
  .command('prune-candidates')
@@ -14,7 +14,7 @@ import fs from 'node:fs';
14
14
  import os from 'node:os';
15
15
  import path from 'node:path';
16
16
  import crypto from 'node:crypto';
17
- import { CandidateSchema } from '../core/schema.js';
17
+ import { CandidateSchema, LaneResultSchema } from '../core/schema.js';
18
18
  import { listCandidates, listArchivedCandidates, saveCandidate } from '../core/candidates.js';
19
19
  import { createRuntimeEvent } from '../core/events.js';
20
20
  import { memoryExists } from '../core/io.js';
@@ -186,4 +186,127 @@ export function runHarvestCandidates(options = {}) {
186
186
  }
187
187
  console.log(`\n✔ Harvest complete${dryTag}: ${result.harvested.length} imported, ${result.skipped.length} skipped, ${result.errors.length} error(s).`);
188
188
  }
189
+ // ─────────────────────────────────────────────────────────────────────────────
190
+ // pln#526 — LANE-RESULT convention
191
+ //
192
+ // A dispatched worker writes a single `LANE-RESULT.json` at its worktree root
193
+ // as its final step. This is the standard, brief-boilerplate-free channel for a
194
+ // worker (especially a sandboxed one that cannot reach MCP) to report its
195
+ // outcome. The coordinator ingests it with `brainclaw harvest <assignment_id>`.
196
+ // ─────────────────────────────────────────────────────────────────────────────
197
+ /** Conventional path of a worker's lane-result file at the worktree root. */
198
+ export function getLaneResultPath(worktreePath) {
199
+ return path.join(worktreePath, 'LANE-RESULT.json');
200
+ }
201
+ /** Idempotency marker so a lane-result is harvested once. */
202
+ function laneHarvestedMarkerPath(cwd, assignmentId) {
203
+ return path.join(cwd, '.brainclaw', 'coordination', 'runtime', 'result', `${assignmentId}.harvested`);
204
+ }
205
+ /**
206
+ * Scan worktrees for `LANE-RESULT.json`, validate, and ingest each: emit a
207
+ * `lane_result_harvested` runtime event (durable + queryable) and drop an
208
+ * idempotency marker so re-runs skip it. The worker's actual code lives in the
209
+ * worktree/branch; this surfaces the structured outcome (status + summary) the
210
+ * coordinator needs to converge the lane.
211
+ */
212
+ export function harvestLaneResults(options = {}) {
213
+ const cwd = options.cwd ?? process.cwd();
214
+ const agent = options.agent ?? 'coordinator';
215
+ const result = { harvested: [], skipped: [], errors: [] };
216
+ const worktreePaths = (options.worktreePaths && options.worktreePaths.length > 0)
217
+ ? options.worktreePaths
218
+ : autoDetectWorktreePaths(cwd);
219
+ for (const worktreePath of worktreePaths) {
220
+ const file = getLaneResultPath(worktreePath);
221
+ if (!fs.existsSync(file))
222
+ continue;
223
+ let lane;
224
+ try {
225
+ lane = LaneResultSchema.parse(JSON.parse(fs.readFileSync(file, 'utf-8')));
226
+ }
227
+ catch (err) {
228
+ result.errors.push(`Failed to parse ${file}: ${err instanceof Error ? err.message : String(err)}`);
229
+ continue;
230
+ }
231
+ // Assignment filter (when harvesting a specific lane).
232
+ if (options.assignmentId && lane.assignment_id !== options.assignmentId)
233
+ continue;
234
+ const marker = laneHarvestedMarkerPath(cwd, lane.assignment_id);
235
+ if (fs.existsSync(marker)) {
236
+ result.skipped.push(lane.assignment_id);
237
+ continue;
238
+ }
239
+ if (!options.dryRun) {
240
+ try {
241
+ createRuntimeEvent({
242
+ agent,
243
+ event_type: 'lane_result_harvested',
244
+ text: `Lane result for ${lane.assignment_id}: ${lane.status} — ${lane.summary.slice(0, 120)}`,
245
+ tags: ['harvest', 'lane-result', lane.status],
246
+ assignment_id: lane.assignment_id,
247
+ metadata: {
248
+ assignment_id: lane.assignment_id,
249
+ status: lane.status,
250
+ artifacts: lane.artifacts ?? [],
251
+ files_changed: lane.files_changed ?? [],
252
+ source_worktree: worktreePath,
253
+ },
254
+ }, cwd);
255
+ fs.mkdirSync(path.dirname(marker), { recursive: true });
256
+ fs.writeFileSync(marker, new Date(0).toISOString(), 'utf-8');
257
+ }
258
+ catch (err) {
259
+ result.errors.push(`Failed to ingest lane result for ${lane.assignment_id}: ${err instanceof Error ? err.message : String(err)}`);
260
+ continue;
261
+ }
262
+ }
263
+ result.harvested.push(lane);
264
+ }
265
+ return result;
266
+ }
267
+ export function runHarvestLane(assignmentId, options = {}) {
268
+ const cwd = options.cwd ?? process.cwd();
269
+ if (!memoryExists(cwd)) {
270
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
271
+ process.exit(1);
272
+ }
273
+ if (!assignmentId && !options.all) {
274
+ console.error('Error: provide an <assignment_id>, or pass --all to harvest every lane result.');
275
+ process.exit(1);
276
+ }
277
+ const result = harvestLaneResults({
278
+ assignmentId: options.all ? undefined : assignmentId,
279
+ worktreePaths: options.worktree,
280
+ dryRun: options.dryRun,
281
+ cwd,
282
+ });
283
+ if (options.json) {
284
+ console.log(JSON.stringify({
285
+ harvested: result.harvested,
286
+ skipped: result.skipped,
287
+ errors: result.errors,
288
+ }, null, 2));
289
+ return;
290
+ }
291
+ const dryTag = options.dryRun ? ' (dry-run)' : '';
292
+ if (result.harvested.length === 0 && result.skipped.length === 0 && result.errors.length === 0) {
293
+ console.log(assignmentId ? `No LANE-RESULT.json found for ${assignmentId}.` : 'No lane results found in any worktree.');
294
+ return;
295
+ }
296
+ for (const lane of result.harvested) {
297
+ const verb = options.dryRun ? ' (dry-run) Would harvest' : ' ✔ Harvested';
298
+ console.log(`${verb} [${lane.assignment_id}] ${lane.status}: ${lane.summary.slice(0, 100)}`);
299
+ if (lane.files_changed?.length)
300
+ console.log(` files: ${lane.files_changed.slice(0, 8).join(', ')}`);
301
+ if (lane.notes)
302
+ console.log(` notes: ${lane.notes.slice(0, 120)}`);
303
+ }
304
+ for (const id of result.skipped) {
305
+ console.log(` ⟳ Skipped (already harvested): ${id}`);
306
+ }
307
+ for (const err of result.errors) {
308
+ console.error(` ✗ ${err}`);
309
+ }
310
+ console.log(`\n✔ Lane harvest complete${dryTag}: ${result.harvested.length} harvested, ${result.skipped.length} skipped, ${result.errors.length} error(s).`);
311
+ }
189
312
  //# sourceMappingURL=harvest.js.map
@@ -16,7 +16,7 @@ import { collectLoadValidationWarnings, findLoadValidationWarning, loadState, pe
16
16
  import { generateIdWithLabel } from '../core/ids.js';
17
17
  import { memoryExists } from '../core/io.js';
18
18
  import { generateCandidateIdWithLabel, loadCandidate, saveCandidate } from '../core/candidates.js';
19
- import { createEntity, getEntity, listEntities, removeEntity, transitionEntity, updateEntity, } from '../core/entity-operations.js';
19
+ import { createEntity, getEntity, listEntities, boundListResult, removeEntity, transitionEntity, updateEntity, } from '../core/entity-operations.js';
20
20
  import { ENTITY_REGISTRY } from '../core/entity-registry.js';
21
21
  import { generateClaimId, listClaims, loadClaim, saveClaim, createCoordinatorClaim, adoptClaimSession, attachAssignmentMessageToClaim, linkClaimToAssignment, releaseClaimWithCascade } from '../core/claims.js';
22
22
  import { createSequence, updateSequence, deleteSequence } from '../core/sequence.js';
@@ -575,7 +575,7 @@ const MCP_WRITE_TOOLS = [
575
575
  },
576
576
  {
577
577
  name: 'bclaw_claim',
578
- description: 'Claim a work scope (advisory lock). Automatically creates an isolated git worktree for this claim. Requires contributor trust level or above.',
578
+ description: 'Claim a work scope (advisory lock). By default creates an isolated git worktree for the claim (multi-agent safety). Pass advisory:true (or worktree:false) for an advisory-only lock with NO worktree — use this when the work already lives uncommitted in the main tree and a fresh worktree would be counterproductive (trp#431). Requires contributor trust level or above.',
579
579
  annotations: { tier: 'standard', category: 'coordination', headlessApproval: 'auto' },
580
580
  inputSchema: {
581
581
  type: 'object',
@@ -588,6 +588,8 @@ const MCP_WRITE_TOOLS = [
588
588
  project: { type: 'string', description: 'Project name or path. Use this when working on a project different from the MCP server workspace (e.g. CLI agents in a different directory).' },
589
589
  store: { type: 'string', description: 'Target store level: local (default), repo, workspace.' },
590
590
  worktreeBranch: { type: 'string', description: 'Branch name for the worktree. Defaults to feat/<scope-slug>.' },
591
+ worktree: { type: 'boolean', description: 'Whether to create an isolated git worktree (default true). Pass false for an advisory-only lock with no worktree (trp#431) — for in-place work in the main tree.' },
592
+ advisory: { type: 'boolean', description: 'Alias for worktree:false — advisory-only lock with no worktree (trp#431).' },
591
593
  handoffMode: { type: 'string', enum: ['self-commit', 'integrator'], description: 'Handoff mode: "self-commit" (worker commits+merges) or "integrator" (another agent reviews+merges). Default: self-commit.' },
592
594
  },
593
595
  required: ['scope', 'description'],
@@ -956,7 +958,7 @@ const MCP_WRITE_TOOLS = [
956
958
  },
957
959
  {
958
960
  name: 'bclaw_coordinate',
959
- description: 'Multi-agent coordination facade: assign tasks to agents (with claims), consult agents (no claim), create a review candidate, open an ideation loop, reroute an active claim to another agent, or summarize a thread. Returns a FacadeResponse with selected_targets, delivery_plan, artifacts, side_effects, and execution_status. IMPORTANT — execution_status semantics: `delivered_and_started` means the spawn wrapper touched the brief-ack sentinel (`.brainclaw/coordination/runtime/ack/<assignment_id>.ack`) — NOT that the worker is doing useful work. Spawned workers may still die silently before consuming the brief (cf. trap trp_38f63ea4). To verify a dispatch is actually alive, query `bclaw_find(entity="agent_run", filter={assignment_id})` then check `status==="running"` AND OS-level pid liveness (Windows: `Get-Process -Id <pid>` ; POSIX: `kill -0 <pid>`) AND `last_event_at` within the last few minutes. See docs/concepts/troubleshooting.md §"Inbox messages stuck / brief-ack never arrived" for the diagnostic playbook, and docs/integrations/<agent>.md for per-agent spawn semantics (notably codex.md re sandbox MCP availability).',
961
+ description: 'Multi-agent coordination facade: assign tasks to agents (with claims), consult agents (no claim), create a review candidate, open an ideation loop, reroute an active claim to another agent, or summarize a thread. Returns a FacadeResponse with selected_targets, delivery_plan, artifacts, side_effects, and execution_status. IMPORTANT — execution_status semantics: `delivered_and_started` means the spawn wrapper touched the brief-ack sentinel (`.brainclaw/coordination/runtime/ack/<assignment_id>.ack`) — NOT that the worker is doing useful work. Spawned workers may still die silently before consuming the brief (cf. trap trp_38f63ea4). To verify a dispatch is actually alive, call `bclaw_dispatch_status(target_id=<asgn_…>)` it reads the runtime sentinels (ack/heartbeat/completed/failed) plus captured stdout/stderr tails and returns a single health verdict + recommended next action (this is the `verify_with` target attached to the response). Do NOT diagnose liveness from the tracked pid: on Windows an ack-wrapped spawn runs under cmd.exe, so `agent_run.pid` is the wrapper (which exits early by design), NOT the real worker — `Get-Process -Id <pid>` reads it dead while the worker is alive and committing. The reconciler trusts the sentinels and infers `completed` from a post-start commit even when the worker never called bclaw_assignment_update. See docs/concepts/dispatch-lifecycle.md for the full FSM + diagnostic decision tree, and docs/integrations/<agent>.md for per-agent spawn semantics (notably codex.md re sandbox MCP availability).',
960
962
  annotations: { tier: 'facade', category: 'coordination', headlessApproval: 'auto' },
961
963
  inputSchema: {
962
964
  type: 'object',
@@ -1033,7 +1035,7 @@ const MCP_WRITE_TOOLS = [
1033
1035
  },
1034
1036
  {
1035
1037
  name: 'bclaw_assignment_update',
1036
- description: 'Report assignment lifecycle status. Part of the Agent SDK runtime protocol. Workers call this to report: accepted (acknowledging receipt), started (work begun), progress (heartbeat), completed (done with artifacts), failed (error), or blocked (external blocker). The assignment_id is provided in the dispatch brief.',
1038
+ description: 'Report assignment lifecycle status. Part of the Agent SDK runtime protocol. Workers call this to report: accepted (acknowledging receipt), started (work begun), progress (heartbeat), completed (done with artifacts), failed (error), or blocked (external blocker). The assignment_id is provided in the dispatch brief. OWNERSHIP (trp#291): only the agent the assignment is OWNED BY (the dispatched worker) may update it — a different agent (e.g. the coordinator) gets `Agent <x> cannot update assignment owned by <y>`. If you are the coordinator and need to converge a worker run, do NOT call this; verify via bclaw_dispatch_status instead (the reconciler infers completion from sentinels/commits).',
1037
1039
  annotations: { tier: 'standard', category: 'coordination', headlessApproval: 'auto' },
1038
1040
  inputSchema: {
1039
1041
  type: 'object',
@@ -1111,7 +1113,7 @@ const MCP_WRITE_TOOLS = [
1111
1113
  // Promoted to `standard` tier at the v1.0 cut.
1112
1114
  {
1113
1115
  name: 'bclaw_find',
1114
- description: 'Canonical list query over a brainclaw entity. Default read filter excludes records with provenance.kind="legacy" and auto_reflect records below 0.6 confidence — override via filter.includeLegacy / filter.minAutoReflectConfidence. Tag filters accept `tag: string` for one tag or `tags: string[]` for any-match. For entity="agent_run", filters also accept assignment_id, claim_id, and message_id. Pass `project` to query a linked project instead of the current one.',
1116
+ description: 'Canonical list query over a brainclaw entity. Default read filter excludes records with provenance.kind="legacy" and auto_reflect records below 0.6 confidence — override via filter.includeLegacy / filter.minAutoReflectConfidence. Tag filters accept `tag: string` for one tag or `tags: string[]` for any-match. For entity="agent_run", filters also accept assignment_id, claim_id, and message_id. Pass `project` to query a linked project instead of the current one. PAGINATION & SIZE (pln#491): returns at most filter.limit items (default 50), and the page is additionally shrunk if it would exceed the MCP size budget. The response carries `total` (full match count), `returned`, and — when more remain — `has_more: true`, `next_offset`, and a `hint`; pass `filter.offset=<next_offset>` (or a narrower filter) to page rather than expecting everything at once. ORDERING: results follow on-disk/load order, NOT recency — do not assume the first item is the newest (trp#291); filter explicitly (e.g. status, plan_id) to target what you need.',
1115
1117
  annotations: { tier: 'standard', category: 'memory', headlessApproval: 'auto' },
1116
1118
  inputSchema: {
1117
1119
  type: 'object',
@@ -2906,9 +2908,13 @@ async function _executeMcpToolCallInner(payload) {
2906
2908
  const claimId = generateClaimId();
2907
2909
  let worktreePath;
2908
2910
  let worktreeWarn = '';
2909
- // Always create worktree in MCP context for multi-agent isolation.
2910
- // The createWorktree param is no longer exposed in the schema.
2911
- {
2911
+ // trp#431: advisory mode skips worktree creation. Default is to create an
2912
+ // isolated worktree (multi-agent safety), but when the work already lives
2913
+ // (uncommitted) in the main tree a fresh worktree is counterproductive and
2914
+ // the agent ends up skipping the claim. Pass advisory:true (or
2915
+ // worktree:false) for an advisory-only lock with no worktree.
2916
+ const advisoryClaim = args.advisory === true || args.worktree === false;
2917
+ if (!advisoryClaim) {
2912
2918
  const branchSlug = claimScope.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/-+/g, '-').slice(0, 48);
2913
2919
  const worktreeBranch = args.worktreeBranch?.trim() || `feat/${branchSlug}`;
2914
2920
  try {
@@ -5788,6 +5794,12 @@ async function _executeMcpToolCallInner(payload) {
5788
5794
  };
5789
5795
  }
5790
5796
  const result = listEntities(entity, targetCwd, filter);
5797
+ // pln#491 — bound the payload (count is already capped by applyPaging;
5798
+ // this caps SIZE) so a verbose result set never overflows the MCP token
5799
+ // cap and silently pushes the agent to the CLI (trp#449). Advertises
5800
+ // has_more / next_offset / hint for explicit pagination.
5801
+ const offset = Math.max(0, Number(filter.offset) || 0);
5802
+ const bounded = boundListResult(result, offset);
5791
5803
  const warnings = collectLoadValidationWarnings(entity, targetCwd);
5792
5804
  // structuredContent is the canonical MCP return channel that clients
5793
5805
  // (VS Code extension, Codex, etc.) read for machine-parseable data.
@@ -5795,10 +5807,11 @@ async function _executeMcpToolCallInner(payload) {
5795
5807
  // response body, which got dropped by the MCP protocol wrapper so
5796
5808
  // `result.items` arrived as undefined on the client — the root cause
5797
5809
  // of the VS Code Backlog section rendering empty.
5810
+ const moreNote = bounded.has_more ? ` (returned ${bounded.returned}; ${result.total - bounded.returned} more — offset ${bounded.next_offset})` : '';
5798
5811
  return {
5799
5812
  response: toolResponse({
5800
- content: [{ type: 'text', text: `✔ ${result.total} ${entity} item(s)` }],
5801
- structuredContent: { ...result, warnings },
5813
+ content: [{ type: 'text', text: `✔ ${result.total} ${entity} item(s)${moreNote}` }],
5814
+ structuredContent: { ...bounded, warnings },
5802
5815
  }),
5803
5816
  };
5804
5817
  }
@@ -664,6 +664,34 @@ export function resolveBriefMode(agentName) {
664
664
  return 'compact';
665
665
  return 'full';
666
666
  }
667
+ // ── Dispatch-time capability matrix (pln#528) ──────────────────────────────
668
+ /**
669
+ * pln#528 — capability matrix DERIVED from the spawn template, so it stays in
670
+ * sync with how each agent is actually invoked (no per-profile duplication).
671
+ *
672
+ * The motivating reality (debrief LeaseUp): codex is spawned with
673
+ * `--sandbox workspace-write`, which (a) does NOT wire the brainclaw MCP server
674
+ * and (b) puts `.git` outside the sandbox root — so a sandboxed worker can
675
+ * neither call `bclaw_*` nor `git commit`, regardless of the profile's nominal
676
+ * `runtime.mcp_direct` flag. These helpers expose that so the brief / handoff /
677
+ * harvest logic can adapt to the transport instead of issuing instructions the
678
+ * worker cannot follow.
679
+ */
680
+ export function isSandboxedSpawn(profile) {
681
+ return /--sandbox\b/.test(profile.invoke_template ?? '');
682
+ }
683
+ /** Whether the agent, AS SPAWNED by the dispatcher, can reach brainclaw MCP. */
684
+ export function dispatchHasMcp(profile) {
685
+ return profile.runtime.mcp_direct && !isSandboxedSpawn(profile);
686
+ }
687
+ /**
688
+ * Whether the spawned worker can `git commit`. A sandbox whose root excludes
689
+ * `.git` cannot — the coordinator must integrate the worker's output instead of
690
+ * relying on a self-commit handoff.
691
+ */
692
+ export function dispatchCanCommit(profile) {
693
+ return !isSandboxedSpawn(profile);
694
+ }
667
695
  // ── getDefaultInvokeTemplate ───────────────────────────────────────────────
668
696
  /**
669
697
  * Get the default invoke template for an agent.