opencode-gbuild 0.6.1 → 0.6.3

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
@@ -98,10 +98,12 @@ OpenCode (same order, kebab-case names):
98
98
  `FROM` edges carrying data from one node's output to the next node's input. Every dependency edge
99
99
  passes the cut test — and because data flow is explicit, the validator checks it; every node has
100
100
  mandatory concrete acceptance criteria. Written and validated entirely in Cypher.
101
- - **`run`** dispatches every node in the current ready wave concurrently — not one at a time — reviews
102
- each node's output with a dedicated `gbuild-reviewer` subagent before recording it complete, and
103
- applies the node's declared failure policy on a review failure. Re-invoking after an interruption
104
- resumes only the remaining frontier.
101
+ - **`run`** dispatches every node in the current ready wave concurrently — not one at a time — with
102
+ each file-touching node working in its own git worktree on its own branch, forked from the feature
103
+ 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.
105
+ Unaccepted work never lands. A review failure applies the node's declared failure policy. Re-invoking
106
+ after an interruption resumes only the remaining frontier, reusing the surviving worktrees.
105
107
  - **`status`** reports the graph — frontier, blocked, in-flight, completed, each node's review verdict,
106
108
  and evidence of what actually ran concurrently vs. serially.
107
109
  - **`review`** audits the finished branch as a whole in a `gbuild-auditor` subagent — abstraction
@@ -210,8 +212,8 @@ worked example through a full run, checks every validator rule and write guard,
210
212
 
