opencode-gbuild 0.4.0 → 0.6.0

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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Decomposes a feature into a real dependency graph in .gbuild/<feature>/graph.json — nodes with typed 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 (.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.
3
3
  ---
4
4
 
5
5
  Plan the work described in `$ARGUMENTS` as a graph.
@@ -11,9 +11,11 @@ skill: `/gbuild:run <slug>` once — it iterates waves until the graph completes
11
11
  `/gbuild:review <slug>`; if it finds issues, reopen with `/gbuild:plan <slug> --add "<fix>"` and
12
12
  `/gbuild:run` again; if clean, `/gbuild:pr <slug>`.
13
13
 
14
- Read `${CLAUDE_PLUGIN_ROOT}/reference/graph-format.md` before doing anything — it's the normative schema. `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md`,
15
- `${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`, and `${CLAUDE_PLUGIN_ROOT}/reference/cost-model.md` inform the choices below.
16
- `${CLAUDE_PLUGIN_ROOT}/reference/checklist.md` is the self-check at the end.
14
+ Read `${CLAUDE_PLUGIN_ROOT}/reference/graph-format.md` (the normative graph model) and
15
+ `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md` (how to talk to the store) before doing anything, and run
16
+ its version check. `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md`,
17
+ `${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`, and `${CLAUDE_PLUGIN_ROOT}/reference/cost-model.md`
18
+ inform the choices below. `${CLAUDE_PLUGIN_ROOT}/reference/checklist.md` is the self-check at the end.
17
19
 
18
20
  ## Mode
19
21
 
@@ -22,7 +24,7 @@ Read `${CLAUDE_PLUGIN_ROOT}/reference/graph-format.md` before doing anything —
22
24
  | a description, link, or file path | **Chart** — new feature |
23
25
  | an existing feature slug with `--add "<req>"` | **Reopen** |
24
26
 
25
- A bare slug with no `.gbuild/<slug>/` is a description, not a slug — chart it.
27
+ A bare slug with no `.gbuild/<slug>/db/` is a description, not a slug — chart it.
26
28
 
27
29
  ## Chart
28
30
 
@@ -30,7 +32,7 @@ A bare slug with no `.gbuild/<slug>/` is a description, not a slug — chart it.
30
32
 
31
33
  Derive the feature slug from `$ARGUMENTS`: kebab-case, ≤4 words, names the outcome not the mechanism
32
34
  (`oauth-device-flow`, not `add-some-auth-stuff`). It's the `.gbuild/<slug>/` directory name and the
33
- `feature` field in `graph.json` — immutable once charted.
35
+ `Feature` node's `feature` property — immutable once charted.
34
36
 
35
37
  Then get onto a branch for it, so `run`'s commits land somewhere isolated:
36
38
 
@@ -44,9 +46,9 @@ Report the branch. Everything below happens on it.
44
46
 
45
47
  ### 2. Analyse the codebase
46
48
 
47
- Read the project manifest(s), directory structure, `README.md`, and any existing `.gbuild/*/graph.json`
48
- for related in-flight work. Resolve file paths or URLs in `$ARGUMENTS`; if a reference fails to load,
49
- say so and ask.
49
+ 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
51
+ file paths or URLs in `$ARGUMENTS`; if a reference fails to load, say so and ask.
50
52
 
51
53
  ### 3. Validate and explore
52
54
 
@@ -77,8 +79,8 @@ the survey when there's only one real way — don't perform thoroughness for its
77
79
  a `research`-typed GbuildNode in the first wave — `plan` charts it, `run` executes it. `plan` does
78
80
  not run prototypes inline (the "Plan, don't do" rule below). The one exception: a check so cheap it
79
81
  finishes in a single tool call (grep, read a file, check an installed version) — do that inline and
80
- note the result. A prototype worth charting names the specific question it answers in its
81
- `contract.output`, and its `acceptance` is the answer to that question.
82
+ note the result. A prototype worth charting names the specific question it answers in its `OUTPUT`
83
+ field, and its `acceptance` is the answer to that question.
82
84
 
83
85
  ### 4. Name the destination
84
86
 
@@ -87,108 +89,102 @@ done. Do not proceed until it's that sharp; everything past it goes in `out_of_s
87
89
 
88
90
  ### 5. Decompose into nodes, not steps
89
91
 
90
- List the real pieces of work. For each pair, apply the **cut test** (`${CLAUDE_PLUGIN_ROOT}/reference/graph-format.md`):
91
- does one actually read the other's output? If no, they're independent — same wave, not a chain. This is
92
- the step hone-ai v1 skipped, and the whole reason gbuild exists: state the true dependency structure,
93
- don't default to the order you thought of things in.
94
-
95
- Classify each cluster's shape per `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md` (chain / diamond / router / controlled cycle).
96
- A controlled cycle needs its round cap and dedup rule decided now, not left to `run` to invent.
97
-
98
- The graph is a **CypherLite GraphData file** (see `${CLAUDE_PLUGIN_ROOT}/reference/graph-format.md` — normative). You
99
- write three kinds of nodes and three kinds of relationships:
100
-
101
- - **`Feature` node** (id 1) — one per graph, properties `feature` (the slug), `destination`, `context`,
102
- `out_of_scope` (array). No `acceptance` property here.
103
- - **`Acceptance` nodes** — one per feature-level bullet, properties `id` (`a-1`, `a-2`, …) and `text`.
104
- Each linked `Feature -[:HAS_ACCEPTANCE]-> Acceptance`.
105
- - **`GbuildNode`s** — one per unit of work. Integer `id` (sequential after the Feature and Acceptance
106
- nodes), `labels` `["GbuildNode", <PascalCase type>]`, properties `slug` / `title` / `type` (lowercase
107
- enum) / `contract` (`{input,output}`) / `acceptance` (node-level local criteria) / `verify` / `failure_policy`
108
- / `model_tier`. No `satisfies` property — use a `SATISFIES` edge instead.
109
- - **`DEPENDS_ON` relationships** — one per dependency that passed the cut test. Direction
110
- dependent→dependency (`start_node` depends on `end_node`). Both endpoints are GbuildNodes.
111
- - **`SATISFIES` relationships** — `GbuildNode -[:SATISFIES]-> Acceptance` for each global criterion a
112
- node covers.
113
- - **`HAS_ACCEPTANCE` relationships** — `Feature -[:HAS_ACCEPTANCE]-> Acceptance`, one per bullet.
114
-
115
- For each GbuildNode, write:
116
-
117
- - `id` — integer, sequential, unique, never reused. Authoring convenience; `slug` is the stable identity.
118
- - `slug` — filename-safe, kebab-case, immutable once created. This is what skills and checkpoint files
119
- key on; it's how nodes are referred to in anything the user reads.
120
- - `type` — `research | decision | code | test | verify | chore`. Set both the lowercase `type` property
121
- and the matching PascalCase label (`Code`, `Research`, …).
122
- - `contract.input` / `contract.output` — structured shapes, not prose. A downstream node's `input`
123
- should be able to reference an upstream node's `output` field directly.
124
- - `acceptance` — **mandatory, non-empty, concrete.** No vague adjectives ("looks correct", "works
125
- well"). A criterion a fresh context with no other information couldn't check is not concrete enough.
126
- Rewrite it until it names a file, a shape, a value, or a command's result.
127
- - `verify` — only set when this node needs something *beyond* the standard `gbuild-reviewer` pass
128
- (adversarial check, fact-check, human approval). Usually `null`.
129
- - `failure_policy` — pick per `${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`, don't default everything to `retry`.
130
- - `model_tier` — `strong` only where judgement, not throughput, is the bottleneck
131
- (`${CLAUDE_PLUGIN_ROOT}/reference/cost-model.md`).
132
-
133
- Wire each dependency as a `DEPENDS_ON` relationship (not a `dependencies` array on the node), and each
134
- criterion a node covers as a `SATISFIES` relationship (not a `satisfies` array). Set `next_node_id` and
135
- `next_rel_id` to `max(id)+1` for each kind. `${CLAUDE_PLUGIN_ROOT}/templates/graph.json` is a complete worked example.
92
+ List the real pieces of work. For each pair, apply the **cut test** (`graph-format.md`): does one
93
+ actually read the other's output? If no, they're independent — same wave, not a chain. This is the step
94
+ hone-ai v1 skipped, and the whole reason gbuild exists: state the true dependency structure, don't
95
+ default to the order you thought of things in.
96
+
97
+ Classify each cluster's shape per `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md` (chain / diamond / router /
98
+ controlled cycle). A controlled cycle needs its round cap and dedup rule decided now, not left to `run`
99
+ to invent.
100
+
101
+ For each GbuildNode decide (full model in `graph-format.md`):
102
+
103
+ - `slug` — kebab-case, immutable; what skills and the store key on. `title` — what the user reads.
104
+ - `type` — `research | decision | code | test | verify | chore`, plus the matching PascalCase label.
105
+ - **`OUTPUT` fields** — every node declares at least one, each with a precise `shape`
106
+ (`<integer>`, `<list of changed file paths>`, `<commit sha>`). Values will be scalars or lists of
107
+ scalars; for anything nested, the shape says it is a JSON-encoded string.
108
+ - **`INPUT` fields** — one per value the node reads. If it comes from another node, wire it
109
+ `FROM` that node's `OUTPUT` field and add the `DEPENDS_ON`. If it comes from the codebase or the
110
+ environment, leave it without `FROM` and say where in its `shape`. The cut test is now concrete:
111
+ a `DEPENDS_ON` exists exactly when the node reads one of the other's outputs.
112
+ - `acceptance` — **mandatory, non-empty, concrete.** No vague adjectives. A criterion a fresh context
113
+ with no other information couldn't check is not concrete enough — rewrite it until it names a file, a
114
+ shape, a value, or a command's result.
115
+ - `verify` — only when the node needs something *beyond* the standard `gbuild-reviewer` pass. Usually
116
+ omitted.
117
+ - `failure_policy` — per `${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`, don't default everything
118
+ to `retry`. `model_tier` — `strong` only where judgement, not throughput, is the bottleneck.
119
+ - `status: 'pending'` — every new node starts pending.
136
120
 
137
121
  ### 6. Write the global acceptance bar
138
122
 
139
- The feature-level bar is one `Acceptance` node per bullet, each linked `Feature -[:HAS_ACCEPTANCE]-> Acceptance`,
140
- with ids `a-1`, `a-2`, …. Every bullet must end up covered by at least one node's `SATISFIES` edge —
141
- that's checked in step 7 and enforced by the validator.
123
+ The feature-level bar is one `Acceptance` node per bullet (`id` `a-1`, `a-2`, …), each linked
124
+ `(:Feature)-[:HAS_ACCEPTANCE]->(:Acceptance)`. Every bullet must end up covered by at least one node's
125
+ `SATISFIES` edge — the validator enforces it.
142
126
 
143
127
  ### 7. Self-check against the checklist
144
128
 
145
- Walk every item in `${CLAUDE_PLUGIN_ROOT}/reference/checklist.md` against the graph you just wrote. Fix the graph, don't
146
- annotate around a failing item.
129
+ Walk every item in `${CLAUDE_PLUGIN_ROOT}/reference/checklist.md` against the graph you designed. Fix the
130
+ design, don't annotate around a failing item.
147
131
 
148
- ### 8. Write and validate
132
+ ### 8. Create the store and write the graph
149
133
 
150
- Write `.gbuild/<slug>/graph.json` (a CypherLite GraphData file). **Run the format validator first:**
134
+ Create the store exactly as `reference/cypher.md` § Creating a store shows (`mkdir`, `schema.cypher` with
135
+ `--snapshot-format json`, the `db/.gitignore`).
136
+
137
+ Then write the whole graph as **one** Cypher script, piped through a quoted heredoc.
138
+ `${CLAUDE_PLUGIN_ROOT}/templates/example.cypher` is a complete worked example — follow its structure:
139
+ `.begin`, a single statement of `CREATE` clauses (so variables like `a1` or `a_value` stay in scope for
140
+ the edges), `.commit`, `.checkpoint`.
151
141
 
152
142
  ```
153
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/validate_format.py .gbuild/<slug>/graph.json
143
+ cypherlite .gbuild/<slug>/db <<'CYPHER'
144
+ .begin
145
+ CREATE (f:Feature {feature: '<slug>', destination: '...', context: '...', out_of_scope: ['...']})
146
+ CREATE (a1:Acceptance {id: 'a-1', text: '...'})
147
+ CREATE (f)-[:HAS_ACCEPTANCE]->(a1)
148
+ CREATE (n:GbuildNode:Code {slug: '...', title: '...', type: 'code', acceptance: ['...'],
149
+ failure_policy: 'repair', model_tier: 'cheap', status: 'pending'})
150
+ CREATE (n)-[:OUTPUT]->(n_out:Field {name: '...', shape: '<...>'})
151
+ ...
152
+ CREATE (m)-[:DEPENDS_ON]->(n)
153
+ CREATE (m)-[:INPUT]->(:Field {name: '...', shape: '<...>'})-[:FROM]->(n_out)
154
+ CREATE (m)-[:SATISFIES]->(a1);
155
+ .commit
156
+ .checkpoint
157
+ CYPHER
154
158
  ```
155
159
 
156
- If it prints `invalid: ...`, fix the file and re-run — never hand off or commit a file that fails
157
- validation. The validator is the format gate; it checks the GraphData envelope, node shapes, the three
158
- relationship types, id/counter invariants, acyclicity, and coverage. **Run it after every change** —
159
- initial write, reopen, any hand-edit, any fix-up — before doing anything else with the file.
160
+ A constraint violation (`ConstraintValidationFailed`: a missing property, a wrong type, a duplicate
161
+ slug) fails the transaction and writes nothing — fix the script and run it again.
160
162
 
161
- Then confirm run-readiness:
163
+ ### 9. Validate
162
164
 
163
165
  ```
164
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/graph.py .gbuild/<slug>/graph.json --status
166
+ cypherlite .gbuild/<slug>/db -json < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
165
167
  ```
166
168
 
167
- The graph is now queryable with Cypher **if the `cypherlite` binary is on PATH** (`command -v
168
- cypherlite`) — in that case you can mention it to the user:
169
+ `[]` means valid. Every row is a violation (`check`, `detail`): fix it with Cypher — a `.begin` …
170
+ `.commit` … `.checkpoint` script of `MATCH … SET/CREATE/DELETE` — and re-run until it prints `[]`. Never
171
+ hand off or commit a graph that fails validation, and never edit `graph.json` directly.
172
+
173
+ Then confirm run-readiness:
169
174
 
170
175
  ```
171
- cypherlite .gbuild/<slug>/graph.json "MATCH (n:GbuildNode) RETURN n.slug, n.type"
176
+ cypherlite .gbuild/<slug>/db -json < ${CLAUDE_PLUGIN_ROOT}/cypher/status.cypher
172
177
  ```
173
178
 
174
- If the binary is absent, skip this without comment. CypherLite is an optional consumer, never a
175
- requirement — `validate_format.py` already cross-checks with it when present and passes without it,
176
- and the user should never be asked to install it.
177
-
178
- ### 9. Fire research nodes
179
-
180
- For each `research`-typed node in the initial wave, dispatch a subagent **in parallel** (one message,
181
- multiple `Agent` calls). Give each the node's `contract.input`, `acceptance`, and enough context to
182
- work independently. Each writes its `output` to `.gbuild/<slug>/nodes/<id>.json` directly (`status:
183
- completed`) — these still get a `gbuild-reviewer` pass before `run` will treat them as satisfying
184
- anyone's dependency, same as every other node type.
179
+ The `wave` column is the decomposition; rows with `state: frontier` are what `run` dispatches first.
185
180
 
186
181
  ### 10. Commit and stop
187
182
 
188
- Commit `.gbuild/<slug>/` unless the repo ignores it (`git check-ignore -q .gbuild/`; if true, don't
189
- stage anything under it). Message: `<slug>: chart graph`.
183
+ Commit `.gbuild/<slug>/` unless the repo ignores it (`git check-ignore -q .gbuild/`; if true, don't stage
184
+ anything under it). The store's own `.gitignore` limits that to `db/graph.json` and `db/.gitignore`.
185
+ Message: `<slug>: chart graph`.
190
186
 
191
- Report the branch, the wave decomposition, and the frontier. Close with `next: /gbuild:run <slug>`.
187
+ Report the branch, the waves, and the frontier. Close with `next: /gbuild:run <slug>`.
192
188
 
193
189
  ## Reopen
194
190
 
@@ -196,22 +192,40 @@ Report the branch, the wave decomposition, and the frontier. Close with `next: /
196
192
 
197
193
  0. Check out the feature's existing branch if you're not on it. Never chart a reopen onto a different
198
194
  branch than the one the graph was charted on.
199
- 1. Add a new `Acceptance` node (next `a-` id) + a `Feature -[:HAS_ACCEPTANCE]-> Acceptance` edge for the
200
- requirement.
201
- 2. Decompose whatever new GbuildNodes it needs, wire `DEPENDS_ON` edges against the *existing* graph (a
202
- new node may depend on an already-completed one — that's fine, its dependency is satisfied) and
203
- `SATISFIES` edges to the new (or existing) `Acceptance` nodes it covers. Assign the next available
204
- integer ids, bump `next_node_id`/`next_rel_id`. Re-run the checklist against the delta.
205
- 3. **Re-run `validate_format.py`** (counters, coverage, acyclicity all re-checked on the mutated graph),
206
- then `graph.py --status`. Commit (`<slug>: reopen graph — <what was added>`), report the new frontier.
195
+ 1. Read the current graph (`status.cypher`, and `node.cypher` for any node the requirement touches).
196
+ 2. Design the delta: a new `Acceptance` node (next `a-` id) linked from the `Feature`, and whatever new
197
+ GbuildNodes it needs, with their `Field`s. A new node may depend on an already-completed one — wire
198
+ its `INPUT` `FROM` that node's `OUTPUT` like any other; its value is already recorded. Re-run the
199
+ checklist against the delta.
200
+ 3. Write it as one script that `MATCH`es what it connects to and `CREATE`s the rest:
201
+
202
+ ```
203
+ cypherlite .gbuild/<slug>/db <<'CYPHER'
204
+ .begin
205
+ MATCH (f:Feature), (up:GbuildNode {slug: '<existing-slug>'})-[:OUTPUT]->(up_out:Field {name: '<field>'})
206
+ CREATE (f)-[:HAS_ACCEPTANCE]->(a:Acceptance {id: 'a-3', text: '...'})
207
+ CREATE (n:GbuildNode:Code {slug: '...', ..., status: 'pending'})
208
+ CREATE (n)-[:DEPENDS_ON]->(up)
209
+ CREATE (n)-[:INPUT]->(:Field {name: '...', shape: '<...>'})-[:FROM]->(up_out)
210
+ CREATE (n)-[:OUTPUT]->(:Field {name: '...', shape: '<...>'})
211
+ CREATE (n)-[:SATISFIES]->(a);
212
+ .commit
213
+ .checkpoint
214
+ CYPHER
215
+ ```
216
+
217
+ 4. **Re-run `validate.cypher`** until it prints `[]`, then `status.cypher`. Commit
218
+ (`<slug>: reopen graph — <what was added>`), report the new frontier.
207
219
 
208
220
  If the requirement needs no new nodes (an existing node's contract already covers it), say so and stop
209
221
  — don't manufacture graph churn.
210
222
 
211
223
  ## Rules
212
224
 
213
- - **Plan, don't do.** Nodes here get *defined*, not executed — even `research` nodes just write
214
- findings to their own checkpoint, they don't touch anything `run` is responsible for.
225
+ - **Plan, don't do.** Nodes here get *defined*, not executed — even `research` nodes. They land in the
226
+ first wave and `run` dispatches them.
227
+ - **The store is only touched through `cypherlite`**, from this skill, one command at a time
228
+ (`reference/cypher.md`).
215
229
  - **Acceptance criteria are the bar, not a formality.** A node with vague acceptance is a node
216
230
  `gbuild-reviewer` can't actually check — that's a planning failure, not something to fix later.
217
- - **Refer to nodes by title in anything the user reads.** IDs are for the graph, not the conversation.
231
+ - **Refer to nodes by title in anything the user reads.** Slugs are for the graph, not the conversation.
@@ -12,7 +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 most recently modified `.gbuild/*/graph.json`. 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 — 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).
16
+
17
+ When `<slug>` resolved, read `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md` and run its version check — every graph read below goes through `cypherlite`, one command at a time.
16
18
  - **max_rounds**: the value of `--max-rounds N` (also accept `--max-rounds=N`). Default `3` when absent.
17
19
 
18
20
  ## Step 1: Preconditions
@@ -25,7 +27,7 @@ Verify the environment before touching the remote. Stop with a clear, actionable
25
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.
26
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).
27
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.
28
- 7. **Graph finished.** When `<slug>` resolved, run `python3 ${CLAUDE_PLUGIN_ROOT}/scripts/graph.py .gbuild/<slug>/graph.json --status`. If the frontier is non-empty or nodes are still `in_progress`, warn that the graph isn't finished and ask whether to open the PR anyway. If any node is `failed`, name it — a PR built on an escalated node is usually premature.
30
+ 7. **Graph finished.** When `<slug>` resolved, run `cypherlite .gbuild/<slug>/db -json < ${CLAUDE_PLUGIN_ROOT}/cypher/status.cypher`. If any row has `state: frontier` or `in_progress`, warn that the graph isn't finished and ask whether to open the PR anyway. If any node is `failed`, name it — a PR built on an escalated node is usually premature.
29
31
 
30
32
  ## Step 2: Determine the remote & push
31
33
 
@@ -45,7 +47,7 @@ Gather context from:
45
47
 
46
48
  - Commit subjects: `git log <base>..HEAD --oneline`.
47
49
  - File overview: `git diff <base>...HEAD --stat`.
48
- - When present: the `Feature` node's `destination` property and the `Acceptance` nodes (linked `Feature -[:HAS_ACCEPTANCE]-> Acceptance`) from `.gbuild/<slug>/graph.json` (a CypherLite GraphData file), and the per-node `output` values in `.gbuild/<slug>/nodes/*.json` (keyed by GbuildNode `slug`).
50
+ - When `<slug>` resolved: `cypherlite .gbuild/<slug>/db -json < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher` — the `destination`, the acceptance bar (with the nodes covering each criterion), and every node's recorded output values.
49
51
 
50
52
  Derive a concise, conventional-commit-style title from the feature/branch and the changes (e.g. `feat(auth): add OAuth login`).
51
53
 
@@ -11,20 +11,28 @@ missed abstractions that only exist *between* nodes, which no per-node review ca
11
11
  ## Arguments
12
12
 
13
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 most
15
- recently modified `.gbuild/*/graph.json`. Do NOT write any file — this skill outputs to chat exclusively.
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.
16
17
 
17
- Run `python3 ${CLAUDE_PLUGIN_ROOT}/scripts/validate_format.py .gbuild/<slug>/graph.json` then
18
- `python3 ${CLAUDE_PLUGIN_ROOT}/scripts/graph.py .gbuild/<slug>/graph.json --status` first. If the frontier is
19
- non-empty or nodes are still `in_progress`, say the graph isn't finished and ask whether to audit anyway
20
- — auditing a half-built branch produces findings that the remaining nodes were going to address.
18
+ Read `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md` and run its version check. Then, one after the other,
19
+ run `validate.cypher` and `status.cypher` against `.gbuild/<slug>/db`. If the frontier is non-empty or
20
+ nodes are still `in_progress`, say the graph isn't finished and ask whether to audit anyway — auditing a
21
+ half-built branch produces findings that the remaining nodes were going to address.
21
22
 
22
23
  ## Run the audit
23
24
 
25
+ Fetch the feature context:
26
+
27
+ ```
28
+ cypherlite .gbuild/<slug>/db -json < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher
29
+ ```
30
+
24
31
  Launch the `gbuild-auditor` subagent (agents/gbuild-auditor.md — this plugin's own copy, gbuild does not
25
32
  depend on hone-ai being installed). Pass it:
26
33
 
27
- - The resolved slug and its `.gbuild/<slug>/graph.json` path.
34
+ - The resolved slug and the `feature.cypher` result verbatim (destination, acceptance bar with the nodes
35
+ covering each criterion, every node's recorded outputs) — the auditor never opens the store itself.
28
36
  - Tell it to audit the current branch.
29
37
 
30
38
  The sub-agent runs the full audit in its own fresh context and returns the audit as its final message.
@@ -1,99 +1,140 @@
1
1
  ---
2
- description: Executes a gbuild graph — dispatches every ready-and-independent node in the current wave concurrently, reviews each node's output with gbuild-reviewer before checkpointing it complete, and resumes only the remaining frontier on re-invocation. Use after /gbuild:plan has written graph.json.
2
+ description: Executes a gbuild graph — dispatches every ready-and-independent node in the current wave concurrently, reviews each node's output with gbuild-reviewer before 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 `.gbuild/$ARGUMENTS/graph.json` (the feature slug is `$ARGUMENTS`).
5
+ Run the graph in `.gbuild/$ARGUMENTS/db` (the feature slug is `$ARGUMENTS`).
6
6
 
7
7
  **CRITICAL: this skill must run in the main context, not inside a forked agent.** It dispatches its own
8
- subagents per node; a subagent cannot itself fan out further subagents reliably.
8
+ subagents per node; a subagent cannot itself fan out further subagents reliably. It is also the store's
9
+ **only writer**: subagents never run `cypherlite` — they get what they need in their prompt and report
10
+ back in their final message, and this skill records it. Read `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`
11
+ first and run its version check; `DB` below is `.gbuild/<feature>/db`.
9
12
 
10
13
  ## Step 0: Load and query
11
14
 
12
- Load `.gbuild/<feature>/graph.json` (a CypherLite GraphData file — nodes keyed by integer `id`, with a
13
- `slug` property; dependencies are `DEPENDS_ON` relationships, feature-level acceptance is `Acceptance`
14
- nodes linked by `HAS_ACCEPTANCE`/`SATISFIES`). **Confirm it's well-formed first:**
15
+ **Confirm the graph is well-formed:**
15
16
 
16
17
  ```
17
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/validate_format.py .gbuild/<feature>/graph.json
18
+ cypherlite DB -json < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
18
19
  ```
19
20
 
20
- If it prints `invalid: ...`, stop and report — do not attempt to run a graph `plan` didn't finish
21
- correctly. Then take the frontier:
21
+ Anything but `[]` → stop and report the rows; do not run a graph `plan` didn't finish correctly. Then:
22
22
 
23
23
  ```
24
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/graph.py .gbuild/<feature>/graph.json --status
24
+ cypherlite DB -json < ${CLAUDE_PLUGIN_ROOT}/cypher/status.cypher
25
25
  ```
26
26
 
27
- Take the `frontier` list (node **slugs**) from the status report. This is the set of nodes ready
28
- right now — not a judgement call, the query already excludes anything blocked, in-flight, or done.
29
-
30
- If `frontier` is empty and `completed` + `cancelled` covers every node, the graph is done — report that
31
- and stop. If `frontier` is empty but nodes remain incomplete, there's a stuck cluster (likely an
32
- `escalate`d or `stop`ped node) — report what's blocking per the last checkpoint and stop.
27
+ - **Rows with `state: in_progress`** at this point are left over from an interrupted run (this skill is
28
+ the only writer, and it records every node before finishing). Check `git log` for commits the node's
29
+ agent already made, reset each to `pending` with `set-status.cypher` (`--param 'status="pending"'`) so
30
+ it re-dispatches, and mention it in the report.
31
+ - The rows with `state: frontier` are the set ready right now — not a judgement call; the query already
32
+ excludes anything blocked, in flight, or done.
33
+ - If there is no frontier and every row is `completed` or `cancelled`, the graph is done — report that
34
+ and stop. If there is no frontier but nodes remain incomplete, there's a stuck cluster (likely an
35
+ `escalate`d or `stop`ped node, now `failed`) — report what's blocking and stop.
33
36
 
34
37
  ## Step 1: Establish VCS facts
35
38
 
36
- Default to `git`. `git check-ignore -q .gbuild/` tells you whether to stage anything under it at all.
39
+ Default to `git`. `git check-ignore -q .gbuild/` tells you whether to commit the store at all.
37
40
 
38
41
  ## Step 2: Pick a backend
39
42
 
40
43
  **Workflow tool**, if this session has opted into multi-agent orchestration (ultracode, or the user
41
- asked for a workflow explicitly) — translate `frontier` into a `pipeline()`/`parallel()` script: each
44
+ asked for a workflow explicitly) — translate the frontier into a `pipeline()`/`parallel()` script: each
42
45
  node is an `agent()` implement call, immediately followed by a `gbuild-reviewer` `agent()` review call
43
- scoped to that node's `contract.output` + `acceptance`, using `schema` for the pass/fail verdict. Code
44
- nodes touching files that could conflict with a concurrent sibling get `isolation: 'worktree'`.
45
- Controlled-cycle clusters become a bounded `while` loop per `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md`'s round cap. Route
46
- `model_tier: strong` nodes via `opts.model` where the harness supports an override.
46
+ scoped to that node's outputs + `acceptance`, using `schema` for the pass/fail verdict. Code nodes
47
+ touching files that could conflict with a concurrent sibling get `isolation: 'worktree'`.
48
+ Controlled-cycle clusters become a bounded `while` loop per `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md`'s
49
+ round cap. Route `model_tier: strong` nodes via `opts.model` where the harness supports an override. The
50
+ script never touches the store: collect every implement and review result, then record them from the
51
+ main context exactly as step 3.4 describes.
47
52
 
48
53
  **Fallback** (default — no opt-in required, and always what step 3 below assumes): fire one `Agent`
49
- tool call per node in the current `frontier`, **all in a single message** — this is what makes the fan-
50
- out real rather than claimed. Do not dispatch them one at a time across separate messages; that's
51
- exactly the serial behavior this plugin replaces.
52
-
53
- ## Step 3: Per-node dispatch (fallback backend)
54
-
55
- For every node in the current wave's `frontier`, in one message, spawn one `Agent` call per node:
56
-
57
- 1. **IMPLEMENT.** Give the agent the node's `slug`, `title`, `type`, `contract.input` (resolved: for
58
- each `DEPENDS_ON` target, read the actual `output` values from its checkpoint
59
- `.gbuild/<feature>/nodes/<dep-slug>.json`), and `acceptance`. Tell it to produce `contract.output`'s
60
- declared shape and, for `code`/`test`/`chore` nodes, to actually make the change in the working tree
61
- (not describe it) and commit it — message `<feature>/<slug>: <what changed>`.
62
- 2. **REVIEW.** Once IMPLEMENT returns, spawn a `gbuild-reviewer` agent (agents/gbuild-reviewer.md — this
63
- plugin's own copy, gbuild does not depend on hone-ai being installed) against the actual diff and
64
- output. Never let the implementing agent review its own work.
65
- 3. **PASS or FAIL:**
66
- - **Pass** (`VERDICT: pass`): write `.gbuild/<feature>/nodes/<slug>.json` with `status: completed`,
67
- the `output`, the review verdict, and timestamps.
68
- - **Fail**: apply the node's `failure_policy` (`${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`) — `retry`/`repair`
69
- re-dispatch (bounded, 2 attempts) with the reviewer's per-criterion failures fed back in;
70
- `fallback` dispatches the named alternate approach; `skip` checkpoints `cancelled`; `escalate`
71
- checkpoints `failed` and stops that node's downstream only; `stop` halts the entire run.
72
-
73
- Each node's checkpoint write is independent — nodes in the same wave never touch each other's files, so
74
- true concurrent dispatch is safe by construction.
54
+ tool call per frontier node, **all in a single message** — this is what makes the fan-out real rather
55
+ than claimed. Do not dispatch them one at a time across separate messages; that's exactly the serial
56
+ behavior this plugin replaces.
57
+
58
+ ## Step 3: One wave (fallback backend)
59
+
60
+ 1. **MARK.** Move the whole frontier to `in_progress` in one call and check that `dispatched` lists every
61
+ slug you passed:
62
+
63
+ ```
64
+ cypherlite DB -json --param 'slugs=["<slug>", ...]' < ${CLAUDE_PLUGIN_ROOT}/cypher/dispatch.cypher
65
+ ```
66
+
67
+ Then fetch each node's definition — `node.cypher` with `--param 'slug="<slug>"'`, one call per node,
68
+ one after the other. Its `inputs` are already resolved: each input read from another node carries
69
+ the upstream `value`.
70
+
71
+ 2. **IMPLEMENT.** In one message, spawn one `Agent` per node. Give each the node's `slug`, `title`,
72
+ `type`, `inputs` (name, shape, value — or for an input with no `from`, where to find it),
73
+ `outputs` (name, shape), and `acceptance`. Tell it to:
74
+ - actually do the work — for `code`/`test`/`chore` nodes make the change in the working tree (not
75
+ describe it) and commit it, message `<feature>/<slug>: <what changed>`;
76
+ - produce a value for every output field: a scalar or a list of scalars, JSON-encoding anything
77
+ nested into a string;
78
+ - record its own UTC start and finish (`date -u +%Y-%m-%dT%H:%M:%SZ`) and end its final message with
79
+ a JSON object `{"started_at": "...", "completed_at": "...", "outputs": {...}}`;
80
+ - **not** run `cypherlite` or touch `.gbuild/`.
81
+
82
+ 3. **REVIEW.** Once IMPLEMENT returns, spawn a `gbuild-reviewer` agent per node, again all in one message
83
+ (`agents/gbuild-reviewer.md` — this plugin's own copy, gbuild does not depend on hone-ai being
84
+ installed). Give it the node definition from `node.cypher`, the implementing agent's reported
85
+ outputs, and where the diff is. Never let the implementing agent review its own work.
86
+
87
+ 4. **RECORD** — sequentially, one `cypherlite` call at a time, after the agents returned. For each node:
88
+
89
+ ```
90
+ cypherlite DB -json --param 'slug="<slug>"' --param 'verdict="pass"' --param 'failed_criteria=[]' \
91
+ < ${CLAUDE_PLUGIN_ROOT}/cypher/record-review.cypher
92
+ ```
93
+
94
+ (`verdict="fail"` and the reviewer's failed criteria as `failed_criteria` on a fail.)
95
+
96
+ - **Pass** (`VERDICT: pass`): record it complete with the agent's outputs and times.
97
+
98
+ ```
99
+ cypherlite DB -json --param 'slug="<slug>"' --param 'outputs={...}' \
100
+ --param 'started_at="<iso>"' --param 'completed_at="<iso>"' < ${CLAUDE_PLUGIN_ROOT}/cypher/complete.cypher
101
+ ```
102
+
103
+ Check `completed: true`. If it is `false`, `missing_outputs` names the output fields the agent didn't
104
+ produce — treat that as a failed review with those as the failed criteria. An `InvalidPropertyType`
105
+ error means an output value was a map: JSON-encode it into a string and record again.
106
+ - **Fail**: apply the node's `failure_policy` (`${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`).
107
+ `retry`/`repair` re-dispatch implement → review (bounded: 2 attempts — `review_attempts` in
108
+ `status.cypher` counts them) with the reviewer's failed criteria fed back in; the node stays
109
+ `in_progress`. `fallback` dispatches the named alternate approach. `skip` sets it `cancelled`,
110
+ `escalate` sets it `failed` (its dependents stay blocked; siblings elsewhere keep going), both with
111
+ `set-status.cypher`. `stop` sets it `failed` and halts the entire run.
112
+
113
+ Nodes in the same wave never read each other's outputs, so true concurrent dispatch is safe by
114
+ construction — only the recording is serialized.
75
115
 
76
116
  ## Step 4: Next wave
77
117
 
78
- Re-run `graph.py --status`. If the wave that just finished unblocked new frontier nodes, repeat step 3
79
- for them. Keep going until `frontier` is empty (done, or stuck — see step 0's exit conditions).
118
+ If `.gbuild/` isn't ignored, commit the store: `git add .gbuild/<feature>/db` and
119
+ `git commit -m "<feature>: record wave <n>"`. Re-run `status.cypher`. If the wave that just finished
120
+ unblocked new frontier nodes, repeat step 3 for them. Keep going until there is no frontier (done, or
121
+ stuck — see step 0's exit conditions).
80
122
 
81
123
  ## Step 5: Report and stop
82
124
 
83
125
  Report: which nodes ran this invocation, which passed review outright vs. needed a repair pass, what's
84
- newly in the frontier or still blocked, and any `failed`/`escalate`d nodes needing a decision.
126
+ newly on the frontier or still blocked, and any `failed` nodes needing a decision.
85
127
 
86
128
  Close with the next step:
87
129
 
88
130
  - **Graph fully done** (every node `completed` or `cancelled`) → say so plainly, then
89
131
  `next: /gbuild:review <feature>` — the per-node reviews graded each node against its own contract;
90
132
  nothing has yet looked at what the accumulated diff did to the codebase.
91
- - **Work remains** (frontier non-empty, or nodes `failed`/`escalate`d awaiting a decision) →
133
+ - **Work remains** (frontier non-empty, or nodes `failed` awaiting a decision) →
92
134
  `next: /gbuild:status <feature>`.
93
135
 
94
136
  ## Resuming
95
137
 
96
- Re-invoking `/gbuild:run <feature>` after an interruption re-runs step 0 fresh: anything with a
97
- `completed` or `cancelled` checkpoint is excluded from `frontier` automatically, so only the genuinely
98
- remaining work re-dispatches. Nothing needs to be told what already finished — the checkpoint files are
99
- the memory.
138
+ Re-invoking `/gbuild:run <feature>` after an interruption re-runs step 0 fresh: completed and cancelled
139
+ nodes are excluded from the frontier automatically, and nodes an interrupted wave left `in_progress` are
140
+ reset and re-dispatched. Nothing needs to be told what already finished — the store is the memory.