opencode-gbuild 0.6.4 → 0.7.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.
package/README.md CHANGED
@@ -12,6 +12,10 @@ Plan in conversation first (your agent's normal plan mode), then hand that plan
12
12
  3. `/gbuild:review <slug>` — audit the accumulated branch
13
13
  4. Issues? `/gbuild:plan <slug> --add "<fix>"`, then `/gbuild:run` again. Clean? `/gbuild:pr <slug>`
14
14
 
15
+ The feature branch lives in its own git worktree (created by `plan`) — your checkout never changes
16
+ branches, your uncommitted work is never in the way, and several features can be in flight in parallel
17
+ without racing each other's `HEAD`.
18
+
15
19
  (OpenCode: `/gbuild-plan`, `/gbuild-run`, … — same loop.)
16
20
 
17
21
  Inspired by the graph-engineering thread at
@@ -93,7 +97,8 @@ OpenCode (same order, kebab-case names):
93
97
  /gbuild-pr add-oauth-login-with-github
94
98
  ```
95
99
 
96
- - **`plan`** charts a feature into a CypherLite graph store at `.gbuild/<feature>/db/` — `Feature`,
100
+ - **`plan`** creates the feature branch in its own git worktree and charts the feature into a
101
+ CypherLite graph store there (`.gbuild/<feature>/db/`) — `Feature`,
97
102
  `Acceptance`, and `GbuildNode` nodes, each node's contract as `INPUT`/`OUTPUT` `Field` nodes, and
98
103
  `FROM` edges carrying data from one node's output to the next node's input. Every dependency edge
99
104
  passes the cut test — and because data flow is explicit, the validator checks it; every node has
@@ -101,12 +106,14 @@ OpenCode (same order, kebab-case names):
101
106
  - **`run`** dispatches every node in the current ready wave concurrently — not one at a time — with
102
107
  each file-touching node working in its own git worktree on its own branch, forked from the feature
103
108
  branch. A dedicated `gbuild-reviewer` subagent reviews each node's output against its branch's diff;
104
- only a passing node's branch is merged back into the feature branch, and its worktree is removed.
109
+ only a passing node's branch is merged back into the feature branch — in the feature's worktree, not
110
+ your checkout — and its worktree is removed.
105
111
  Unaccepted work never lands. A review failure applies the node's declared failure policy. Re-invoking
106
112
  after an interruption resumes only the remaining frontier, reusing the surviving worktrees.
107
113
  - **`status`** reports the graph — frontier, blocked, in-flight, completed, each node's review verdict,
108
114
  and evidence of what actually ran concurrently vs. serially.
109
- - **`review`** audits the finished branch as a whole in a `gbuild-auditor` subagent — abstraction
115
+ - **`review`** audits the finished branch as a whole in a `gbuild-auditor` subagent, working in the
116
+ feature worktree (the full suite runs there) — abstraction
110
117
  quality, file size, spaghetti growth, and the cross-node duplication that per-node review can't see
111
118
  (two nodes, two contexts, same sub-problem solved twice). Blocking findings go back into the graph as
112
119
  a `plan --add` requirement. Ported from hone-ai's `review`.
@@ -178,19 +185,20 @@ decision is wanted; neither guesses.
178
185
 
179
186
  ## Runtime
180
187
 
181
- gbuild **requires the [CypherLite](https://github.com/neo4j-labs/cypherlite) CLI**, 0.7.0 or newer:
188
+ gbuild **requires the [CypherLite](https://github.com/neo4j-labs/cypherlite) CLI**, 0.12.0 or newer:
182
189
 
183
190
  ```bash
184
191
  curl -sSfL https://neo4j-labs.github.io/cypherlite/install.sh | bash
185
192
  ```
186
193
 
187
194
  Every skill reads and writes the graph with `cypherlite` and nothing else — there is no Python and no
188
- direct JSON editing. The plugin ships the queries as `.cypher` files; skills pipe them into the store:
195
+ direct JSON editing. The plugin ships the queries as `.cypher` files; skills pipe them into the store
196
+ (`.gbuild/<feature>/db` inside the feature's worktree — examples below run from there):
189
197
 
190
198
  ```bash
191
- cypherlite .gbuild/<feature>/db --mode jsonl < plugins/gbuild/cypher/validate.cypher # format gate: no output = valid
192
- cypherlite .gbuild/<feature>/db --mode jsonl < plugins/gbuild/cypher/progress.cypher # one row per wave: counts + open nodes
193
- cypherlite .gbuild/<feature>/db --mode jsonl "MATCH (n:GbuildNode {status: 'failed'}) RETURN n.slug" # anything else
199
+ cypherlite query --database .gbuild/<feature>/db --mode jsonl < plugins/gbuild/cypher/validate.cypher # format gate: no output = valid
200
+ cypherlite query --database .gbuild/<feature>/db --mode jsonl < plugins/gbuild/cypher/progress.cypher # one row per wave: counts + open nodes
201
+ cypherlite query "MATCH (n:GbuildNode {status: 'failed'}) RETURN n.slug" --database .gbuild/<feature>/db --mode jsonl # anything else
194
202
  ```
195
203
 
196
204
  Reads are deliberately narrow so a run doesn't pay for the whole graph on every step: `run` claims a
@@ -225,8 +233,11 @@ plugins/gbuild/
225
233
  ```
226
234
 
227
235
  State lives outside the plugin, in the project: one CypherLite store per feature at
228
- `.gbuild/<feature>/db/`. It holds the plan, every node's status and output values, and the full review
236
+ `.gbuild/<feature>/db/` inside the feature's own git worktree. It holds the plan, every node's status
237
+ and output values, and the full review
229
238
  history. Only `db/graph.json` (CypherLite's JSON snapshot, kept current by every write) and the store's
230
- own `.gitignore` are committed. Per-node worktrees during a run live under `.git/gbuild/<feature>/` —
231
- never in `git status` — and each is removed the moment its node is accepted (merged) or terminally
232
- failed (discarded).
239
+ own `.gitignore` are committed (on the feature branch). All of a feature's worktrees live under
240
+ `.git/gbuild/<feature>/` — never in `git status`: `tree/` is the feature worktree itself, created by
241
+ `plan` and kept until the feature is done, and during a run each file-touching node gets a `<node>/`
242
+ worktree that is removed the moment its node is accepted (merged into the feature branch, in `tree/`)
243
+ or terminally failed (discarded).
@@ -1,24 +1,26 @@
1
1
  ---
2
2
  name: gbuild-auditor
3
- description: Strict end-of-feature maintainability auditor for a gbuild feature branch. Audits abstraction quality, file size, spaghetti growth, and missed code-judo simplifications across the current branch. Use during the final review phase of a gbuild feature, after the graph's nodes are all complete.
3
+ description: Strict end-of-feature maintainability auditor for a gbuild feature branch. Audits abstraction quality, file size, spaghetti growth, and missed code-judo simplifications across the feature branch's accumulated diff, working in the feature's git worktree. Use during the final review phase of a gbuild feature, after the graph's nodes are all complete.
4
4
  ---
5
5
 
6
6
  # gbuild Auditor
7
7
 
8
8
  Unusually strict review of implementation quality, maintainability, and abstraction health. Push for **ambitious** restructurings — "code judo" moves that preserve behavior while making the code dramatically simpler, smaller, and more direct. Delete complexity; don't rearrange it.
9
9
 
10
- The caller passes you the resolved feature slug and the feature context it read from the gbuild store: the `destination` and the acceptance bar (each criterion with the nodes covering it). Use it for scoping context only — you are auditing the branch's code, not re-grading individual nodes (`gbuild-reviewer` already did that per node, against each node's own contract; your job is the thing no per-node pass can see — what the accumulated diff did to the codebase as a whole). Do not open `.gbuild/` or run `cypherlite` — the caller owns the store. Do NOT write any file — output your audit as your final message exclusively.
10
+ The caller passes you the resolved feature slug, the **feature worktree** path, and the feature context it read from the gbuild store: the `destination` and the acceptance bar (each criterion with the nodes covering it). Use the context for scoping only — you are auditing the branch's code, not re-grading individual nodes (`gbuild-reviewer` already did that per node, against each node's own contract; your job is the thing no per-node pass can see — what the accumulated diff did to the codebase as a whole). The feature branch is checked out in the feature worktree: every file read, every check, and every git command runs there (`git -C <worktree> …` or with it as your working directory), never in the main tree. Do not open `.gbuild/` or run `cypherlite` — the caller owns the store. Do NOT write any file — output your audit as your final message exclusively.
11
11
 