211
213
  ```
212
214
  plugins/gbuild/
213
- agents/gbuild-reviewer.md # per-node review, ported from hone-ai's reviewer, never self-review
214
- agents/gbuild-auditor.md # end-of-branch maintainability audit, ported from hone-ai's auditor
215
+ agents/gbuild-reviewer.md # fast per-node gate — contract, acceptance, duplication; never self-review
216
+ agents/gbuild-auditor.md # end-of-branch audit: runs the test suite, then strict maintainability
215
217
  reference/ # graph-format.md (normative model), cypher.md (how to talk to the store), queries.md (ad-hoc recipes); shapes/failure-policies/cost-model/checklist inform plan
216
218
  cypher/ # schema, validate, progress/attention/status/node/feature reads, and run's dispatch/record/set-status
217
219
  templates/example.cypher # a worked 4-node diamond, written the way plan writes a feature
@@ -225,4 +227,6 @@ plugins/gbuild/
225
227
  State lives outside the plugin, in the project: one CypherLite store per feature at
226
228
  `.gbuild/<feature>/db/`. It holds the plan, every node's status and output values, and the full review
227
229
  history. Only `db/graph.json` (CypherLite's JSON snapshot, kept current by every write) and the store's
228
- own `.gitignore` are committed.
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).
@@ -9,6 +9,13 @@ Unusually strict review of implementation quality, maintainability, and abstract
9
9
 
10
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.
11
11
 
12
+ ## Run the branch's checks first
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
16
+ criteria — nobody has yet run the whole suite against the accumulated diff. A failing check is a
17
+ blocking finding: name the exact failures.
18
+
12
19
  ## Core Prompt
13
20
 
14
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.
@@ -138,6 +145,7 @@ Prefer a small number of high-conviction comments over many cosmetic nits.
138
145
 
139
146
  Don't approve merely because behavior seems correct, or because every node passed its own review — a graph of individually-passing nodes can still add up to a worse codebase. Required:
140
147
 
148
+ - the repo's test suite, typecheck, and lint (where defined) pass on the branch
141
149
  - no structural regression
142
150
  - no obvious missed simplification when a path is visible
143
151
  - no unjustified file-size explosion
@@ -1,12 +1,17 @@
1
1
  ---
2
2
  name: gbuild-reviewer
3
- description: Reviews a gbuild node's output against its contract and acceptance criteria. Runs after every node's implement pass, before it can be recorded complete. Never the same agent that implemented the node.
3
+ description: Fast per-node gate in a gbuild run — checks the node's outputs match their contract, its acceptance criteria are fulfilled, and the diff doesn't duplicate something the repo already has. Runs after every node's implement pass, before it can be recorded complete. Never the same agent that implemented the node.
4
4
  ---
5
5
 
6
6
  You are reviewing one gbuild node's output. You did not implement it — do not defer to the implementer's
7
7
  own account of what it did. Judge the actual diff and the actual output against the node's own
8
8
  declared bar.
9
9
 
10
+ Keep this fast — you are the per-node gate, not the final audit. Correctness depth beyond the
11
+ acceptance bar, security sweeps, performance, elegance, style, file size, and maintainability are
12
+ `gbuild-auditor`'s job in the end-of-feature review, which also runs the branch's test suite. Do not
13
+ flag them here. Three things belong to you: the contract, the acceptance criteria, and duplication.
14
+
10
15
  ## WHAT YOU'RE GIVEN
11
16
 
12
17
  - The node's definition, as the caller read it from the gbuild store: its `slug`, `title`, `type`,
@@ -22,59 +27,29 @@ store and records your verdict.
22
27
 
23
28
  ## REVIEW OBJECTIVE
24
29
 
25
- Two checks, in order:
30
+ Three checks, in order:
26
31
 
27
32
  1. **Does every output field have a value matching its declared `shape`?** A missing field, or a value
28
33
  that doesn't match its shape, is a fail regardless of the acceptance criteria — a downstream node
29
34
  reading it through `FROM` will break. Values must be scalars or lists of scalars; a nested value must
30
35
  be a JSON-encoded string.
31
36
  2. **Does the output satisfy every bullet in `acceptance`, one at a time?** Not "does it seem fine
32
- overall" — go bullet by bullet, pass or fail each one individually, cite why.
33
-
34
- ## REVIEW CHECKLIST (for `code`/`test` nodes, against the diff)
35
-
36
- 1. Correctness — does it actually do what the contract says, including edge cases the acceptance
37
- criteria imply.
38
- 2. Tests — present, meaningful, not just asserting the mock.
39
- 3. Security — no injection, no secrets committed, no unsafe deserialization, OWASP top 10.
40
- 4. Performance — no obviously pathological complexity for the data sizes implied by the contract.
41
- 5. Edge cases — nulls, empties, boundary values the contract's input shape allows.
42
- 6. Elegance — is this the simplest implementation that satisfies the contract, not a fancier one.
43
- 7. Cleanliness — no dead code, no leftover debug statements.
44
- 8. Structure — file/function placement matches the surrounding codebase's conventions.
45
- 9. Clarity — names and control flow read without needing the node's own prose to explain them.
46
- 10. Efficiency — no redundant work, no unnecessary re-computation.
47
- 11. Conventions — matches this repo's actual patterns, not a generic default.
48
- 12. Best-implementation check — would a competent reviewer's first suggestion be "do it this other way
49
- instead"? If so, say so as an Issue, not a Suggestion.
50
- 13. Code reuse — didn't reinvent something the repo already has.
51
- 14. Unnecessary comments — comments explaining *what* rather than a non-obvious *why* are flagged at
52
- **high priority**, same bar as any other issue.
53
-
54
- ## CODE SMELL BASELINE
55
-
56
- Judgement calls, not hard violations — documented repo conventions override this baseline. Each is a
57
- smell paired with its usual fix:
58
-
59
- - **Mysterious Name** → rename to something the reader doesn't need the node's contract to decode.
60
- - **Duplicated Code** → extract, but only if the repo doesn't already have a place for it.
61
- - **Feature Envy** → a function using another object's data more than its own belongs on that object.
62
- - **Data Clumps** → the same group of values passed together repeatedly should be one structure.
63
- - **Primitive Obsession** → a primitive standing in for a concept that has its own rules deserves a type.
64
- - **Repeated Switches** → the same conditional logic scattered across call sites belongs behind one
65
- abstraction.
66
- - **Shotgun Surgery** → one conceptual change requiring edits across many unrelated places.
67
- - **Divergent Change** → one module changing for many unrelated reasons.
68
- - **Speculative Generality** → machinery built for a future need the node's contract doesn't ask for.
69
- - **Message Chains** → `a.b.c.d` reaching through several objects to get one value.
70
- - **Middle Man** → a class that only delegates, doing nothing of its own.
71
- - **Refused Bequest** → a subclass that doesn't want most of what it inherits.
37
+ overall" — go bullet by bullet, pass or fail each one individually, cite why. If a bullet names
38
+ something runnable (a command, a specific test file), run just that rather than taking the diff's
39
+ word for it — but never the whole suite; that's the end-of-feature audit's job.
40
+ 3. **Does the diff duplicate something the repo already has?** Search for an existing helper, utility,
41
+ type, or pattern that already does what this node just built. If the node's core deliverable already
42
+ exists, that's a fail — cite the code it should have reused. A partial overlap is an Issue citing
43
+ the canonical code.
72
44
 
73
45
  ## GIT DIFF
74
46
 
75
47
  For `code`/`test`/`chore` nodes, always look at the actual diff before forming a verdict — the node's
76
- own account of what it did is not evidence. `git diff HEAD` for uncommitted work, `git diff --staged`
77
- if staged, `git log -1 -p` if this node already committed.
48
+ own account of what it did is not evidence. In a gbuild run the node worked on its own branch in its
49
+ own worktree: `git diff <feature-branch>...<node-branch>` shows the whole node diff (the caller names
50
+ both branches and gives you the worktree path — run any check `acceptance` names from there, not from
51
+ the main tree). Outside a run: `git diff HEAD` for uncommitted work, `git diff --staged` if staged,
52
+ `git log -1 -p` if this node already committed.
78
53
 
79
54
  ## FINDINGS THAT CONTRADICT AN UPSTREAM NODE
80
55
 
@@ -92,10 +67,14 @@ Lead with the acceptance checklist, one line per bullet:
92
67
  [fail] <criterion text> — <why>
93
68
  ```
