brainclaw 1.10.2 → 1.11.1

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.
@@ -130,16 +130,44 @@ export function resolveEffectiveCwdInfo(options = {}) {
130
130
  if (resolved)
131
131
  return { cwd: resolved, active_source: 'env_project', resolved_project: projectInfo(resolved) };
132
132
  }
133
- // 4. Session-scoped active project (per-agent, no cross-agent interference)
134
- const session = options.sessionId
135
- ? loadSessionById(options.sessionId, anchorCwd)
136
- : loadCurrentSession(anchorCwd);
137
- if (session?.active_project) {
138
- const sp = session.active_project;
139
- if (fs.existsSync(path.join(sp.path, MEMORY_DIR, 'config.yaml'))) {
133
+ // 4. Session-scoped active project (per-agent, no cross-agent interference).
134
+ // A session can be persisted under a DIFFERENT store than the anchor we read
135
+ // from: an agent physically inside a child project gets its session created
136
+ // AND switched under that child (cwd_child / the switch handler use the
137
+ // physical cwd), while resolution here anchors at the workspace
138
+ // (BRAINCLAW_CWD). Sessions are stored per-cwd (sessionsDir(cwd)) with no
139
+ // chain search, so reading only the anchor makes a successful bclaw_switch
140
+ // invisible — resolution then silently falls through to cwd_child and pins
141
+ // the agent to the wrong project (DGX Finding 1, 2026-06-22). Probe the
142
+ // anchor, the physical baseCwd, and the workspace root for each; the first
143
+ // session carrying a still-valid active_project wins (anchor first preserves
144
+ // prior precedence when the session lives where we expect).
145
+ const probedSessionCwds = new Set();
146
+ const probeSessionAt = (candidate) => {
147
+ if (!candidate)
148
+ return undefined;
149
+ const probeCwd = path.resolve(candidate);
150
+ if (probedSessionCwds.has(probeCwd))
151
+ return undefined; // dedup — no double read
152
+ probedSessionCwds.add(probeCwd);
153
+ const session = options.sessionId
154
+ ? loadSessionById(options.sessionId, probeCwd)
155
+ : loadCurrentSession(probeCwd);
156
+ const sp = session?.active_project;
157
+ if (sp && fs.existsSync(path.join(sp.path, MEMORY_DIR, 'config.yaml'))) {
140
158
  return { cwd: sp.path, active_source: 'session', resolved_project: { path: sp.path, name: sp.name } };
141
159
  }
142
- }
160
+ return undefined;
161
+ };
162
+ // `??` short-circuits: the common case (agent at the anchor, session there)
163
+ // costs exactly one session load and never walks for the workspace root. The
164
+ // baseCwd / workspace-root probes only run when the cheap ones miss — i.e. the
165
+ // monorepo case where the session was stored under the physical child.
166
+ const sessionHit = probeSessionAt(anchorCwd)
167
+ ?? probeSessionAt(baseCwd)
168
+ ?? probeSessionAt(resolveWorkspaceRoot(baseCwd, options.storeChainOptions));
169
+ if (sessionHit)
170
+ return sessionHit;
143
171
  // 5. cwd_child — when anchored and the agent is physically inside a child store
144
172
  // STRICTLY under the anchor, resolve THAT child rather than the shared global
145
173
  // pointer or the anchor root. This is the independence rule: physical location
@@ -243,6 +271,22 @@ export function resolveProjectRef(ref, cwd = process.cwd(), storeChainOptions) {
243
271
  if (fs.existsSync(path.join(asPath, MEMORY_DIR, 'config.yaml'))) {
244
272
  return asPath;
245
273
  }
274
+ // The workspace root itself is a legal target — it is the "umbrella" project
275
+ // of a monorepo. The chain scan below deliberately skips wsRoot (so a child
276
+ // can never be shadowed by the root), but an agent must still be able to
277
+ // target the root by its own project_name/project_id. Without this, an agent
278
+ // working inside a child cannot switch UP to the monorepo root: bclaw_switch
279
+ // (and project="<root-name>" routing) failed with "Cannot resolve project"
280
+ // (DGX dogfood 2026-06-22, Finding 1). Matching by name/id — not by arbitrary
281
+ // path — preserves the path-injection trust boundary enforced above.
282
+ try {
283
+ const rootConfig = loadConfig(wsRoot);
284
+ if (rootConfig.project_name === ref || rootConfig.project_id === ref)
285
+ return wsRoot;
286
+ }
287
+ catch {
288
+ // unreadable workspace-root config — fall through to the child scan
289
+ }
246
290
  // Try by project name or project ID: scan child stores
247
291
  const chain = resolveStoreChain(wsRoot, storeChainOptions);
248
292
  for (const store of chain) {
@@ -985,6 +985,92 @@ export function cleanMergedWorktrees(mainWorktreePath, options = {}) {
985
985
  cleanOrphanWorktreeDirs(mainWorktreePath, worktrees, result, options.dryRun);
986
986
  return result;
987
987
  }
988
+ /** A worker whose heartbeat file was touched within this window looks alive. */
989
+ const WORKTREE_GC_LIVENESS_WINDOW_MS = 120_000;
990
+ /**
991
+ * A worker still looks alive when a `.brainclaw-heartbeat-*` sentinel in its
992
+ * worktree was modified within `windowMs`. Cheap liveness signal that needs no
993
+ * agent_run lookup — the spawn wrapper touches the heartbeat periodically.
994
+ */
995
+ function workerLooksAlive(worktreePath, windowMs) {
996
+ try {
997
+ const now = Date.now();
998
+ for (const name of fs.readdirSync(worktreePath)) {
999
+ if (!name.startsWith('.brainclaw-heartbeat-'))
1000
+ continue;
1001
+ try {
1002
+ if (now - fs.statSync(path.join(worktreePath, name)).mtimeMs < windowMs)
1003
+ return true;
1004
+ }
1005
+ catch { /* ignore unreadable sentinel */ }
1006
+ }
1007
+ }
1008
+ catch { /* worktree dir unreadable — treat as not-alive */ }
1009
+ return false;
1010
+ }
1011
+ /**
1012
+ * Garbage-collect a single dispatched sub-agent worktree once its work is safely
1013
+ * harvested (pln#594). Used by the loop-close cascade so review/dispatch
1014
+ * worktrees stop accumulating under ~/.brainclaw/worktrees/.
1015
+ *
1016
+ * SAFE BY DEFAULT — returns { removed: false, reason } instead of removing when:
1017
+ * - a worker still looks alive (recent heartbeat) — never bypassed, even by force;
1018
+ * - the worktree has un-harvested edits (anything beyond brainclaw birth-noise /
1019
+ * LANE-RESULT.json / heartbeat — i.e. real uncommitted work);
1020
+ * - the lane branch has commits NOT reachable from the main repo HEAD
1021
+ * (un-integrated work that `branch -D` would drop).
1022
+ * `force` bypasses the dirty + unmerged guards (NOT the liveness guard).
1023
+ * Removal is junction-safe (delegates to removeWorktree → detachWorktreeJunctions).
1024
+ */
1025
+ export function gcWorktreeIfHarvested(mainWorktreePath, worktreePath, options = {}) {
1026
+ const out = (removed, reason, branch) => ({
1027
+ path: worktreePath, branch, removed, reason,
1028
+ });
1029
+ if (!worktreePath || !fs.existsSync(worktreePath))
1030
+ return out(false, 'already gone');
1031
+ if (workerLooksAlive(worktreePath, options.livenessWindowMs ?? WORKTREE_GC_LIVENESS_WINDOW_MS)) {
1032
+ return out(false, 'worker still active (recent heartbeat)');
1033
+ }
1034
+ const branchRes = runGit(['rev-parse', '--abbrev-ref', 'HEAD'], worktreePath);
1035
+ const branch = branchRes.ok ? branchRes.stdout.trim() : undefined;
1036
+ if (!options.force) {
1037
+ // FAIL CLOSED (codex review): every safety probe that cannot be read must
1038
+ // KEEP the worktree, never fall through to removal. A transient `git status`
1039
+ // timeout on a real, dirty worktree previously skipped the dirty check and
1040
+ // force-removed it — losing un-harvested edits. Same for the HEAD reads.
1041
+ const status = runGit(['status', '--porcelain=v1', '-z', '--untracked-files=normal'], worktreePath);
1042
+ if (!status.ok) {
1043
+ return out(false, 'could not read worktree status — keeping (fail-closed)', branch);
1044
+ }
1045
+ if (!worktreeHasOnlyBirthNoise(status.stdout)) {
1046
+ return out(false, 'un-harvested changes in worktree', branch);
1047
+ }
1048
+ // The lane HEAD must be reachable from the main repo HEAD, else the branch
1049
+ // carries un-integrated commits that `branch -D` would silently drop.
1050
+ const laneHead = runGit(['rev-parse', 'HEAD'], worktreePath);
1051
+ const mainHead = runGit(['rev-parse', 'HEAD'], mainWorktreePath);
1052
+ if (!laneHead.ok || !mainHead.ok) {
1053
+ return out(false, 'could not verify merge status — keeping (fail-closed)', branch);
1054
+ }
1055
+ const ancestor = runGit(['merge-base', '--is-ancestor', laneHead.stdout.trim(), mainHead.stdout.trim()], mainWorktreePath);
1056
+ // exit 0 = ancestor (safe). Non-zero = not an ancestor OR a git error — both
1057
+ // mean "cannot prove integrated", so keep.
1058
+ if (!ancestor.ok)
1059
+ return out(false, 'lane branch has un-integrated commits (or unverifiable)', branch);
1060
+ }
1061
+ try {
1062
+ removeWorktree(mainWorktreePath, worktreePath, { force: true });
1063
+ }
1064
+ catch (err) {
1065
+ return out(false, `removal failed: ${err.message}`, branch);
1066
+ }
1067
+ // Delete the now-redundant dispatch branch (force: it may be a squash-merge
1068
+ // descendant that `-d` would refuse). Best-effort — a kept branch is harmless.
1069
+ if (branch && branch !== 'HEAD' && branch !== '(detached)') {
1070
+ runGit(['branch', '-D', branch], mainWorktreePath);
1071
+ }
1072
+ return out(true, options.force ? 'force-removed' : 'harvested + merged', branch);
1073
+ }
988
1074
  /**
989
1075
  * Removes brainclaw-managed worktree directories under ~/.brainclaw/worktrees/
990
1076
  * that no longer have a corresponding git worktree entry.
package/dist/facts.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // Generated by scripts/emit-site-facts.mjs at build time. Do not edit manually.
2
- // Source: brainclaw v1.10.2 on 2026-06-21T23:44:46.631Z
2
+ // Source: brainclaw v1.11.1 on 2026-06-24T13:31:04.040Z
3
3
  export const FACTS = {
4
- "version": "1.10.2",
5
- "generated_at": "2026-06-21T23:44:46.631Z",
4
+ "version": "1.11.1",
5
+ "generated_at": "2026-06-24T13:31:04.040Z",
6
6
  "tools": {
7
- "count": 66,
8
- "published_count": 65,
7
+ "count": 67,
8
+ "published_count": 66,
9
9
  "names": [
10
10
  "bclaw_bootstrap",
11
11
  "bclaw_release_notes",
@@ -72,7 +72,8 @@ export const FACTS = {
72
72
  "bclaw_create",
73
73
  "bclaw_update",
74
74
  "bclaw_remove",
75
- "bclaw_transition"
75
+ "bclaw_transition",
76
+ "bclaw_move"
76
77
  ]
77
78
  },
78
79
  "entities": {
package/dist/facts.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "1.10.2",
3
- "generated_at": "2026-06-21T23:44:46.631Z",
2
+ "version": "1.11.1",
3
+ "generated_at": "2026-06-24T13:31:04.040Z",
4
4
  "tools": {
5
- "count": 66,
6
- "published_count": 65,
5
+ "count": 67,
6
+ "published_count": 66,
7
7
  "names": [
8
8
  "bclaw_bootstrap",
9
9
  "bclaw_release_notes",
@@ -70,7 +70,8 @@
70
70
  "bclaw_create",
71
71
  "bclaw_update",
72
72
  "bclaw_remove",
73
- "bclaw_transition"
73
+ "bclaw_transition",
74
+ "bclaw_move"
74
75
  ]
75
76
  },
76
77
  "entities": {
package/docs/cli.md CHANGED
@@ -65,6 +65,24 @@ brainclaw --cwd /other/path status # one-off override without switching
65
65
 
66
66
  **MCP usage:** The active project also affects MCP tools. When `bclaw_context(kind="memory")` is called without an explicit path, it resolves context from the active project's store. Agents can also use the `BRAINCLAW_PROJECT=<name>` environment variable for the same effect.
67
67
 
68
+ ### `brainclaw move <entity> <id> --to <project>`
69
+
70
+ Relocate a brainclaw item to another project in a multi-project workspace, **preserving its id** — so `pln#`/`dec#` references stay stable. Useful when an item was created in the wrong store (e.g. before a project switch took effect). MCP equivalent: `bclaw_move(entity, id, to_project, …)`.
71
+
72
+ | Option | Description |
73
+ |---|---|
74
+ | `--to <project>` | Target project (name, path, or basename) — required |
75
+ | `--from <project>` | Source project (defaults to the current project) |
76
+ | `--force` | Move even if an active claim references the item |
77
+ | `--json` | Output as JSON |
78
+
79
+ Relocatable entities: `plan`, `decision`, `constraint`, `trap`, `handoff`, `sequence`. **Execution-local entities** (`claim`, `assignment`, `agent_run`, `session`) are rejected — they belong to the project where the work ran. The move refuses an id collision in the target or a plan under an active claim (unless `--force`), audits both stores, and warns about sequences that still reference a moved plan.
80
+
81
+ ```bash
82
+ brainclaw move plan pln_a1b2c3d4 --to global # move a misplaced plan to the root project
83
+ brainclaw move decision dec_99 --from app_a --to app_b
84
+ ```
85
+
68
86
  ---
69
87
 
70
88
  ## Initialize and Inspect
@@ -246,14 +264,18 @@ Run health checks on config, state, and generated views.
246
264
  | `--json` | Output as JSON |
247
265
  | `--migration-check` | Check for pending migrations |
248
266
  | `--fix-agent-ignore` | Add missing `.gitignore` entries for generated local Brainclaw agent files |
267
+ | `--fix-hooks` | Purge stale/broken/duplicate brainclaw session hooks across all Claude Code settings scopes (user + cwd) and rewrite the canonical ones |
249
268
 
250
269
  ```bash
251
270
  brainclaw doctor
252
271
  brainclaw doctor --json
253
272
  brainclaw doctor --migration-check
254
273
  brainclaw doctor --fix-agent-ignore
274
+ brainclaw doctor --fix-hooks
255
275
  ```
256
276
 
277
+ `--fix-hooks` collapses every recognized brainclaw session hook (across `UserPromptSubmit` / `Stop` / `PostToolUse`) in `~/.claude/settings.json`, `~/.claude/settings.local.json`, `<cwd>/.claude/settings.json`, and `<cwd>/.claude/settings.local.json` down to a single canonical entry, repairing legacy/broken forms. It only touches files (and events) that already contain a brainclaw hook, so user-authored hooks and hook-less scopes are left untouched. Use it when you see repeated `UserPromptSubmit hook error` warnings.
278
+
257
279
  When Brainclaw detects generated local agent files such as `.mcp.json` or `.claude/settings.local.json` inside a Git repo, `doctor` warns if they are not ignored or are still tracked. `--fix-agent-ignore` only updates `.gitignore`; if a file is already tracked you still need to untrack it with `git rm --cached <path>`.
258
280
 
259
281
  In `multi-project` mode with `projects.strategy: folder`, `doctor` now checks the effective workspace project set, not just `config.projects.known`. That avoids false positives on workspaces that resolve child stores from the filesystem or global project registry.
@@ -613,13 +635,13 @@ can ask "where is X / what should I read first" before editing. The MCP equivale
613
635
  are `bclaw_code_status` / `bclaw_code_find` / `bclaw_code_brief` / `bclaw_code_refresh`.
614
636
  Full reference (freshness model, supported languages, WASM bundling): [docs/code-map.md](code-map.md).
615
637
 
616
- ### `brainclaw code-map status`
638
+ ### `brainclaw code-map status [--cascade]`
617
639
 
618
- Store presence, freshness badge (`fresh` / `stale_changed_files` / `stale_extractor` / `stale_grammar` / `partial` / `missing_index`), and index stats (files, nodes, edges). Read-only.
640
+ Store presence, freshness badge (`fresh` / `stale_changed_files` / `stale_extractor` / `stale_grammar` / `partial` / `missing_index`), and index stats (files, nodes, edges). Read-only. In a multi-project workspace, `--cascade` adds a per-child recap (which nested projects have a built index vs `missing_index`, plus an aggregate count).
619
641
 
620
- ### `brainclaw code-map refresh [--all|--changed]`
642
+ ### `brainclaw code-map refresh [--all|--changed] [--cascade]`
621
643
 
622
- Build or update the index. `--changed` (default) re-parses only touched files; `--all` does a full re-index. Run this when status shows `missing_index` or a stale badge. Fails fast (never blocks) if another writer holds the project lock.
644
+ Build or update the index. `--changed` (default) re-parses only touched files; `--all` does a full re-index. Run this when status shows `missing_index` or a stale badge. Fails fast (never blocks) if another writer holds the project lock. In a multi-project workspace, `--cascade` refreshes **every nested project** into its own store plus a root store scoped to the files no child owns (zero double-indexing) — one command at the root indexes the whole monorepo per-project. See [docs/code-map.md](code-map.md#cascading-a-multi-project-workspace---cascade).
623
645
 
624
646
  ### `brainclaw code-map find <query> [--limit <n>]`
625
647
 
package/docs/code-map.md CHANGED
@@ -165,11 +165,12 @@ Code Map is **per project**: the index lives at `<project>/.brainclaw/code/`, an
165
165
  into subdirectories but skipping `node_modules`, `dist`, `.git`, `.brainclaw`,
166
166
  `vendor`, `target`, … at any depth.
167
167
 
168
- There is no nested-project *boundary*, so the scope follows **where you run it**:
168
+ By default there is no nested-project *boundary*, so a plain (non-cascade) scope
169
+ follows **where you run it**:
169
170
 
170
171
  | You run refresh / find / brief … | … against |
171
172
  |---|---|
172
- | at the monorepo root | one index covering the whole tree (every child project's source) |
173
+ | at the monorepo root (plain) | one index covering the whole tree (every child project's source) |
173
174
  | inside a child project (e.g. `apps/api`) | that child's own index, at `apps/api/.brainclaw/code/` |
174
175
 
175
176
  When an agent works inside a child project, brainclaw's project resolution routes
@@ -178,10 +179,33 @@ Code Map to **that child** — the same per-project scoping that powers `bclaw_w
178
179
  juggling. A submodule that is itself an application (under e.g. `apps/`) is indexed
179
180
  like any other directory.
180
181
 
182
+ ### Cascading a multi-project workspace (`--cascade`)
183
+
184
+ In a `project_mode: multi-project` workspace, one refresh at the root can index
185
+ the whole monorepo **per project** instead of building one monolithic root index:
186
+
187
+ ```bash
188
+ brainclaw code-map refresh --all --cascade # CLI
189
+ # bclaw_code_refresh(scope="all", cascade=true) # MCP
190
+ ```
191
+
192
+ This refreshes **every nested brainclaw project** into its own
193
+ `<child>/.brainclaw/code/` store, and refreshes the **root** store *scoped to the
194
+ files no child owns*. The rule is "each file is indexed by exactly the most
195
+ specific brainclaw project that contains it" — so there is **zero
196
+ double-indexing**, even when projects nest inside one another. `--cascade` is
197
+ opt-in; without it, the root refresh keeps its single-tree behaviour (above), and
198
+ single-project repos ignore the flag entirely.
199
+
200
+ `status --cascade` (or `bclaw_code_status(cascade=true)`) adds a per-child recap —
201
+ which nested projects have a built index vs `missing_index`, plus an aggregate
202
+ count — so you can see workspace-wide freshness from the root.
203
+
181
204
  **Not yet supported** (roadmap):
182
205
 
183
- - A single aggregated view that keeps **separate per-child indexes and federates
184
- them** at the root (a query spanning services without double-indexing).
206
+ - A single **federated query** at the root that fans out across the per-child
207
+ indexes and merges the results (today, `--cascade` builds the per-child indexes;
208
+ `find` / `brief` still run against one store at a time).
185
209
  - **Cross-service edges** — e.g. linking an API call to the route that defines it in
186
210
  another service. Code Map indexes language *symbols* and *module imports*, not
187
211
  framework routes or runtime HTTP calls, so it does not (today) map "service A calls
@@ -165,6 +165,32 @@ The on-behalf commit is guarded by the linked-worktree check (`isLinkedWorktree`
165
165
 
166
166
  Integration is strictly additive and opt-in. Plain `brainclaw harvest <assignment_id>` remains report-only; it reads and reports the lane result without committing or mutating assignment / claim state. The on-behalf commit and lifecycle completion happen only when the coordinator passes `--integrate`.
167
167
 
168
+ ### Worktree garbage collection on loop close (pln#594)
169
+
170
+ Closing a loop as **`completed`** garbage-collects the worktrees of its slot
171
+ assignments, so review/dispatch worktrees stop accumulating under
172
+ `~/.brainclaw/worktrees/`. The cascade runs inside `closeLoop` (so it covers MCP,
173
+ CLI, and reconciler-driven closes) and is **safe by default** — each worktree is
174
+ removed only when all of these hold:
175
+
176
+ - the worker no longer looks alive (no `.brainclaw-heartbeat-*` touched within the
177
+ liveness window) — this guard is never bypassed, even with force;
178
+ - the worktree has no un-harvested edits — anything beyond brainclaw birth-noise
179
+ (`.gitignore`, the sidecar), `LANE-RESULT.json`, and the heartbeat counts as
180
+ real work and is preserved;
181
+ - the lane branch carries no commits unreachable from the main repo HEAD (so
182
+ deleting the branch can't drop un-integrated work).
183
+
184
+ A worktree that fails a guard is **kept** (with a debug-log reason) so you can
185
+ harvest or inspect it. A **`cancelled`/`blocked`** close keeps the worktree and
186
+ its run logs for forensics. Removal is junction-safe (`removeWorktree` detaches
187
+ `node_modules`/`dist` junctions first), then the redundant dispatch branch is
188
+ deleted. The whole step is best-effort — it never blocks the close — and can be
189
+ disabled with `BRAINCLAW_NO_WORKTREE_GC=1`. The reusable primitive is
190
+ `gcWorktreeIfHarvested(mainWorktreePath, worktreePath, { force? })` in
191
+ `core/worktree.ts`; `brainclaw worktree clean` remains the manual/TTL backstop
192
+ for anything the cascade keeps.
193
+
168
194
  ---
169
195
 
170
196
  ## Diagnostic playbook
@@ -107,6 +107,7 @@ Each tool also has an `annotations.category` field: `session`, `context`, `memor
107
107
  | `bclaw_update` | memory | Partially update mutable fields on a canonical entity |
108
108
  | `bclaw_remove` | memory | Archive or purge a canonical entity |
109
109
  | `bclaw_transition` | memory | Move an entity through its validated state machine |
110
+ | `bclaw_move` | memory | Relocate an item to another project, id-preserving (multi-project) |
110
111
  | `bclaw_code_status` | discovery | Code Map freshness badge + index stats (store presence, files/nodes/edges) |
111
112
  | `bclaw_code_find` | discovery | Search the Code Map symbol index by name (function/class/component/hook/type) |
112
113
  | `bclaw_code_brief` | discovery | Ranked reading list + related decisions/traps before editing a symbol or path |
@@ -162,6 +163,7 @@ for the full 1.0.0 changelog.
162
163
  | `bclaw_update(entity, id, patch)` | Partial merge (updatable fields only) | `bclaw_update_plan`, `bclaw_update_memory` |
163
164
  | `bclaw_remove(entity, id, purge?)` | Archive (default) or hard-delete | `bclaw_delete_memory`, `bclaw_delete_plan` |
164
165
  | `bclaw_transition(entity, id, to, reason?)` | State machine transition with side-effect tags | `bclaw_accept`, `bclaw_reject`, status-update flows |
166
+ | `bclaw_move(entity, id, to_project, from_project?, force?)` | Id-preserving cross-project relocation (plan/decision/constraint/trap/handoff/sequence; execution entities rejected) | — (new in 1.11.0) |
165
167
 
166
168
  Supported entities: plan, decision, constraint, trap, handoff,
167
169
  runtime_note, candidate, sequence, claim, action, assignment, agent_run
@@ -10,6 +10,24 @@ guarantees this changelog follows.
10
10
 
11
11
  ## Unreleased
12
12
 
13
+ **Added — `bclaw_move` cross-project relocation (pln#595)**
14
+ - New canonical-grammar verb `bclaw_move(entity, id, to_project, from_project?, force?)`:
15
+ relocates a brainclaw item to another project in a multi-project workspace,
16
+ PRESERVING its id. Relocatable: plan, decision, constraint, trap, handoff,
17
+ sequence. Execution-local entities (claim, assignment, agent_run, session) are
18
+ rejected. Refuses id collisions / active-claim moves (unless force); audits both
19
+ stores. Additive — no tool removed or renamed.
20
+
21
+ **Changed — Code Map monorepo cascade (DGX Finding 2)**
22
+ - `bclaw_code_refresh` gains an optional `cascade: boolean`. In a
23
+ `project_mode: multi-project` workspace it refreshes every nested
24
+ brainclaw project into its own store + a child-scoped root store
25
+ (zero double-indexing). No-op outside a multi-project workspace.
26
+ - `bclaw_code_status` gains an optional `cascade: boolean` that adds a
27
+ per-child store-presence / freshness recap.
28
+ - No tool was removed or renamed; no required argument changed.
29
+ - Surface fingerprint bumped in the `(current)` section below.
30
+
13
31
  **Changed — agent-UX read-path surface (pln#542)**
14
32
  - `bclaw_work`, `bclaw_context`, `bclaw_find`, `bclaw_get`, `bclaw_search`
15
33
  gain an optional `budget_tokens` argument (relevance-ranked fill).
@@ -104,10 +122,16 @@ will still succeed. A follow-up PR will strip the dead handler code.
104
122
  changelog records the published MCP surface fingerprint. When a tool
105
123
  name, tier, category, or input schema changes, the test fails until
106
124
  this section is updated.
107
- - MCP public surface fingerprint: `sha256:35fd83b0d124df94`
108
- (updated 2026-06-20 for 1.10.0: Code Map tools added to the published surface —
125
+ - MCP public surface fingerprint: `sha256:188d2eba8828e4fe`
126
+ (updated 2026-06-24 for 1.11.0: `bclaw_move` added (pln#595) AND a `cascade`
127
+ boolean added to `bclaw_code_refresh` / `bclaw_code_status` (DGX Finding 2).
128
+ Additive: one new tool; nothing removed or renamed; no required argument changed.
129
+ Supersedes the per-branch interim hashes sha256:dffcc868ae90e013 and
130
+ sha256:41eb6d55010cdfb5.)
131
+ Previous: `sha256:35fd83b0d124df94`,
132
+ updated 2026-06-20 for 1.10.0: Code Map tools added to the published surface —
109
133
  `bclaw_code_find`, `bclaw_code_brief`, `bclaw_code_refresh`, `bclaw_code_status`.
110
- Additive: no tool removed or renamed.)
134
+ Additive: no tool removed or renamed.
111
135
  Previous: `sha256:70cf80b9615f631f`,
112
136
  updated 2026-06-18 for 1.9.1: monorepo project-scoping fix — session-aware
113
137
  effective-cwd resolution and read-path project scoping shift the published
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainclaw",
3
- "version": "1.10.2",
3
+ "version": "1.11.1",
4
4
  "description": "Shared project memory for humans and coding agents.",
5
5
  "type": "module",
6
6
  "repository": {