12
12
  ## Run the branch's checks first
13
13
 
14
- Before you audit, run the repo's own verification on the branch: the full test suite, plus typecheck and
15
- lint where the repo defines them. Per-node reviews only checked each node against its own acceptance
14
+ Before you audit, run the repo's own verification on the branch, in the feature worktree: the full test suite, plus typecheck and
15
+ lint where the repo defines them. A worktree is a fresh checkout — if the project needs installed
16
+ dependencies to build or test (`node_modules`, `.venv`, …), install or symlink them there first.
17
+ Per-node reviews only checked each node against its own acceptance
16
18
  criteria — nobody has yet run the whole suite against the accumulated diff. A failing check is a
17
19
  blocking finding: name the exact failures.
18
20
 
19
21
  ## Core Prompt
20
22
 
21
- > Deep code-quality audit of the current branch. Rethink structure to improve quality without changing behavior. Improve abstractions, modularity, succinctness, legibility. Be ambitious — if restructuring the codebase yields a clearly better implementation, go for it. Measure twice, cut once.
23
+ > Deep code-quality audit of the feature branch. Rethink structure to improve quality without changing behavior. Improve abstractions, modularity, succinctness, legibility. Be ambitious — if restructuring the codebase yields a clearly better implementation, go for it. Measure twice, cut once.
22
24
 
23
25
  ## Standards
24
26
 
@@ -3,7 +3,7 @@
3
3
  // in_progress claimed but not recorded; if no run is active, an interrupted wave left it
4
4
  // blocked-by-failed pending behind a failed node, so it can never unblock on its own;
5
5
  // `detail` = the failed nodes it waits on