94
69
 
70
+ For a duplication fail, the `[fail]` line names what was duplicated: `[fail] duplicates <existing code> —
71
+ <what should have been reused>`.
72
+
95
73
  Then, only if there are findings beyond acceptance: `Issue` or `Suggestion`, each with a `Priority`
96
74
  (`critical | high | medium | low`).
97
75
 
98
- Finish with one verdict line: `VERDICT: pass` only if every output field has a value matching its shape
99
- and every acceptance bullet passed — otherwise `VERDICT: fail`. A single failed acceptance bullet or an
100
- output shape mismatch is enough to fail the whole node, even if everything else is clean. The caller
101
- records each `[fail]` line's criterion as the review's failed criteria.
76
+ Finish with one verdict line: `VERDICT: pass` only if every output field has a value matching its shape,
77
+ every acceptance bullet passed, and the diff doesn't substantially duplicate existing repo code —
78
+ otherwise `VERDICT: fail`. A single failed acceptance bullet or an output shape mismatch is enough to
79
+ fail the whole node, even if everything else is clean. The caller records each `[fail]` line's criterion
80
+ as the review's failed criteria.
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.1",
4
+ "version": "0.6.3",
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,5 +1,5 @@
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 recording it complete, and resumes only the remaining frontier on re-invocation. Use after /gbuild:plan has charted the graph.
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
5
  Run the graph in `.gbuild/$ARGUMENTS/db` (the feature slug is `$ARGUMENTS`; `DB` below).
@@ -30,28 +30,39 @@ cypherlite DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/attention.cypher
30
30
  - `validate` printing anything → stop and report the rows; do not run a graph `plan` didn't finish
31
31
  correctly.
32
32
  - `attention` rows with reason `in_progress` are left over from an interrupted run (this skill is the
33
- only writer and records every node before finishing). Check `git log` for commits their agents already
34
- made, then reset them all in one call so they re-dispatch, and mention it in the report:
33
+ 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
35
+ means it never got that far) and leave the worktree in place: the re-dispatch reuses it. Then reset
36
+ them all in one call so they re-dispatch, and mention it in the report:
35
37
 
36
38
  ```
37
39
  cypherlite DB --mode jsonl --param 'slugs=["<slug>", ...]' --param 'status="pending"' \
38
40
  < ${CLAUDE_PLUGIN_ROOT}/cypher/set-status.cypher
39
41
  ```
40
42
 
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
45
+ (`git worktree remove --force`) and delete its branch (`git branch -D`). An unmerged branch is
46
+ unaccepted work, which must never land — that is what the isolation is for.
47
+
41
48
  ## Step 1: Establish VCS facts
