From 39fc66389443744d29c42cf82a9c96af68193ecf Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 29 Sep 2026 16:19:39 +0000 Subject: [PATCH] fix(sync-gbrain): define Step 4 helper args and one atomic write path Both census read-ready attempts spent turns reading the helper source to resolve , inspecting fixture internals kept inside the repo, and reconciling 'Read + Edit' with the tmp+mv atomic write, then hit max turns before the verdict. --- sync-gbrain/SKILL.md | 15 ++++++++++----- sync-gbrain/SKILL.md.tmpl | 15 ++++++++++----- test/helpers/sync-gbrain-readiness-fixture.ts | 11 ++++++----- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/sync-gbrain/SKILL.md b/sync-gbrain/SKILL.md index bf60cfae3..a3455f9dc 100644 --- a/sync-gbrain/SKILL.md +++ b/sync-gbrain/SKILL.md @@ -672,6 +672,10 @@ Capability check (per /plan-eng-review §6): bun run ~/.claude/skills/gstack/bin/gstack-gbrain-read-capability.ts ``` +`` are the same flags this /sync-gbrain invocation passed to Step 2, +unchanged (empty for a plain run). The helper needs no other input: run it once +and use its JSON result; do not inspect its source or the gbrain CLI first. + The helper reports JSON `status: ready` only after the successful code sync's source and real worktree match `.gbrain-source`, the source registration points to that worktree, and a bounded, source-scoped list/get returns the same page. @@ -746,16 +750,17 @@ sync code walk for them requires an explicit `--allow-reclone` opt-in. ``` -Use the Read + Edit tools. The find-and-replace target is the entire region -from `` through +Read CLAUDE.md once and compute its new content. The replacement target is +the entire region from `` through ``. If those markers are missing, search for `## GBrain Search Guidance (configured by /sync-gbrain)` heading and replace from there to the next `## ` or EOF. If no heading exists, append the entire block at the end of CLAUDE.md. -**Atomic write:** write the new CLAUDE.md content to a tmp file alongside it -(e.g., `CLAUDE.md.sync-gbrain.tmp`) then `mv` to atomic-rename, so a crash -mid-write never leaves the file half-modified. +**Atomic write (the only write path; do not Edit CLAUDE.md in place):** Write +the complete new content to `CLAUDE.md.sync-gbrain.tmp` beside it, then `mv` it +over CLAUDE.md, so a crash mid-write never leaves the file half-modified. Verify +the block count in the same Bash call as the `mv`, then go to Step 5. **If `status=unknown`** — preserve the existing guidance block, if any, and report the helper's reason as WARN with advice to retry `/sync-gbrain` or the diff --git a/sync-gbrain/SKILL.md.tmpl b/sync-gbrain/SKILL.md.tmpl index fe3bec81c..0398939c6 100644 --- a/sync-gbrain/SKILL.md.tmpl +++ b/sync-gbrain/SKILL.md.tmpl @@ -323,6 +323,10 @@ Capability check (per /plan-eng-review §6): bun run ~/.claude/skills/gstack/bin/gstack-gbrain-read-capability.ts ``` +`` are the same flags this /sync-gbrain invocation passed to Step 2, +unchanged (empty for a plain run). The helper needs no other input: run it once +and use its JSON result; do not inspect its source or the gbrain CLI first. + The helper reports JSON `status: ready` only after the successful code sync's source and real worktree match `.gbrain-source`, the source registration points to that worktree, and a bounded, source-scoped list/get returns the same page. @@ -397,16 +401,17 @@ sync code walk for them requires an explicit `--allow-reclone` opt-in. ``` -Use the Read + Edit tools. The find-and-replace target is the entire region -from `` through +Read CLAUDE.md once and compute its new content. The replacement target is +the entire region from `` through ``. If those markers are missing, search for `## GBrain Search Guidance (configured by /sync-gbrain)` heading and replace from there to the next `## ` or EOF. If no heading exists, append the entire block at the end of CLAUDE.md. -**Atomic write:** write the new CLAUDE.md content to a tmp file alongside it -(e.g., `CLAUDE.md.sync-gbrain.tmp`) then `mv` to atomic-rename, so a crash -mid-write never leaves the file half-modified. +**Atomic write (the only write path; do not Edit CLAUDE.md in place):** Write +the complete new content to `CLAUDE.md.sync-gbrain.tmp` beside it, then `mv` it +over CLAUDE.md, so a crash mid-write never leaves the file half-modified. Verify +the block count in the same Bash call as the `mv`, then go to Step 5. **If `status=unknown`** — preserve the existing guidance block, if any, and report the helper's reason as WARN with advice to retry `/sync-gbrain` or the diff --git a/test/helpers/sync-gbrain-readiness-fixture.ts b/test/helpers/sync-gbrain-readiness-fixture.ts index 3ef477ee8..b818b50b8 100644 --- a/test/helpers/sync-gbrain-readiness-fixture.ts +++ b/test/helpers/sync-gbrain-readiness-fixture.ts @@ -6,10 +6,11 @@ import { spawnSync } from 'node:child_process'; const root = path.resolve(import.meta.dir, '../..'); export function createReadinessFixture(kind: 'ready' | 'unknown') { - const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gbrain-ready-')); - const home = path.join(workDir, '.fixture-home'); - const bin = path.join(workDir, '.fixture-bin'); - fs.mkdirSync(home); fs.mkdirSync(bin); + const base = fs.mkdtempSync(path.join(os.tmpdir(), 'gbrain-ready-')); + const workDir = path.join(base, 'repo'); + const home = path.join(base, 'home'); + const bin = path.join(base, 'bin'); + fs.mkdirSync(workDir); fs.mkdirSync(home); fs.mkdirSync(bin); const init = spawnSync('git', ['init', '--quiet'], { cwd: workDir, timeout: 10_000 }); if (init.status !== 0) throw new Error('readiness fixture git init failed'); fs.writeFileSync(path.join(workDir, '.gbrain-source'), 'client-fixture\n'); @@ -57,6 +58,6 @@ else { console.error('unsupported operation'); process.exit(3); } sourceIntact: () => fs.readFileSync(path.join(workDir, '.gbrain-source'), 'utf8') === pin && fs.readFileSync(path.join(stateDir, '.gbrain-sync-state.json'), 'utf8') === state && !fs.existsSync(path.join(workDir, 'code')), - cleanup: () => fs.rmSync(workDir, { recursive: true, force: true }), + cleanup: () => fs.rmSync(base, { recursive: true, force: true }), }; }