6
- // cypherlite .gbuild/<slug>/db --mode jsonl < cypher/attention.cypher
6
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl < cypher/attention.cypher
7
7
  CALL {
8
8
  MATCH (n:GbuildNode {status: 'failed'})
9
9
  RETURN n.slug AS slug, 'failed' AS reason,
@@ -5,7 +5,7 @@
5
5
  // external, and its `shape` says where to find it. No rows = nothing is
6
6
  // ready (the graph is done or stuck — ask progress.cypher / attention.cypher).
7
7
  // For a router, cancel the unselected branches with set-status.cypher first.
8
- // cypherlite .gbuild/<slug>/db --mode jsonl < cypher/dispatch.cypher
8
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl < cypher/dispatch.cypher
9
9
  MATCH (n:GbuildNode {status: 'pending'})
10
10
  WHERE NOT EXISTS { MATCH (n)-[:DEPENDS_ON]->(d:GbuildNode) WHERE NOT d.status IN ['completed', 'cancelled'] }
11
11
  SET n.status = 'in_progress'
@@ -2,7 +2,7 @@
2
2
  // nodes that cover each criterion. What review hands the auditor and what pr
3
3
  // builds the description from. Per-node output values are not included — get
4
4
  // one node's with node.cypher if you actually need it.
5
- // cypherlite .gbuild/<slug>/db --mode jsonl < cypher/feature.cypher
5
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl < cypher/feature.cypher
6
6
  MATCH (f:Feature)
7
7
  RETURN f.feature AS feature,
8
8
  f.destination AS destination,
@@ -3,7 +3,7 @@
3
3
  // external (from the codebase / environment): its `from` and `value` are null.
4
4
  // Used for reopen and ad-hoc inspection; run gets its definitions from
5
5
  // dispatch.cypher instead.
6
- // cypherlite .gbuild/<slug>/db --mode jsonl --param 'slug="<node-slug>"' < cypher/node.cypher
6
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl --param 'slug="<node-slug>"' < cypher/node.cypher
7
7
  MATCH (n:GbuildNode {slug: $slug})
8
8
  RETURN n.slug AS slug,
9
9
  n.title AS title,
@@ -2,7 +2,7 @@
2
2
  // that wave not yet completed or cancelled. A wave with `open: []` is done;
3
3
  // the graph is done when every row's `open` is empty. `wave` is the longest
4
4
  // DEPENDS_ON chain below a node (0 = no dependencies).
5
- // cypherlite .gbuild/<slug>/db --mode jsonl < cypher/progress.cypher
5
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl < cypher/progress.cypher
6
6
  MATCH (n:GbuildNode)
7
7
  OPTIONAL MATCH p = (n)-[:DEPENDS_ON*]->()
8
8
  WITH n, coalesce(max(length(p)), 0) AS wave
@@ -7,7 +7,7 @@
7
7
  // you sent. Output values must be scalars or lists of scalars — one nested
8
8
  // value rejects the whole batch (InvalidPropertyType); JSON-encode it and
9
9
  // resend.
10
- // cypherlite .gbuild/<slug>/db --mode jsonl --param 'results=[
10
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl --param 'results=[
11
11
  // {"slug": "a", "verdict": "pass", "failed_criteria": [], "outputs": {"value": 7},
12
12
  // "started_at": "<iso>", "completed_at": "<iso>"},
13
13
  // {"slug": "b", "verdict": "fail", "failed_criteria": ["<criterion>"]}
@@ -1,5 +1,5 @@
1
1
  // gbuild store schema. Run once, when plan creates the store:
2
- // cypherlite .gbuild/<slug>/db --snapshot-format json < cypher/schema.cypher
2
+ // cypherlite query --database .gbuild/<slug>/db --snapshot-format json < cypher/schema.cypher
3
3
  // Everything the engine can enforce at write time lives here; the rest of the
4
4
  // contract is checked by validate.cypher.
5
5
  CREATE CONSTRAINT feature_name FOR (n:Feature) REQUIRE n.feature IS :: STRING;
@@ -3,7 +3,7 @@
3
3
  // retry/repair, stop), or `pending` (reset nodes an interrupted run left
4
4
  // in_progress). `completed` is rejected — only record.cypher may set it, so it
5
5
  // can require a passing review and every output value.
6
- // cypherlite .gbuild/<slug>/db --mode jsonl --param 'slugs=["a","b"]' --param 'status="cancelled"' \
6
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl --param 'slugs=["a","b"]' --param 'status="cancelled"' \
7
7
  // < cypher/set-status.cypher
8
8
  UNWIND $slugs AS s
9
9
  MATCH (n:GbuildNode {slug: s})
@@ -4,7 +4,7 @@
4
4
  // otherwise = the stored status (in_progress, completed, failed, cancelled)
5
5
  // `wave` is the longest DEPENDS_ON chain below the node (0 = no dependencies),
6
6
  // so nodes sharing a wave are safe to run concurrently.
7
- // cypherlite .gbuild/<slug>/db --mode jsonl < cypher/status.cypher
7
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl < cypher/status.cypher
8
8
  MATCH (n:GbuildNode)
9
9
  OPTIONAL MATCH p = (n)-[:DEPENDS_ON*]->()
10
10
  WITH n, coalesce(max(length(p)), 0) AS wave
@@ -1,5 +1,5 @@
1
1
  // gbuild format gate. Zero rows = valid; every row is one violation.
2
- // cypherlite .gbuild/<slug>/db --mode jsonl < cypher/validate.cypher
2
+ // cypherlite query --database .gbuild/<slug>/db --mode jsonl < cypher/validate.cypher
3
3
  // Required properties, their types and slug/acceptance-id uniqueness are
4
4
  // engine constraints (schema.cypher) — a write that breaks them never lands.
5
5
  // This query checks everything the engine can't: enums, label/type agreement,
package/index.ts CHANGED
@@ -43,7 +43,7 @@ instructions above:
43
43
  - \`$ARGUMENTS\` refers to the arguments this skill was invoked with in the
44
44
  conversation.
45
45
  - run's "Workflow tool" backend is Claude-specific; always use the fallback
46
- backend described in step 3.
46
+ backend described in step 2.
47
47
  `
48
48
 
49
49
  const frontmatter = (raw: string): { description: string; body: string } => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-gbuild",
4
- "version": "0.6.4",
4
+ "version": "0.7.1",
5
5
  "description": "OpenCode plugin for gbuild — graph-native task planning and execution with concurrent fan-out, per-node review, and durable checkpoints.",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -1,12 +1,13 @@
1
1
  # Talking to the gbuild store
2
2
 
3
- Every gbuild skill reads and writes the feature store (`.gbuild/<feature>/db/`, modelled in
3
+ Every gbuild skill reads and writes the feature store (`.gbuild/<feature>/db/` inside the feature's git
4
+ worktree, modelled in
4
5
  `reference/graph-format.md`) with the `cypherlite` CLI and nothing else. This file is the full operating
5
6
  manual; the skills that only run plugin scripts carry the short version of these rules inline.
6
7
 
7
8
  ## Requirement
8
9
 
9
- `cypherlite` 0.7.0 or newer must be on PATH. Check first, in every skill:
10
+ `cypherlite` 0.12.0 or newer must be on PATH. Check first, in every skill:
10
11
 
11
12
  ```
12
13
  cypherlite --version
@@ -16,36 +17,44 @@ If the command is missing, stop and tell the user gbuild needs it:
16
17
  `curl -sSfL https://neo4j-labs.github.io/cypherlite/install.sh | bash`. Never fall back to reading or
17
18
  editing `graph.json` by hand.
18
19
 
20
+ The floor is the ADR-0200 CLI cutover: the store path is a flag (`-d` / `--database PATH`), the first
21
+ positional is the Cypher statement (never the database), and piped scripts and the interactive shell
22
+ require the explicit `query` verb. The old path-first form — database as the first argument — hard-fails.
23
+
19
24
  ## One writer, one command at a time
20
25
 
21
26
  CypherLite takes an exclusive lock on the store while a process has it open. A second process — reader
22
- or writer — fails with `storage locked` (exit 1). So:
27
+ or writer — fails with `storage locked` (exit 5). So:
23
28
 
24
29
  - **Only the skill running in the main context runs `cypherlite` against the store.** Subagents never
25
30
  do: give them what they need in their prompt, have them report results in their final message, and
26
31
  record those results yourself.
27
32
  - **Never run two `cypherlite` commands against the same store at once** — no parallel tool calls on it.
28
33
  Fan-out is for agents, not for store access.
29
- - On `storage locked`, another process holds the store (another gbuild command, a REPL left open). Wait a
30
- few seconds and retry, up to 3 times; then stop and report. Never delete `.cypherlite.lock`.
34
+ - On `storage locked` (exit 5), another process holds the store (another gbuild command, a REPL left
35
+ open). Wait a few seconds and retry, up to 3 times; then stop and report. Never delete
36
+ `.cypherlite.lock`.
31
37
 
32
38
  ## Invocation
33
39
 
34
40
  Always pass `--mode jsonl`: one compact JSON object per row, one row per line, and no output at all when
35
- there are no rows. `DB` below is `.gbuild/<feature>/db`.
41
+ there are no rows. `DB` below is the feature store: `.gbuild/<feature>/db` inside the feature worktree
42
+ (`<git-common-dir>/gbuild/<feature>/tree/`), or in the main tree for a feature charted before the
43
+ worktree layout. Every call is `cypherlite query [STATEMENT] --database DB …`; with no STATEMENT the
44
+ piped stdin is the script list.
36
45
 
37
46
  ```
38
47
  # a plugin script
39
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
48
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
40
49
 
41
50
  # a plugin script with parameters
42
- cypherlite DB --mode jsonl --param 'slug="b-consume-doubled"' < ${CLAUDE_PLUGIN_ROOT}/cypher/node.cypher
51
+ cypherlite query --database DB --mode jsonl --param 'slug="b-consume-doubled"' < ${CLAUDE_PLUGIN_ROOT}/cypher/node.cypher
43
52
 
44
53
  # an ad-hoc read — return only the columns you need, and keep a LIMIT on anything that can grow
45
- cypherlite DB --mode jsonl "MATCH (n:GbuildNode {status: 'failed'}) RETURN n.slug LIMIT 20"
54
+ cypherlite query "MATCH (n:GbuildNode {status: 'failed'}) RETURN n.slug LIMIT 20" --database DB --mode jsonl
46
55
 
47
56
  # a write you author (plan, reopen, fixes) — quoted heredoc delimiter, so the shell expands nothing
48
- cypherlite DB <<'CYPHER'
57
+ cypherlite query --database DB <<'CYPHER'
49
58
  .begin
50
59
  MATCH (n:GbuildNode {slug: 'b-consume-doubled'}) SET n.title = 'Consume the value, double it';
51
60
  .commit
@@ -70,17 +79,26 @@ CYPHER
70
79
  JSON-encode anything nested into a string.
71
80
  - **Read the result.** The plugin write scripts are guarded and return what they actually changed.
72
81
  Compare it with what you asked for; never assume a write landed.
73
- - **Exit code 1 is a failure** — lock, constraint violation (`ConstraintValidationFailed`), syntax. Read
74
- stderr, fix, retry the whole transaction.
82
+ - **Check the exit code.** `0` is success (an empty result included). `5` is `storage locked` or a
83
+ write conflict — wait and retry as above. `6` is a rejected statement — syntax, semantic, or type
84
+ error, including `ConstraintValidationFailed` and writes on a `--read-only` store — read stderr, fix,
85
+ retry the whole transaction. `2` is a usage error — fix the invocation, not the data. `1` is any other
86
+ runtime failure. With `--mode jsonl` a failure also prints one JSON error envelope on stdout, after
87
+ any rows already written: `{"error":{"code","exit_code","kind","message","retryable"}}`.
88
+ - **Agent output is capped at 100 rows.** Under a detected agent harness with non-terminal stdout —
89
+ gbuild skills always are — a statement prints at most 100 rows, warns on stderr, and still exits `0`.
90
+ Keep a `LIMIT` on anything that can grow; when you genuinely need every row, pass `--max-rows 0` (or
91
+ set `CYPHERLITE_MAX_ROWS=0`).
75
92
 
76
93
  ## Creating a store
77
94
 
78
- Only `plan` does this, once per feature:
95
+ Only `plan` does this, once per feature, with `DB` = `.gbuild/<feature>/db` inside the feature worktree
96
+ it just created:
79
97
 
80
98
  ```
81
- mkdir -p .gbuild/<feature>/db
82
- cypherlite .gbuild/<feature>/db --snapshot-format json < ${CLAUDE_PLUGIN_ROOT}/cypher/schema.cypher
83
- printf '*\n!.gitignore\n!graph.json\n' > .gbuild/<feature>/db/.gitignore
99
+ mkdir -p DB
100
+ cypherlite query --database DB --snapshot-format json < ${CLAUDE_PLUGIN_ROOT}/cypher/schema.cypher
101
+ printf '*\n!.gitignore\n!graph.json\n' > DB/.gitignore
84
102
  ```
85
103
 
86
104
  `--snapshot-format json` belongs on this first call only (it applies to an empty directory). It installs
@@ -2,7 +2,9 @@
2
2
 
3
3
  This file is normative. Where a skill disagrees with this doc, this doc wins.
4
4
 
5
- One CypherLite store per feature: `.gbuild/<feature>/db/`. `plan` creates and charts it, `run` records
5
+ One CypherLite store per feature: `.gbuild/<feature>/db/` inside the feature's git worktree
6
+ (`<git-common-dir>/gbuild/<feature>/tree/` — `plan` creates the worktree; a feature charted before the
7
+ worktree layout keeps its store in the main tree). `plan` creates and charts it, `run` records
6
8
  progress in it, and every skill reads it — **all through Cypher**. No skill reads or writes `graph.json`
7
9
  directly; it is CypherLite's snapshot of the store. How to talk to the store (the single-writer rule,
8
10
  invocation, quoting, the script catalog) is in `reference/cypher.md`.
@@ -39,7 +41,7 @@ data **and** constraints.
39
41
 
40
42
  | property | type | meaning |
41
43
  | -------------- | -------------- | ---------------------------------------------------- |
42
- | `feature` | string | the slug — matches the `.gbuild/<feature>/` dir, immutable |
44
+ | `feature` | string | the slug — matches the `.gbuild/<feature>/` dir in the feature worktree, immutable |
43
45
  | `destination` | string | one line: what is true once this is done |
44
46
  | `context` | string | why this exists, one or two lines |
45
47
  | `out_of_scope` | list of string | bullets, may be empty |
@@ -60,7 +62,7 @@ universal label is for `MATCH (n:GbuildNode)`; the type label enables `MATCH (n:
60
62
 
61
63
  | property | type | meaning |
62
64
  | ---------------- | -------------- | -------------------------------------------------------------- |
63
- | `slug` | string | kebab-case, unique, immutable — the identity every skill keys on |
65
+ | `slug` | string | kebab-case, unique, immutable — the identity every skill keys on; `tree` is reserved (the feature worktree's directory name) |
64
66
  | `title` | string | what the user reads; refer to nodes by title in conversation |
65
67
  | `type` | string | `research \| decision \| code \| test \| verify \| chore` |
66
68
  | `acceptance` | list of string | node-level criteria — mandatory, non-empty, concrete |
@@ -1,8 +1,9 @@
1
1
  # Ad-hoc queries
2
2
 
3
3
  The plugin scripts (`reference/cypher.md` § Script catalog) cover everything the skills do. These are for
4
- the questions in between. Run them with `cypherlite .gbuild/<feature>/db --mode jsonl "<query>"`, one at a
5
- time; return only the columns you need and keep a `LIMIT` on anything that can grow.
4
+ the questions in between. Run them with `cypherlite query "<query>" --database DB --mode jsonl`, one at a
5
+ time — `DB` is the feature store (`.gbuild/<feature>/db` inside the feature worktree);
6
+ return only the columns you need and keep a `LIMIT` on anything that can grow.
6
7
 
7
8
  ```cypher
8
9
  -- the whole plan with progress
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Decomposes a feature into a real dependency graph in the gbuild CypherLite store (.gbuild/<feature>/db) — nodes with typed field contracts, mandatory concrete acceptance criteria, and edges that pass the cut test. Use when starting new gbuild work, before /gbuild:run.
2
+ description: Decomposes a feature into a real dependency graph in the gbuild CypherLite store — nodes with typed field contracts, mandatory concrete acceptance criteria, and edges that pass the cut test. Creates the feature branch in its own git worktree, never touching the main checkout. Use when starting new gbuild work, before /gbuild:run.
3
3
  ---
4
4
 
5
5
  Plan the work described in `$ARGUMENTS` as a graph.
@@ -24,30 +24,45 @@ inform the choices below. `${CLAUDE_PLUGIN_ROOT}/reference/checklist.md` is the
24
24
  | a description, link, or file path | **Chart** — new feature |
25
25
  | an existing feature slug with `--add "<req>"` | **Reopen** |
26
26
 
27
- A bare slug with no `.gbuild/<slug>/db/` is a description, not a slug — chart it.
27
+ A bare slug with no store is a description, not a slug — chart it. A slug's store lives in its feature
28
+ worktree at `<git-common-dir>/gbuild/<slug>/tree/.gbuild/<slug>/db` (a feature charted before the
29
+ worktree layout keeps its store at `.gbuild/<slug>/db` in the main tree).
28
30
 
29
31
  ## Chart
30
32
 
31
- ### 1. Slug and branch
33
+ ### 1. Slug, branch, and feature worktree
32
34
 
33
35
  Derive the feature slug from `$ARGUMENTS`: kebab-case, ≤4 words, names the outcome not the mechanism
34
36
  (`oauth-device-flow`, not `add-some-auth-stuff`). It's the `.gbuild/<slug>/` directory name and the
35
37
  `Feature` node's `feature` property — immutable once charted.
36
38
 
37
- Then get onto a branch for it, so `run`'s commits land somewhere isolated:
39
+ gbuild never changes the branch of the user's checkout: the feature branch lives in its own **feature
40
+ worktree**, so several features can be charted and run in parallel without racing each other's `HEAD`,
41
+ and uncommitted work in the main tree is irrelevant — there is no dirty-tree check. Resolve two paths,
42
+ both absolute: `GC` = `git rev-parse --git-common-dir` (resolve it to an absolute path), and `FT` =
43
+ `GC/gbuild/<slug>/tree` — the feature worktree, the one place the feature branch is checked out. Then:
38
44
 
39
- - Working tree dirty → stop and ask. Don't stash or sweep someone else's changes into your branch.
40
- - Already on a branch ending in `<slug>` → stay there, say so.
41
- - Otherwise `git checkout -b <prefix>/<slug>` from current HEAD. Take `<prefix>` from the repo's
42
- convention — project or user instructions first, else the dominant pattern in `git branch --list`
43
- — and fall back to `gbuild` when there is none.
45
+ - `FT` is already a registered worktree (`git worktree list --porcelain`) → reuse it, say so.
46
+ - A branch named `<slug>` or ending `/<slug>` already exists → if it's checked out somewhere (a feature
47
+ charted before the worktree layout usually has it in the main tree), that location is `FT`; otherwise
48
+ attach it: `git worktree add FT <branch>`.
49
+ - Otherwise create branch and worktree in one step, forked from the current HEAD:
44
50
 
45
- Report the branch. Everything below happens on it.
51
+ ```
52
+ git worktree add FT -b <prefix>/<slug> HEAD
53
+ ```
54
+
55
+ Take `<prefix>` from the repo's convention — project or user instructions first, else the dominant
56
+ pattern in `git branch --list` — and fall back to `gbuild` when there is none.
57
+
58
+ Report the branch and `FT`. Everything below happens in `FT`; `DB` below is
59
+ `FT/.gbuild/<slug>/db`.
46
60
 
47
61
  ### 2. Analyse the codebase
48
62
 
49
63
  Read the project manifest(s), directory structure, `README.md`, and — for related in-flight work — the
50
- `Feature` of every existing store (`feature.cypher` against each `.gbuild/*/db`, one at a time). Resolve
64
+ `Feature` of every existing store (`feature.cypher` against each `GC/gbuild/*/tree/.gbuild/*/db`, plus
65
+ any legacy `.gbuild/*/db` in the main tree, one at a time). Resolve
51
66
  file paths or URLs in `$ARGUMENTS`; if a reference fails to load, say so and ask.
52
67
 
53
68
  ### 3. Validate and explore
@@ -140,7 +155,7 @@ Then write the whole graph as **one** Cypher script, piped through a quoted here
140
155
  the edges), `.commit`, `.checkpoint`.
141
156
 
142
157
  ```
143
- cypherlite .gbuild/<slug>/db <<'CYPHER'
158
+ cypherlite query --database DB <<'CYPHER'
144
159
  .begin
145
160
  CREATE (f:Feature {feature: '<slug>', destination: '...', context: '...', out_of_scope: ['...']})
146
161
  CREATE (a1:Acceptance {id: 'a-1', text: '...'})
@@ -163,7 +178,7 @@ slug) fails the transaction and writes nothing — fix the script and run it aga
163
178
  ### 9. Validate
164
179
 
165
180
  ```
166
- cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
181
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
167
182
  ```
168
183
 
169
184
  No output means valid. Every row is a violation (`check`, `detail`): fix it with Cypher — a `.begin` …
@@ -173,7 +188,7 @@ hand off or commit a graph that fails validation, and never edit `graph.json` di
173
188
  Then confirm run-readiness:
174
189
 
175
190
  ```
176
- cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
191
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
177
192
  ```
178
193
 
179
194
  One row per wave: each row's `open` slugs are the decomposition, and the `frontier` counts are what
@@ -181,18 +196,21 @@ One row per wave: each row's `open` slugs are the decomposition, and the `fronti
181
196
 
182
197
  ### 10. Commit and stop
183
198
 
184
- Commit `.gbuild/<slug>/` unless the repo ignores it (`git check-ignore -q .gbuild/`; if true, don't stage
185
- anything under it). The store's own `.gitignore` limits that to `db/graph.json` and `db/.gitignore`.
186
- Message: `<slug>: chart graph`.
199
+ Commit `.gbuild/<slug>/` on the feature branch unless the repo ignores it (`git -C FT check-ignore -q
200
+ .gbuild/`; if true, don't stage anything under it). The store's own `.gitignore` limits that to
201
+ `db/graph.json` and `db/.gitignore`. Stage and commit from the feature worktree:
202
+ `git -C FT add .gbuild/<slug>` and `git -C FT commit -m "<slug>: chart graph"`.
187
203
 
188
- Report the branch, the waves, and the frontier. Close with `next: /gbuild:run <slug>`.
204
+ Report the branch, `FT`, the waves, and the frontier. Close with `next: /gbuild:run <slug>`.
189
205
 
190
206
  ## Reopen
191
207
 
192
208
  `--add "<requirement>"`:
193
209
 
194
- 0. Check out the feature's existing branch if you're not on it. Never chart a reopen onto a different
195
- branch than the one the graph was charted on.
210
+ 0. Resolve `FT` and `DB` exactly as Chart's step 1 describes (reuse the existing feature worktree;
211
+ attach it if the branch is checked out nowhere). Never chart a reopen onto a different branch than
212
+ the one the graph was charted on, and never into the user's main checkout unless that's where the
213
+ feature branch already lives (a legacy feature).
196
214
  1. Read the current graph narrowly: `progress.cypher` for its shape and what's done, and `node.cypher`
197
215
  only for the nodes the requirement touches.
198
216
  2. Design the delta: a new `Acceptance` node (next `a-` id) linked from the `Feature`, and whatever new
@@ -202,7 +220,7 @@ Report the branch, the waves, and the frontier. Close with `next: /gbuild:run <s
202
220
  3. Write it as one script that `MATCH`es what it connects to and `CREATE`s the rest:
203
221
 
204
222
  ```
205
- cypherlite .gbuild/<slug>/db <<'CYPHER'
223
+ cypherlite query --database DB <<'CYPHER'
206
224
  .begin
207
225
  MATCH (f:Feature), (up:GbuildNode {slug: '<existing-slug>'})-[:OUTPUT]->(up_out:Field {name: '<field>'})
208
226
  CREATE (f)-[:HAS_ACCEPTANCE]->(a:Acceptance {id: 'a-3', text: '...'})
@@ -216,8 +234,9 @@ Report the branch, the waves, and the frontier. Close with `next: /gbuild:run <s
216
234
  CYPHER
217
235
  ```
218
236
 
219
- 4. **Re-run `validate.cypher`** until it prints nothing, then `progress.cypher`. Commit
220
- (`<slug>: reopen graph — <what was added>`), report the new frontier.
237
+ 4. **Re-run `validate.cypher`** until it prints nothing, then `progress.cypher`. Commit in `FT`
238
+ (`git -C FT commit -m "<slug>: reopen graph — <what was added>"` after staging `.gbuild/<slug>`,
239
+ same ignore rule as Chart's step 10), report the new frontier.
221
240
 
222
241
  If the requirement needs no new nodes (an existing node's contract already covers it), say so and stop
223
242
  — don't manufacture graph churn.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Pushes the current gbuild feature branch to the appropriate remote, opens a pull request with a concise description that classifies the change (new feature, fix, optimization, etc.) and notes any user-facing impact, then monitors CI checks in the background and auto-triggers a graph reopen + run if any fail. Use after /gbuild:review reports nothing blocking, to publish the finished feature branch.
2
+ description: Pushes the gbuild feature branch to the appropriate remote, opens a pull request with a concise description that classifies the change (new feature, fix, optimization, etc.) and notes any user-facing impact, then monitors CI checks in the background and auto-triggers a graph reopen + run if any fail. Works from the feature's own worktree, never touching the main checkout's branch. Use after /gbuild:review reports nothing blocking, to publish the finished feature branch.
3
3
  ---
4
4
 
5
5
  Publish a finished feature branch: push it to the right remote, open a well-formed pull request, then watch the PR's CI checks and drive a graph reopen automatically when a check goes red — looping until the checks are green or a round cap is hit.
@@ -12,9 +12,9 @@ This skill MUST run in the **main conversation context**. It MUST NOT be invoked
12
12
 
13
13
  `$ARGUMENTS` is free-form text: an optional feature slug and an optional `--max-rounds N` flag.
14
14
 
15
- - **slug**: the first token that isn't a flag. If absent, infer it — the current branch commonly ends in the slug (`plan` checks out `<prefix>/<slug>`), otherwise fall back to the store whose `.gbuild/*/db/graph.json` was most recently modified. If neither resolves, leave `<slug>` unresolved (the auto-fix step degrades gracefully — see Step 4).
15
+ - **slug**: the first token that isn't a flag. If absent, infer it — if exactly one feature worktree exists (`<git-common-dir>/gbuild/*/tree`), that's the slug; otherwise fall back to the most recently modified `graph.json` under `<git-common-dir>/gbuild/*/tree/.gbuild/*/db` (or, for features charted before the worktree layout, `.gbuild/*/db/graph.json` in the main tree). If neither resolves, leave `<slug>` unresolved (the auto-fix step degrades gracefully — see Step 4).
16
16
 
17
- When `<slug>` resolved, every graph read below goes through `cypherlite`: run `cypherlite --version` first (missing → stop; gbuild needs it), one command at a time, always `--mode jsonl` (no output = no rows), and on `storage locked` wait and retry up to 3 times. Anything else: `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`.
17
+ When `<slug>` resolved, every graph read below goes through `cypherlite`: run `cypherlite --version` first (missing → stop; gbuild needs it), one command at a time, always `--mode jsonl` (no output = no rows), and on `storage locked` wait and retry up to 3 times. Anything else: `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`. `DB` below is `FT/.gbuild/<slug>/db` (see Step 1 for `FT`).
18
18
  - **max_rounds**: the value of `--max-rounds N` (also accept `--max-rounds=N`). Default `3` when absent.
19
19
 
20
20
  ## Step 1: Preconditions
@@ -24,10 +24,10 @@ Verify the environment before touching the remote. Stop with a clear, actionable
24
24
  1. **Git repo.** This skill is git + GitHub specific. If the repo is not git, stop and explain that `/gbuild:pr` only supports git + GitHub (`gh`).
25
25
  2. **GitHub CLI.** Confirm `gh` is installed and authenticated (`gh auth status`). If not, stop and tell the user to install/authenticate `gh`.
26
26
  3. **Base branch.** Determine the upstream default branch: `gh repo view --json defaultBranchRef -q .defaultBranchRef.name`. Call it `<base>`.
27
- 4. **Not on base.** Get the current branch (`git branch --show-current`). If it equals `<base>`, stop — there is nothing to open a PR for.
28
- 5. **Clean tree.** Check for uncommitted changes (`git status --porcelain`). `/gbuild:run` commits each node's work as it goes, so the tree should be clean. If there are uncommitted changes, warn the user and ask whether to proceed (they may want to commit first).
29
- 6. **Commits ahead of base.** Confirm the branch has commits the base lacks (`git log <base>..HEAD --oneline`). If empty, stop — nothing to push.
30
- 7. **Graph finished.** When `<slug>` resolved, run `cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher`. If any row's `open` is non-empty, warn that the graph isn't finished (name the open nodes) and ask whether to open the PR anyway. If any row has `failed` > 0, get their names from `attention.cypher` and name them — a PR built on an escalated node is usually premature.
27
+ 4. **Feature worktree and branch.** Resolve `FT` (the feature worktree) and `<branch>` (the feature branch checked out there) exactly as `/gbuild:run`'s step 0 describes — the registered worktree at `<git-common-dir>/gbuild/<slug>/tree`, a legacy feature whose branch is in the main tree, or attach a branch that is checked out nowhere. When `<slug>` is unresolved, use the main tree's current branch as `<branch>` and its root as `FT`. If `<branch>` equals `<base>`, stop — there is nothing to open a PR for.
28
+ 5. **Clean tree.** Check for uncommitted changes in the feature worktree (`git -C FT status --porcelain`). `/gbuild:run` commits each node's work as it goes, so it should be clean. If there are uncommitted changes, warn the user and ask whether to proceed (they may want to commit first).
29
+ 6. **Commits ahead of base.** Confirm the branch has commits the base lacks (`git log <base>..<branch> --oneline`). If empty, stop — nothing to push.
30
+ 7. **Graph finished.** When `<slug>` resolved, run `cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher`. If any row's `open` is non-empty, warn that the graph isn't finished (name the open nodes) and ask whether to open the PR anyway. If any row has `failed` > 0, get their names from `attention.cypher` and name them — a PR built on an escalated node is usually premature.
31
31
 
32
32
  ## Step 2: Determine the remote & push
33
33
 
@@ -37,7 +37,8 @@ Choose the push remote from the project's actual remotes:
37
37
  2. **Exactly one remote** → use it.
38
38
  3. **Multiple remotes** → look for a push-target hint in `AGENTS.md` or `CLAUDE.md` (e.g. an explicit statement that the project pushes to a fork). If a clear hint exists, use it. Otherwise, use `AskUserQuestion` to let the user pick which remote to push to (e.g. `origin` vs a fork remote).
39
39
 
40
- Push the current branch and set upstream: `git push -u <remote> <branch>`.
40
+ Push the feature branch and set upstream: `git push -u <remote> <branch>` (branch names are refs — the
41
+ main tree's checkout is irrelevant).
41
42
 
42
43
  ## Step 3: Open the pull request
43
44
 
@@ -45,9 +46,9 @@ Build a **short, well-formed** description, then open the PR.
45
46
 
46
47
  Gather context from:
47
48
 
48
- - Commit subjects: `git log <base>..HEAD --oneline`.
49
- - File overview: `git diff <base>...HEAD --stat`.
50
- - When `<slug>` resolved: `cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher` — the `destination` and the acceptance bar (with the nodes covering each criterion).
49
+ - Commit subjects: `git log <base>..<branch> --oneline`.
50
+ - File overview: `git diff <base>...<branch> --stat`.
51
+ - When `<slug>` resolved: `cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher` — the `destination` and the acceptance bar (with the nodes covering each criterion).
51
52
 
52
53
  Derive a concise, conventional-commit-style title from the feature/branch and the changes (e.g. `feat(auth): add OAuth login`).
53
54
 
@@ -72,13 +73,13 @@ This description was auto generated
72
73
 
73
74
  The trailing `This description was auto generated` line is **mandatory** — it always ends the body, after every other section, and is never dropped or reworded.
74
75
 
75
- Create the PR: `gh pr create --base <base> --title "<title>" --body "<body>"`. Let `gh` resolve the head branch (it handles fork `owner:branch` head refs automatically). Capture the PR URL and number (`<pr>`).
76
+ Create the PR: `gh pr create --base <base> --title "<title>" --body "<body>"` — run it with `FT` as the working directory, so `gh` resolves the head from the feature branch (it handles fork `owner:branch` head refs automatically). Capture the PR URL and number (`<pr>`).
76
77
 
77
78
  ## Step 4: Monitor checks in the background → auto-fix loop
78
79
 
79
80
  Watch the PR's CI checks and react when they fail. Run for at most `max_rounds` rounds:
80
81
 
81
- 1. **Launch the watch in the background.** Run `gh pr checks <pr> --watch --fail-fast` via the Bash tool with `run_in_background: true`. It exits `0` when all checks pass and non-zero when a check fails; the harness re-invokes this skill when the command exits, so continue from the result.
82
+ 1. **Launch the watch in the background.** Run `gh pr checks <pr> --watch --fail-fast` (from `FT`) via the Bash tool with `run_in_background: true`. It exits `0` when all checks pass and non-zero when a check fails; the harness re-invokes this skill when the command exits, so continue from the result.
82
83
  2. **Checks green (exit 0):** report success and go to Step 5.
83
84
  3. **Checks red (non-zero exit):**
84
85
  - Collect the failure detail: `gh pr checks <pr>` for the failed-check list, and `gh run view <run-id> --log-failed` for the failing logs.
@@ -88,7 +89,7 @@ Watch the PR's CI checks and react when they fail. Run for at most `max_rounds`
88
89
  2. Run it unattended. `plan`'s Reopen asks nothing, but if executing it would prompt the user (an ambiguous decomposition), resolve it yourself from the failing logs and note the choice in the final report — `/gbuild:pr` runs without a human present.
89
90
  3. If Reopen concludes the requirement needs no new nodes, stop the loop and go to Step 5 with an `unresolved` status — the failure isn't something the graph can act on, so surface the logs to the user.
90
91
  4. Read `${CLAUDE_PLUGIN_ROOT}/skills/run/SKILL.md` and execute it inline against `<slug>` to run the newly-unblocked frontier. Suppress its trailing `next: /gbuild:status…` line — this skill owns the transition.
91
- - After the run completes, push the new commits (`git push`) and **re-launch the watch** (next round).
92
+ - After the run completes, push the new commits (`git -C FT push`) and **re-launch the watch** (next round).
92
93
 
93
94
  4. **Round cap reached while still red:** stop, print the still-failing checks and their logs, and tell the user to inspect manually. Go to Step 5 with an `unresolved` status.
94
95
 
@@ -100,3 +101,6 @@ Report the outcome:
100
101
  PR: <url> (checks: green | fixed after <rounds> round(s) | unresolved after <max_rounds> round(s))
101
102
  next: /gbuild:status <slug>
102
103
  ```
104
+
105
+ The feature worktree `FT` keeps the branch checked out until removed — once the PR is merged and the
106
+ graph is no longer needed, `git worktree remove FT` (and `git branch -d <branch>`) cleans up.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Performs a strict end-of-feature maintainability audit of the current gbuild branch — abstraction quality, file size, spaghetti growth, cross-node duplication, missed code-judo simplifications. Runs the audit in the gbuild-auditor sub-agent and relays its result to chat. Use after /gbuild:run finishes a graph, before /gbuild:pr.
2
+ description: Performs a strict end-of-feature maintainability audit of a gbuild feature branch — abstraction quality, file size, spaghetti growth, cross-node duplication, missed code-judo simplifications. Runs the audit in the gbuild-auditor sub-agent, in the feature's worktree, and relays its result to chat. Use after /gbuild:run finishes a graph, before /gbuild:pr.
3
3
  ---
4
4
 
5
5
  Run the strict end-of-feature maintainability audit in a dedicated sub-agent, then relay its result.
@@ -10,10 +10,14 @@ missed abstractions that only exist *between* nodes, which no per-node review ca
10
10
 
11
11
  ## Arguments
12
12
 
13
- `$ARGUMENTS` is optional. If present, treat it as the feature slug. If absent, infer it — the current
14
- branch commonly ends in the slug (`plan` checks out `<prefix>/<slug>`), otherwise fall back to the store
15
- whose `.gbuild/*/db/graph.json` was most recently modified. Do NOT write any file — this skill outputs to
16
- chat exclusively.
13
+ `$ARGUMENTS` is optional. If present, treat it as the feature slug. If absent, infer it — if exactly one
14
+ feature worktree exists (`<git-common-dir>/gbuild/*/tree`), that's the slug; otherwise fall back to the
15
+ most recently modified `graph.json` under `<git-common-dir>/gbuild/*/tree/.gbuild/*/db` (or, for
16
+ features charted before the worktree layout, `.gbuild/*/db/graph.json` in the main tree). Do NOT write
17
+ any file — this skill outputs to chat exclusively.
18
+
19
+ Resolve `FT`, the feature worktree, exactly as `/gbuild:run`'s step 0 describes (reuse the registered
20
+ worktree, attach if the feature branch is checked out nowhere). `DB` below is `FT/.gbuild/<slug>/db`.
17
21
 
18
22
  Store rules: run `cypherlite --version` first (missing → stop; gbuild needs it). One `cypherlite`
19
23
  command at a time, always `--mode jsonl` (no output = no rows); on `storage locked` wait and retry up to
@@ -22,7 +26,7 @@ command at a time, always `--mode jsonl` (no output = no rows); on `storage lock
22
26
  Check the graph is finished — one row per wave, cheap:
23
27
 
24
28
  ```
25
- cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
29
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
26
30
  ```
27
31
 
28
32
  If any row's `open` is non-empty, say the graph isn't finished (name the open nodes) and ask whether to
@@ -34,7 +38,7 @@ address.
34
38
  Fetch the feature context:
35
39
 
36
40
  ```
37
- cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher
41
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher
38
42
  ```
39
43
 
40
44
  Launch the `gbuild-auditor` subagent (agents/gbuild-auditor.md — this plugin's own copy, gbuild does not
@@ -43,7 +47,8 @@ depend on hone-ai being installed). Pass it:
43
47
  - The resolved slug and the `feature.cypher` result verbatim (destination and the acceptance bar with
44
48
  the nodes covering each criterion) — the auditor never opens the store itself, and reads the code from
45
49
  the branch diff.
46
- - Tell it to audit the current branch.
50
+ - The feature worktree path `FT` and the feature branch name — it audits the branch's code from `FT`:
51
+ every read, every check, and every git command runs there, never in the main tree.
47
52
 
48
53
  The sub-agent runs the full audit in its own fresh context and returns the audit as its final message.
49
54
  That message is a tool result — it is NOT shown to the user. So once it returns:
@@ -2,7 +2,8 @@
2
2
  description: Executes a gbuild graph — dispatches every ready-and-independent node in the current wave concurrently, each file-touching node in its own git worktree, reviews each node's output with gbuild-reviewer before merging its branch and recording it complete, and resumes only the remaining frontier on re-invocation. Use after /gbuild:plan has charted the graph.
3
3
  ---
4
4
 
5
- Run the graph in `.gbuild/$ARGUMENTS/db` (the feature slug is `$ARGUMENTS`; `DB` below).
5
+ Run the graph for the feature `$ARGUMENTS` (the slug; step 0 resolves `DB`, the store — it lives in
6
+ the feature's own worktree, so this skill never changes the branch of the user's checkout).
6
7
 
7
8
  **CRITICAL: this skill must run in the main context, not inside a forked agent.** It dispatches its own
8
9
  subagents per node; a subagent cannot itself fan out further subagents reliably. It is also the store's
@@ -20,39 +21,51 @@ back in their final message, and this skill records it.
20
21
  Exit code 1 = failure; read stderr. Anything beyond the scripts below: `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`.
21
22
  - Never re-read what you already hold. The claimed wave's definitions stay valid for its retries.
22
23
 
23
- ## Step 0: Check and recover
24
+ ## Step 0: Establish VCS facts, then check and recover
25
+
26
+ Default to `git`. Resolve these facts first, absolute paths, and reuse them throughout:
27
+
28
+ - `GC` = `git rev-parse --git-common-dir`, resolved to an absolute path; `WT` = `GC/gbuild/$ARGUMENTS`
29
+ (the worktree root — inside `.git/`, so worktrees never appear in `git status` or get committed).
30
+ - `FT` = the feature worktree and `BRANCH` = the feature branch — the one place `BRANCH` is checked
31
+ out, and where every merge and store commit below lands:
32
+ 1. `WT/tree` is a registered worktree (`git worktree list --porcelain`) → `FT` = it, `BRANCH` =
33
+ `git -C FT branch --show-current`.
34
+ 2. Else a worktree whose branch is `$ARGUMENTS` or ends `/$ARGUMENTS` → `FT` = that worktree's path
35
+ (a feature charted before the worktree layout usually has its branch in the main tree).
36
+ 3. Else such a branch exists but is checked out nowhere → attach it: `git worktree add WT/tree
37
+ <branch>`, `FT` = `WT/tree`.
38
+ 4. Else → stop: there is no feature branch for `$ARGUMENTS` — chart it with `/gbuild:plan` first.
39
+ - `DB` = `FT/.gbuild/$ARGUMENTS/db`. `git -C FT check-ignore -q .gbuild/` tells you whether to commit
40
+ the store at all.
41
+
42
+ Then check and recover:
24
43
 
25
44
  ```
26
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
27
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/attention.cypher
45
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
46
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/attention.cypher
28
47
  ```
29
48
 
30
49
  - `validate` printing anything → stop and report the rows; do not run a graph `plan` didn't finish
31
50
  correctly.
32
51
  - `attention` rows with reason `in_progress` are left over from an interrupted run (this skill is the
33
52
  only writer and records every node before finishing). Check each one's worktree branch for commits
34
- its agent already made (`git log $(git branch --show-current)..gbuild/<feature>/<slug>` — empty
53
+ its agent already made (`git log BRANCH..gbuild/<feature>/<slug>` — empty
35
54
  means it never got that far) and leave the worktree in place: the re-dispatch reuses it. Then reset
36
55
  them all in one call so they re-dispatch, and mention it in the report:
37
56
 
38
57
  ```
39
- cypherlite DB --mode jsonl --param 'slugs=["<slug>", ...]' --param 'status="pending"' \
58
+ cypherlite query --database DB --mode jsonl --param 'slugs=["<slug>", ...]' --param 'status="pending"' \
40
59
  < ${CLAUDE_PLUGIN_ROOT}/cypher/set-status.cypher
41
60
  ```
42
61
 
43
- - Sweep the worktree root (step 1's `WT`) for leaks from an interruption that struck mid-cleanup: any
44
- worktree whose slug is *not* among the slugs just reset does not belong to live work — remove it
62
+ - Sweep the worktree root `WT` for leaks from an interruption that struck mid-cleanup: any worktree
63
+ under `WT` — **except `FT` itself (`WT/tree`)** — whose slug is *not* among the slugs just reset does
64
+ not belong to live work — remove it
45
65
  (`git worktree remove --force`) and delete its branch (`git branch -D`). An unmerged branch is
46
66
  unaccepted work, which must never land — that is what the isolation is for.
47
67
 
48
- ## Step 1: Establish VCS facts
49
-
50
- Default to `git`. `git check-ignore -q .gbuild/` tells you whether to commit the store at all. Record
51
- two facts for the wave mechanics below: `BRANCH` = `git branch --show-current` (the feature branch —
52
- every accepted node merges back into it), and `WT` = `$(git rev-parse --git-common-dir)/gbuild/$ARGUMENTS`
53
- (the worktree root — inside `.git/`, so worktrees never appear in `git status` or get committed).
54
-
55
- ## Step 2: Pick a backend
68
+ ## Step 1: Pick a backend
56
69
 
57
70
  **Workflow tool**, if this session has opted into multi-agent orchestration (ultracode, or the user
58
71
  asked for a workflow explicitly) — translate each claimed wave into a `pipeline()`/`parallel()` script:
@@ -62,25 +75,25 @@ file-touching (`code`/`test`/`chore`) node gets `isolation: 'worktree'`.
62
75
  Controlled-cycle clusters become a bounded `while` loop per `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md`'s
63
76
  round cap. Route `model_tier: strong` nodes via `opts.model` where the harness supports an override. The
64
77
  script never touches the store: collect the results, land each accepted node's branch from the main
65
- context (step 3.4), then record (step 3.5).
78
+ context (step 2.4), then record (step 2.5).
66
79
 
67
- **Fallback** (default — no opt-in required, and always what step 3 below assumes): fire one `Agent`
80
+ **Fallback** (default — no opt-in required, and always what step 2 below assumes): fire one `Agent`
68
81
  tool call per claimed node, **all in a single message** — this is what makes the fan-out real rather
69
82
  than claimed. Do not dispatch them one at a time across separate messages; that's exactly the serial
70
83
  behavior this plugin replaces.
71
84
 
72
- ## Step 3: One wave
85
+ ## Step 2: One wave
73
86
 
74
87
  1. **CLAIM.**
75
88
 
76
89
  ```
77
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/dispatch.cypher
90
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/dispatch.cypher
78
91
  ```
79
92
 
80
93
  It moves the whole frontier to `in_progress` and prints one row per claimed node with everything its
81
94
  agent needs: `slug`, `title`, `type`, `acceptance`, `failure_policy`, `model_tier`, `verify`,
82
95
  `inputs` (an input with a `value` came from an upstream node; one without is external — its `shape`
83
- says where to find it), and `outputs`. **No rows** → nothing is ready: go to step 5.
96
+ says where to find it), and `outputs`. **No rows** → nothing is ready: go to step 4.
84
97
 
85
98
  2. **IMPLEMENT.** First, give every claimed `code`/`test`/`chore` node its own worktree — one shell
86
99
  loop over the wave's slugs, idempotent so an interrupted or re-dispatched node keeps its earlier
@@ -93,7 +106,8 @@ behavior this plugin replaces.
93
106
  || git worktree add WT/<slug> gbuild/<feature>/<slug>
94
107
  ```
95
108
 
96
- (skip if the worktree already exists; else fork a fresh branch off `BRANCH`; if the branch already
109
+ (skip if the worktree already exists; else fork a fresh branch off `BRANCH` — just a ref here, the
110
+ cwd doesn't matter; if the branch already
97
111
  exists from an earlier attempt, attach to it instead — nothing is redone). Then, in one message,
98
112
  spawn one `Agent` per claimed node with its row. Tell it to:
99
113
  - actually do the work — for `code`/`test`/`chore` nodes make the change **in its worktree**
@@ -118,25 +132,26 @@ behavior this plugin replaces.
118
132
  point). Never let the implementing agent review its own work.
119
133
 
120
134
  4. **LAND.** For each `pass` verdict on a `code`/`test`/`chore` node, merge its branch into the
121
- feature branch and clean up, from the main tree:
135
+ feature branch — in the feature worktree `FT`, never the user's checkout — and clean up:
122
136
 
123
137
  ```
124
- git merge --no-ff gbuild/<feature>/<slug> \
138
+ git -C FT merge --no-ff gbuild/<feature>/<slug> \
125
139
  && git worktree remove --force WT/<slug> \
126
140
  && git branch -d gbuild/<feature>/<slug>
127
141
  ```
128
142
 
129
- A merge conflict means two supposedly independent nodes touched the same lines: `git merge --abort`
130
- and treat the node as review-failed, the conflict as its failed criterion — step 6's policy decides
143
+ A merge conflict means two supposedly independent nodes touched the same lines: `git -C FT merge
144
+ --abort`
145
+ and treat the node as review-failed, the conflict as its failed criterion — step 5's policy decides
131
146
  (a retry/repair re-dispatch tells the agent to rebase its branch onto `BRANCH` and resolve it).
132
147
  Worktrees and branches are deleted only here, after acceptance — a failed node's stays for its next
133
- attempt (step 6).
148
+ attempt (step 5).
134
149
 
135
150
  5. **RECORD** the whole wave in one call — one result per node, `verdict` from the reviewer's
136
151
  `VERDICT:` line, `failed_criteria` from its `[fail]` lines:
137
152
 
138
153
  ```
139
- cypherlite DB --mode jsonl --param 'results=[
154
+ cypherlite query --database DB --mode jsonl --param 'results=[
140
155
  {"slug": "<slug>", "verdict": "pass", "failed_criteria": [], "outputs": {...},
141
156
  "started_at": "<iso>", "completed_at": "<iso>"},
142
157
  {"slug": "<slug>", "verdict": "fail", "failed_criteria": ["<criterion>"]}
@@ -165,20 +180,21 @@ behavior this plugin replaces.
165
180
  `set-status.cypher` before the next claim.
166
181
 
167
182
  Nodes in the same wave never read each other's outputs, and every file-touching node works in its own
168
- worktree, so true concurrent dispatch is safe by construction — the store calls and the merges onto
169
- `BRANCH` are the only serialized points.
183
+ worktree, so true concurrent dispatch is safe by construction — the store calls and the merges in `FT`
184
+ are the only serialized points, and since `FT` belongs to this feature alone, other features (or the
185
+ user) changing branches in their own checkouts cannot race them.
170
186
 
171
- ## Step 4: Next wave
187
+ ## Step 3: Next wave
172
188
 
173
- If `.gbuild/` isn't ignored, commit the store: `git add .gbuild/<feature>/db` and
174
- `git commit -m "<feature>: record wave <n>"`. Then claim again (step 3). Keep going until a claim
175
- returns no rows.
189
+ If `.gbuild/` isn't ignored, commit the store in the feature worktree: `git -C FT add
190
+ .gbuild/<feature>/db` and `git -C FT commit -m "<feature>: record wave <n>"`. Then claim again (step 2).
191
+ Keep going until a claim returns no rows.
176
192
 
177
- ## Step 5: Report and stop
193
+ ## Step 4: Report and stop
178
194
 
179
195
  ```
180
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
181
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/attention.cypher
196
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
197
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/attention.cypher
182
198
  ```
183
199
 
184
200
  Every `progress` row with `open: []` → the graph is done. Otherwise `attention` names what's stuck
@@ -194,7 +210,7 @@ Report: which nodes ran this invocation, which passed review outright vs. needed
194
210
 
195
211
  ## Resuming
196
212
 
197
- Re-invoking `/gbuild:run <feature>` after an interruption re-runs step 0 fresh: nodes an interrupted
198
- wave left `in_progress` are reset (their worktrees and any commits on their branches survive — the
199
- re-dispatch reuses them), and the next claim picks up exactly the remaining frontier. Nothing needs to
200
- be told what already finished — the store is the memory.
213
+ Re-invoking `/gbuild:run <feature>` after an interruption re-runs step 0 fresh: the feature worktree is
214
+ resolved or re-attached, nodes an interrupted wave left `in_progress` are reset (their worktrees and any
215
+ commits on their branches survive — the re-dispatch reuses them), and the next claim picks up exactly
216
+ the remaining frontier. Nothing needs to be told what already finished — the store is the memory.
@@ -2,7 +2,11 @@
2
2
  description: Reports a gbuild feature's graph — frontier, blocked, in-flight, completed, each node's review verdict, and the cluster shapes with evidence of what actually ran concurrently. Use to check progress or diagnose a stuck run.
3
3
  ---
4
4
 
5
- Report status for `$ARGUMENTS`. This skill only reads the store; `DB` below is `.gbuild/<feature>/db`.
5
+ Report status for `$ARGUMENTS`. This skill only reads the store. `DB` below is the feature's store:
6
+ `FT/.gbuild/<feature>/db`, where `FT` is the feature worktree at `<git-common-dir>/gbuild/<feature>/tree`
7
+ (a feature charted before the worktree layout keeps its store at `.gbuild/<feature>/db` in the main
8
+ tree). Status never creates or attaches worktrees — if neither store exists, say the feature's worktree
9
+ is gone and that `git worktree add <git-common-dir>/gbuild/<feature>/tree <branch>` reattaches it.
6
10
 
7
11
  ## Store rules
8
12
 
@@ -17,7 +21,7 @@ Report status for `$ARGUMENTS`. This skill only reads the store; `DB` below is `
17
21
 
18
22
  | arguments | mode |
19
23
  | ------------------ | -------------------------------------- |
20
- | empty | every feature under `.gbuild/` |
24
+ | empty | every feature store |
21
25
  | a feature slug | that feature only |
22
26
 
23
27
  ## Single feature
@@ -27,8 +31,8 @@ This is the human-facing report, so it's the one place that reads every node.
27
31
  ### 1. Load
28
32
 
29
33
  ```
30
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/status.cypher
31
- cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
34
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/status.cypher
35
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
32
36
  ```
33
37
 
34
38
  One call after the other. `status.cypher` gives one row per node with its derived `state`
@@ -76,10 +80,11 @@ don't let silence be ambiguous between "checked, clear" and "not checked."
76
80
 
77
81
  ## All-features mode
78
82
 
79
- Don't read every node of every feature. For each `.gbuild/<slug>/db` found, one store at a time:
83
+ Don't read every node of every feature. For each store found — `<git-common-dir>/gbuild/*/tree/.gbuild/*/db`,
84
+ plus any legacy `.gbuild/*/db` in the main tree — one store at a time:
80
85
 
81
86
  ```
82
- cypherlite .gbuild/<slug>/db --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
87
+ cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
83
88
  ```
84
89
 
85
90
  Print one line per feature: slug, node counts by state summed over its waves, and whether it's done
@@ -1,7 +1,7 @@
1
1
  // A worked 4-node diamond: A produces a value, B and C each consume it
2
2
  // independently, D joins both. This is exactly the script plan writes for a
3
3
  // new feature — one atomic statement, piped into the store:
4
- // cypherlite .gbuild/<slug>/db < plan.cypher
4
+ // cypherlite query --database .gbuild/<slug>/db < plan.cypher
5
5
  // Variables (f, a1, a, a_value, ...) are only scoped to this one statement, so
6
6
  // the whole graph is created in a single CREATE chain.
7
7
  .begin