42
49
 
43
- Default to `git`. `git check-ignore -q .gbuild/` tells you whether to commit the store at all.
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).
44
54
 
45
55
  ## Step 2: Pick a backend
46
56
 
47
57
  **Workflow tool**, if this session has opted into multi-agent orchestration (ultracode, or the user
48
58
  asked for a workflow explicitly) — translate each claimed wave into a `pipeline()`/`parallel()` script:
49
59
  each node is an `agent()` implement call, immediately followed by a `gbuild-reviewer` `agent()` review
50
- call scoped to that node's outputs + `acceptance`, using `schema` for the pass/fail verdict. Code nodes
51
- touching files that could conflict with a concurrent sibling get `isolation: 'worktree'`.
60
+ call scoped to that node's outputs + `acceptance`, using `schema` for the pass/fail verdict. Every
61
+ file-touching (`code`/`test`/`chore`) node gets `isolation: 'worktree'`.
52
62
  Controlled-cycle clusters become a bounded `while` loop per `${CLAUDE_PLUGIN_ROOT}/reference/shapes.md`'s
53
63
  round cap. Route `model_tier: strong` nodes via `opts.model` where the harness supports an override. The
54
- script never touches the store: collect the results and record them from the main context (step 3.4).
64
+ 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).
55
66
 
56
67
  **Fallback** (default — no opt-in required, and always what step 3 below assumes): fire one `Agent`
57
68
  tool call per claimed node, **all in a single message** — this is what makes the fan-out real rather
@@ -71,9 +82,28 @@ behavior this plugin replaces.
71
82
  `inputs` (an input with a `value` came from an upstream node; one without is external — its `shape`
72
83
  says where to find it), and `outputs`. **No rows** → nothing is ready: go to step 5.
73
84
 
74
- 2. **IMPLEMENT.** In one message, spawn one `Agent` per claimed node with its row. Tell it to:
75
- - actually do the work — for `code`/`test`/`chore` nodes make the change in the working tree (not
76
- describe it) and commit it, message `<feature>/<slug>: <what changed>`;
85
+ 2. **IMPLEMENT.** First, give every claimed `code`/`test`/`chore` node its own worktree — one shell
86
+ loop over the wave's slugs, idempotent so an interrupted or re-dispatched node keeps its earlier
87
+ work (`research`/`decision` nodes touch no files and get no worktree):
88
+
89
+ ```
90
+ mkdir -p WT
91
+ git worktree list --porcelain | grep -q "^worktree .*/<slug>$" \
92
+ || git worktree add WT/<slug> -b gbuild/<feature>/<slug> BRANCH 2>/dev/null \
93
+ || git worktree add WT/<slug> gbuild/<feature>/<slug>
94
+ ```
95
+
96
+ (skip if the worktree already exists; else fork a fresh branch off `BRANCH`; if the branch already
97
+ exists from an earlier attempt, attach to it instead — nothing is redone). Then, in one message,
98
+ spawn one `Agent` per claimed node with its row. Tell it to:
99
+ - actually do the work — for `code`/`test`/`chore` nodes make the change **in its worktree**
100
+ (`WT/<slug>`), never the main working tree: every edit, every check, and every git command runs
101
+ there, and it commits on the node branch, message `<feature>/<slug>: <what changed>`. A worktree
102
+ is a fresh checkout — if the project needs installed dependencies to build or test
103
+ (`node_modules`, `.venv`, …), install or symlink them in the worktree first;
104
+ - before finishing, run the checks relevant to its change — anything the node's `acceptance` names,
105
+ and for `code`/`test` nodes the tests covering the files it touched — and leave them green. The
106
+ per-node review will not re-run these; the full suite runs in the end-of-feature audit;
77
107
  - produce a value for every output field: a scalar or a list of scalars, JSON-encoding anything
78
108
  nested into a string;
79
109
  - record its own UTC start and finish (`date -u +%Y-%m-%dT%H:%M:%SZ`) and end its final message with
@@ -82,10 +112,27 @@ behavior this plugin replaces.
82
112
 
83
113
  3. **REVIEW.** Once IMPLEMENT returns, spawn a `gbuild-reviewer` per node, again all in one message
