opencode-gbuild 0.6.2 → 0.6.4
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 +9 -5
- package/agents/gbuild-reviewer.md +20 -10
- package/package.json +1 -1
- package/skills/run/SKILL.md +73 -22
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 —
|
|
102
|
-
each node
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
@@ -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).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gbuild-reviewer
|
|
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
|
|
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 neither duplicates something the repo already has nor adds a third copy where an abstraction is due. 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
|
|
@@ -37,16 +37,25 @@ Three checks, in order:
|
|
|
37
37
|
overall" — go bullet by bullet, pass or fail each one individually, cite why. If a bullet names
|
|
38
38
|
something runnable (a command, a specific test file), run just that rather than taking the diff's
|
|
39
39
|
word for it — but never the whole suite; that's the end-of-feature audit's job.
|
|
40
|
-
3. **Does the diff duplicate
|
|
41
|
-
type, or pattern that already does what this node just built. If the
|
|
42
|
-
exists, that's a fail — cite the code it should have reused. A
|
|
43
|
-
the canonical code.
|
|
40
|
+
3. **Does the diff duplicate functionality, or add a copy where an abstraction is due?** Search for an
|
|
41
|
+
existing helper, utility, type, or pattern that already does what this node just built. If the
|
|
42
|
+
node's core deliverable already exists, that's a fail — cite the code it should have reused. A
|
|
43
|
+
partial overlap is an Issue citing the canonical code. Then count copies: if this diff adds a
|
|
44
|
+
**third** copy of the same logic (the other two anywhere in the repo, elsewhere in the diff, or one
|
|
45
|
+
of each), that's a missed abstraction and a fail — name all three sites and the shared helper that
|
|
46
|
+
should own the logic. Two copies can stand; the third is where extracting beats copying. The
|
|
47
|
+
counterweight is just as binding: YAGNI. Judge only copies that actually exist in the code today —
|
|
48
|
+
never fault the diff for lacking an abstraction that only hypothetical future callers would use,
|
|
49
|
+
and never treat a single concrete implementation left un-abstracted as a finding.
|
|
44
50
|
|
|
45
51
|
## GIT DIFF
|
|
46
52
|
|
|
47
53
|
For `code`/`test`/`chore` nodes, always look at the actual diff before forming a verdict — the node's
|
|
48
|
-
own account of what it did is not evidence.
|
|
49
|
-
|
|
54
|
+
own account of what it did is not evidence. In a gbuild run the node worked on its own branch in its
|
|
55
|
+
own worktree: `git diff <feature-branch>...<node-branch>` shows the whole node diff (the caller names
|
|
56
|
+
both branches and gives you the worktree path — run any check `acceptance` names from there, not from
|
|
57
|
+
the main tree). Outside a run: `git diff HEAD` for uncommitted work, `git diff --staged` if staged,
|
|
58
|
+
`git log -1 -p` if this node already committed.
|
|
50
59
|
|
|
51
60
|
## FINDINGS THAT CONTRADICT AN UPSTREAM NODE
|
|
52
61
|
|
|
@@ -65,13 +74,14 @@ Lead with the acceptance checklist, one line per bullet:
|
|
|
65
74
|
```
|
|
66
75
|
|
|
67
76
|
For a duplication fail, the `[fail]` line names what was duplicated: `[fail] duplicates <existing code> —
|
|
68
|
-
<what should have been reused
|
|
77
|
+
<what should have been reused>`, or for a missed abstraction: `[fail] third copy of <logic> (<site 1>,
|
|
78
|
+
<site 2>, this diff) — extract a shared <helper>`.
|
|
69
79
|
|
|
70
80
|
Then, only if there are findings beyond acceptance: `Issue` or `Suggestion`, each with a `Priority`
|
|
71
81
|
(`critical | high | medium | low`).
|
|
72
82
|
|
|
73
83
|
Finish with one verdict line: `VERDICT: pass` only if every output field has a value matching its shape,
|
|
74
|
-
every acceptance bullet passed, and the diff
|
|
75
|
-
otherwise `VERDICT: fail`. A single failed acceptance bullet or an output shape mismatch is enough to
|
|
84
|
+
every acceptance bullet passed, and the diff neither substantially duplicates existing repo code nor adds
|
|
85
|
+
a third copy where an abstraction is due — otherwise `VERDICT: fail`. A single failed acceptance bullet or an output shape mismatch is enough to
|
|
76
86
|
fail the whole node, even if everything else is clean. The caller records each `[fail]` line's criterion
|
|
77
87
|
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.
|
|
4
|
+
"version": "0.6.4",
|
|
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",
|
package/skills/run/SKILL.md
CHANGED
|
@@ -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
|
|
34
|
-
|
|
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.
|
|
51
|
-
touching
|
|
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
|
|
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,25 @@ 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.**
|
|
75
|
-
|
|
76
|
-
|
|
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;
|
|
77
104
|
- before finishing, run the checks relevant to its change — anything the node's `acceptance` names,
|
|
78
105
|
and for `code`/`test` nodes the tests covering the files it touched — and leave them green. The
|
|
79
106
|
per-node review will not re-run these; the full suite runs in the end-of-feature audit;
|
|
@@ -85,10 +112,27 @@ behavior this plugin replaces.
|
|
|
85
112
|
|
|
86
113
|
3. **REVIEW.** Once IMPLEMENT returns, spawn a `gbuild-reviewer` per node, again all in one message
|
|
87
114
|
(`agents/gbuild-reviewer.md` — this plugin's own copy, gbuild does not depend on hone-ai being
|
|
88
|
-
installed). Give it the node's row, the agent's reported outputs, and where the diff is
|
|
89
|
-
|
|
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).
|
|
90
134
|
|
|
91
|
-
|
|
135
|
+
5. **RECORD** the whole wave in one call — one result per node, `verdict` from the reviewer's
|
|
92
136
|
`VERDICT:` line, `failed_criteria` from its `[fail]` lines:
|
|
93
137
|
|
|
94
138
|
```
|
|
@@ -103,20 +147,26 @@ behavior this plugin replaces.
|
|
|
103
147
|
recorded (it wasn't `in_progress`) — find out why before moving on. `InvalidPropertyType` means an
|
|
104
148
|
output value was a map; the whole batch was rejected — JSON-encode it and send the batch again.
|
|
105
149
|
|
|
106
|
-
|
|
107
|
-
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`
|
|
108
152
|
(`${CLAUDE_PLUGIN_ROOT}/reference/failure-policies.md`), using its `attempt` as the count:
|
|
109
153
|
- `retry` / `repair` → re-dispatch implement → review with the failed criteria fed back in, reusing
|
|
110
|
-
the row you already hold
|
|
111
|
-
|
|
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`.
|
|
112
159
|
- `skip` → `cancelled`; `escalate` (or an exhausted retry/repair) → `failed`, its dependents stay
|
|
113
160
|
blocked while siblings elsewhere keep going; `stop` → `failed` and halt the entire run. One
|
|
114
|
-
`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.
|
|
115
164
|
- A completed router (`decision` choosing a branch) → cancel the unselected branches with
|
|
116
165
|
`set-status.cypher` before the next claim.
|
|
117
166
|
|
|
118
|
-
Nodes in the same wave never read each other's outputs,
|
|
119
|
-
construction —
|
|
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.
|
|
120
170
|
|
|
121
171
|
## Step 4: Next wave
|
|
122
172
|
|
|
@@ -145,5 +195,6 @@ Report: which nodes ran this invocation, which passed review outright vs. needed
|
|
|
145
195
|
## Resuming
|
|
146
196
|
|
|
147
197
|
Re-invoking `/gbuild:run <feature>` after an interruption re-runs step 0 fresh: nodes an interrupted
|
|
148
|
-
wave left `in_progress` are reset
|
|
149
|
-
|
|
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.
|