84
114
  (`agents/gbuild-reviewer.md` — this plugin's own copy, gbuild does not depend on hone-ai being
85
- installed). Give it the node's row, the agent's reported outputs, and where the diff is. Never let
86
- the implementing agent review its own work.
115
+ installed). Give it the node's row, the agent's reported outputs, and where the diff is — for a
116
+ worktree'd node that is its branch, `git diff BRANCH...gbuild/<feature>/<slug>`, plus the worktree
117
+ path so it can run just the checks `acceptance` names there (the worktree still exists at this
118
+ point). Never let the implementing agent review its own work.
119
+
120
+ 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:
122
+
123
+ ```
124
+ git merge --no-ff gbuild/<feature>/<slug> \
125
+ && git worktree remove --force WT/<slug> \
126
+ && git branch -d gbuild/<feature>/<slug>
127
+ ```
128
+
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
131
+ (a retry/repair re-dispatch tells the agent to rebase its branch onto `BRANCH` and resolve it).
132
+ Worktrees and branches are deleted only here, after acceptance — a failed node's stays for its next
133
+ attempt (step 6).
87
134
 
88
- 4. **RECORD** the whole wave in one call — one result per node, `verdict` from the reviewer's
135
+ 5. **RECORD** the whole wave in one call — one result per node, `verdict` from the reviewer's
89
136
  `VERDICT:` line, `failed_criteria` from its `[fail]` lines:
90
137
 
91
138
  ```
@@ -100,20 +147,26 @@ behavior this plugin replaces.
100
147
  recorded (it wasn't `in_progress`) — find out why before moving on. `InvalidPropertyType` means an
101
148
  output value was a map; the whole batch was rejected — JSON-encode it and send the batch again.
102
149
 
103
- 5. **FAILURES.** For each row with `completed: false` (a failed review, or a pass `record` downgraded
104
- because `missing_outputs` is non-empty), apply the node's `failure_policy`
150
+ 6. **FAILURES.** For each row with `completed: false` (a failed review, an aborted merge, or a pass
151
+ `record` downgraded because `missing_outputs` is non-empty), apply the node's `failure_policy`
105
152
  (`${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`), using its `attempt` as the count:
106
153
  - `retry` / `repair` → re-dispatch implement → review with the failed criteria fed back in, reusing
107
- the row you already hold, then record just those nodes. Bounded: 2 attempts.
108
- - `fallback` → dispatch the named alternate approach the same way.
154
+ the row you already hold **and the node's existing worktree and branch** — the next attempt builds
155
+ on them, it does not start over. Then record just those nodes. Bounded: 2 attempts.
156
+ - `fallback` → dispatch the named alternate approach the same way, but remove the failed attempt's
157
+ worktree and branch first (`git worktree remove --force`, `git branch -D`) — the alternate
158
+ approach starts from a fresh branch off `BRANCH`.
109
159
  - `skip` → `cancelled`; `escalate` (or an exhausted retry/repair) → `failed`, its dependents stay
110
160
  blocked while siblings elsewhere keep going; `stop` → `failed` and halt the entire run. One
111
- `set-status.cypher` call per status, with every slug that gets it.
161
+ `set-status.cypher` call per status, with every slug that gets it. A node reaching a terminal
162
+ status gets cleaned up: `git worktree remove --force WT/<slug>` and
163
+ `git branch -D gbuild/<feature>/<slug>` — its work was never accepted, so it never lands.
112
164
  - A completed router (`decision` choosing a branch) → cancel the unselected branches with
113
165
  `set-status.cypher` before the next claim.
114
166
 
115
- Nodes in the same wave never read each other's outputs, so true concurrent dispatch is safe by
116
- construction — only the store calls are serialized.
167
+ 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.
117
170
 
118
171
  ## Step 4: Next wave
119
172
 
@@ -142,5 +195,6 @@ Report: which nodes ran this invocation, which passed review outright vs. needed
142
195
  ## Resuming
143
196
 
144
197
  Re-invoking `/gbuild:run <feature>` after an interruption re-runs step 0 fresh: nodes an interrupted
145
- wave left `in_progress` are reset, and the next claim picks up exactly the remaining frontier. Nothing
146
- needs to be told what already finished — the store is the memory.
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.