opencode-gbuild 0.7.1 → 0.9.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.
- package/README.md +72 -33
- package/agents/gbuild-auditor.md +1 -1
- package/agents/gbuild-implementer.md +186 -0
- package/agents/gbuild-reviewer.md +113 -53
- package/cypher/attention.cypher +1 -1
- package/cypher/brief.cypher +39 -0
- package/cypher/check-result.cypher +19 -0
- package/cypher/dispatch.cypher +9 -27
- package/cypher/feature.cypher +1 -1
- package/cypher/node.cypher +5 -4
- package/cypher/progress.cypher +1 -1
- package/cypher/record.cypher +81 -29
- package/cypher/schema.cypher +20 -2
- package/cypher/set-status.cypher +1 -2
- package/cypher/status.cypher +10 -5
- package/cypher/symbols.cypher +9 -0
- package/cypher/validate.cypher +21 -6
- package/index.ts +19 -11
- package/package.json +1 -1
- package/reference/checklist.md +1 -1
- package/reference/cypher.md +51 -36
- package/reference/graph-format.md +62 -22
- package/reference/queries.md +7 -3
- package/skills/plan/SKILL.md +26 -33
- package/skills/pr/SKILL.md +7 -5
- package/skills/review/SKILL.md +8 -7
- package/skills/run/SKILL.md +154 -117
- package/skills/status/SKILL.md +26 -17
- package/templates/example.cypher +1 -2
package/reference/cypher.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Talking to the gbuild store
|
|
2
2
|
|
|
3
|
-
Every gbuild skill reads and writes the feature store (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Every gbuild skill reads and writes the feature store (modelled in `reference/graph-format.md`) with the
|
|
4
|
+
`cypherlite` CLI and nothing else. `DB` throughout is the store path:
|
|
5
|
+
`<git-common-dir>/gbuild/<feature>/db` — beside the feature worktree, machine-local, never committed.
|
|
6
|
+
`INBOX` is `<git-common-dir>/gbuild/<feature>/inbox`, beside it: where `run`'s agents leave their result
|
|
7
|
+
files for `record.cypher` to read. This file is the full operating manual; the skills that only run
|
|
8
|
+
plugin scripts carry the short version of these rules inline.
|
|
7
9
|
|
|
8
10
|
## Requirement
|
|
9
11
|
|
|
@@ -23,42 +25,45 @@ require the explicit `query` verb. The old path-first form — database as the f
|
|
|
23
25
|
|
|
24
26
|
## One writer, one command at a time
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
record
|
|
32
|
-
- **
|
|
33
|
-
|
|
34
|
-
-
|
|
28
|
+
The store is a directory store. A writable open takes its exclusive write lock; a `--read-only` open
|
|
29
|
+
does not, so read-only queries open the store beside a writer. Only a second writer fails, with
|
|
30
|
+
`storage locked` (exit 5). So:
|
|
31
|
+
|
|
32
|
+
- **Only the skill running in the main context writes to the store.** It is the only writer: schema,
|
|
33
|
+
dispatch, record, set-status and authored writes run there and nowhere else.
|
|
34
|
+
- **Subagents may run read-only queries, never writes.** `run`'s implementers and reviewers read their
|
|
35
|
+
own brief (`brief.cypher`), the helper registry (`symbols.cypher`) and their result check
|
|
36
|
+
(`check-result.cypher`), always with `--read-only` — their agent files list exactly what they run.
|
|
37
|
+
They report through files in `INBOX` plus a one-line reply; the orchestrator records them.
|
|
38
|
+
- **One `cypherlite` command at a time from the main context** — no parallel tool calls on the store.
|
|
39
|
+
Fan-out is for agents, not for store writes.
|
|
40
|
+
- On `storage locked` (exit 5), another writer holds the store (another gbuild command, a REPL left
|
|
35
41
|
open). Wait a few seconds and retry, up to 3 times; then stop and report. Never delete
|
|
36
42
|
`.cypherlite.lock`.
|
|
37
43
|
|
|
38
44
|
## Invocation
|
|
39
45
|
|
|
40
46
|
Always pass `--mode jsonl`: one compact JSON object per row, one row per line, and no output at all when
|
|
41
|
-
there are no rows.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
there are no rows. Every call is `cypherlite query [STATEMENT] --database DB …`; with no STATEMENT the
|
|
48
|
+
piped stdin is the script list. Pure reads add `--read-only`: no exit flush, no lock (it opens beside
|
|
49
|
+
a writer, as above), and a fail-fast on a wrong path — a read-only call never creates a missing store.
|
|
50
|
+
Writes (schema creation, dispatch, record, set-status, authored heredocs) go without.
|
|
45
51
|
|
|
46
52
|
```
|
|
47
|
-
# a plugin script
|
|
48
|
-
cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
|
|
53
|
+
# a plugin read script
|
|
54
|
+
cypherlite query --read-only --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
|
|
49
55
|
|
|
50
|
-
# a plugin script with parameters
|
|
51
|
-
cypherlite query --database DB --mode jsonl --param 'slug="b-consume-doubled"' < ${CLAUDE_PLUGIN_ROOT}/cypher/node.cypher
|
|
56
|
+
# a plugin read script with parameters
|
|
57
|
+
cypherlite query --read-only --database DB --mode jsonl --param 'slug="b-consume-doubled"' < ${CLAUDE_PLUGIN_ROOT}/cypher/node.cypher
|
|
52
58
|
|
|
53
59
|
# an ad-hoc read — return only the columns you need, and keep a LIMIT on anything that can grow
|
|
54
|
-
cypherlite query "MATCH (n:GbuildNode {status: 'failed'}) RETURN n.slug LIMIT 20" --database DB --mode jsonl
|
|
60
|
+
cypherlite query "MATCH (n:GbuildNode {status: 'failed'}) RETURN n.slug LIMIT 20" --read-only --database DB --mode jsonl
|
|
55
61
|
|
|
56
62
|
# a write you author (plan, reopen, fixes) — quoted heredoc delimiter, so the shell expands nothing
|
|
57
63
|
cypherlite query --database DB <<'CYPHER'
|
|
58
64
|
.begin
|
|
59
65
|
MATCH (n:GbuildNode {slug: 'b-consume-doubled'}) SET n.title = 'Consume the value, double it';
|
|
60
66
|
.commit
|
|
61
|
-
.checkpoint
|
|
62
67
|
CYPHER
|
|
63
68
|
```
|
|
64
69
|
|
|
@@ -70,11 +75,13 @@ CYPHER
|
|
|
70
75
|
- **Parameters are JSON.** Every `--param` value is JSON, single-quoted for the shell: strings in double
|
|
71
76
|
quotes (`--param 'slug="01-scan-runners"'`), lists and objects as JSON (`--param 'slugs=["a","b"]'`).
|
|
72
77
|
Bare values that start with a digit — timestamps, `01-…` slugs — are rejected, so always quote.
|
|
78
|
+
`--fetch-params NAME=<file>` binds a JSON file as `$NAME` instead — how `record` and `check-result`
|
|
79
|
+
read inbox files without anyone retyping them.
|
|
73
80
|
- **String literals** in Cypher you author: single quotes by default; double quotes when the text
|
|
74
81
|
contains an apostrophe (`"gbuild's"`); backslash-escape (`\'`, `\"`) when it contains both.
|
|
75
|
-
- **Authored writes are atomic
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
- **Authored writes are atomic.** Wrap them in `.begin` / `.commit` — if any statement fails, the whole
|
|
83
|
+
transaction is discarded. The plugin write scripts already do. Never write `.checkpoint`: a writable
|
|
84
|
+
session checkpoints on exit by itself.
|
|
78
85
|
- **Values are scalars or lists of scalars.** CypherLite cannot store a map as a property value;
|
|
79
86
|
JSON-encode anything nested into a string.
|
|
80
87
|
- **Read the result.** The plugin write scripts are guarded and return what they actually changed.
|
|
@@ -92,21 +99,26 @@ CYPHER
|
|
|
92
99
|
|
|
93
100
|
## Creating a store
|
|
94
101
|
|
|
95
|
-
Only `plan` does this, once per feature
|
|
96
|
-
it just created:
|
|
102
|
+
Only `plan` does this, once per feature:
|
|
97
103
|
|
|
98
104
|
```
|
|
99
105
|
mkdir -p DB
|
|
100
106
|
cypherlite query --database DB --snapshot-format json < ${CLAUDE_PLUGIN_ROOT}/cypher/schema.cypher
|
|
101
|
-
printf '*\n!.gitignore\n!graph.json\n' > DB/.gitignore
|
|
102
107
|
```
|
|
103
108
|
|
|
104
109
|
`--snapshot-format json` belongs on this first call only (it applies to an empty directory). It installs
|
|
105
|
-
the schema constraints and pins the JSON codec
|
|
110
|
+
the schema constraints and pins the JSON codec for cross-build compatibility: a parquet-enabled
|
|
111
|
+
CypherLite build defaults a new store to Parquet, and a JSON-pinned store opens in every build.
|
|
112
|
+
|
|
113
|
+
## Migrating an old store
|
|
114
|
+
|
|
115
|
+
Stores charted by gbuild ≤ 0.7.1 live at `<tree>/.gbuild/<slug>/db` and are git-tracked. Migrate with
|
|
116
|
+
`git rm -r --cached .gbuild` (commit the removal) and
|
|
117
|
+
`mv <tree>/.gbuild/<slug>/db <git-common-dir>/gbuild/<slug>/db`.
|
|
106
118
|
|
|
107
119
|
## Script catalog
|
|
108
120
|
|
|
109
|
-
All in `${CLAUDE_PLUGIN_ROOT}/cypher/`.
|
|
121
|
+
All in `${CLAUDE_PLUGIN_ROOT}/cypher/`.
|
|
110
122
|
|
|
111
123
|
| script | params | returns | used by |
|
|
112
124
|
| -------------------- | ------------------------- | -------------------------------------------------------------- | ------- |
|
|
@@ -114,12 +126,15 @@ All in `${CLAUDE_PLUGIN_ROOT}/cypher/`. Writes end with `.checkpoint`.
|
|
|
114
126
|
| `validate.cypher` | — | one row per violation (`check`, `detail`); no rows = valid | every skill |
|
|
115
127
|
| `progress.cypher` | — | one row per wave: `total`, counts per state, `open` (slugs not yet completed or cancelled) | every skill |
|
|
116
128
|
| `attention.cypher` | — | only nodes needing a decision: `slug`, `reason` (`failed` / `in_progress` / `blocked-by-failed`), `detail` | run, status |
|
|
117
|
-
| `dispatch.cypher` | — | claims
|
|
118
|
-
| `
|
|
129
|
+
| `dispatch.cypher` | — | claims every frontier node (→ `in_progress`); per claimed node only the routing columns `slug`, `type`, `model_tier`, `failure_policy` — agents read the rest with `brief` | run (at start, and after every record / set-status) |
|
|
130
|
+
| `brief.cypher` | `slug` | one node's brief: `slug`, `title`, `type`, `acceptance`, `verify`, `inputs` (`name`, `shape`, `from`, `value`), `outputs` (`name`, `shape`), `failed_criteria` (the latest review's when it failed, else `[]`), `notes` (`topic`, `text`, `by` — every transitive upstream node's notes plus setup and testing notes from any node, upstream first, then newest, at most 20) | run's implementer and reviewer (read-only) |
|
|
131
|
+
| `check-result.cypher` | `slug`; `result` (`--fetch-params result=INBOX/<slug>.json`) | `slug`, `missing_outputs`, `invalid_outputs`, `unknown_outputs`, `has_times` — clean when the three lists are `[]` and `has_times` is `true` | run's implementer and reviewer (read-only) |
|
|
132
|
+
| `record.cypher` | `verdict` — `{slug, verdict, failed_criteria}`; `result` — `--fetch-params result=INBOX/<slug>.json` on a pass whose file exists, else `{}`; `review` — `--fetch-params review=INBOX/<slug>.review.json` when it exists, else `{}` (all three always bound) | one call per node: `slug`, `attempt`, `verdict`, `completed`, `missing_outputs`, `notes_added`, `symbols_added`, `unselected`; the `Review` keeps the review file's `checklist`, and a completing pass stores the result file's outputs, times, `notes` (as `Note`) and `symbols` (as `Symbol`), skipping notes and symbols already stored | run |
|
|
133
|
+
| `symbols.cypher` | — | `name`, `file`, `purpose`, `by` — every completed node's registered helpers, by file and name, at most 100 | run's implementer and reviewer (read-only) |
|
|
119
134
|
| `set-status.cypher` | `slugs` (list), `status` (`pending` / `in_progress` / `failed` / `cancelled`) | `slug`, `status` per changed node | run |
|
|
120
|
-
| `status.cypher` | — | one row per node: `state`, `wave`, `waiting_on`, review summary, timestamps | status (human report) |
|
|
121
|
-
| `node.cypher` | `slug` | one node in full: properties, `inputs` (with `from`), `outputs`, `depends_on`, `satisfies`, `reviews` | plan (reopen), ad hoc |
|
|
135
|
+
| `status.cypher` | — | one row per node: `state`, `wave`, `waiting_on`, review summary, timestamps, `last_dependency_at` (latest `completed_at` among its completed dependencies, null without any) | status (human report) |
|
|
136
|
+
| `node.cypher` | `slug` | one node in full: properties, `inputs` (with `from`), `outputs`, `depends_on`, `satisfies`, `reviews` (`attempt`, `verdict`, `failed_criteria`, `checklist`, `at`) | plan (reopen), ad hoc |
|
|
122
137
|
| `feature.cypher` | — | `destination`, `context`, `out_of_scope`, `acceptance` (with `covered_by`) | review, pr |
|
|
123
138
|
|
|
124
|
-
Ready-made ad-hoc queries (lineage, critical path, repair history, …) are in
|
|
139
|
+
Ready-made ad-hoc queries (lineage, critical path, repair history, notes by topic, …) are in
|
|
125
140
|
`${CLAUDE_PLUGIN_ROOT}/reference/queries.md`.
|
|
@@ -2,12 +2,12 @@
|
|
|
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:
|
|
6
|
-
(`<git-common-dir>/gbuild/<feature>/tree
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
One CypherLite store per feature: `<git-common-dir>/gbuild/<feature>/db/`, beside the feature worktree
|
|
6
|
+
(`<git-common-dir>/gbuild/<feature>/tree/`, which `plan` creates) — untracked, machine-local, never
|
|
7
|
+
committed. `plan` creates and charts it, `run` records progress in it, and every skill reads it, as do
|
|
8
|
+
`run`'s agents (read-only) — **all through Cypher**. No skill reads or writes `graph.json` directly; it
|
|
9
|
+
is CypherLite's local snapshot of the store, not a gbuild artifact. How to talk to the store (the single-writer rule, invocation, quoting,
|
|
10
|
+
the script catalog) is in `reference/cypher.md`.
|
|
11
11
|
|
|
12
12
|
`templates/example.cypher` is a complete worked example — a 4-node diamond — written exactly the way
|
|
13
13
|
`plan` writes a new feature.
|
|
@@ -15,17 +15,25 @@ invocation, quoting, the script catalog) is in `reference/cypher.md`.
|
|
|
15
15
|
## Store layout
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
<git-common-dir>/gbuild/<feature>/
|
|
19
|
+
db/ the store
|
|
20
|
+
graph.json CypherLite JSON snapshot — the engine's local snapshot; gbuild never reads or writes it
|
|
21
|
+
manifest.json engine files
|
|
22
|
+
wal/
|
|
23
|
+
.cypherlite.lock
|
|
24
|
+
inbox/ run's hand-off files: <slug>.json (implementer result), <slug>.review.json (reviewer checklist)
|
|
25
|
+
tree/ the feature worktree
|
|
26
|
+
<slug>/ a file-touching node's worktree, during run
|
|
24
27
|
```
|
|
25
28
|
|
|
26
|
-
The
|
|
27
|
-
`
|
|
28
|
-
|
|
29
|
+
The inbox is not part of the store: `run` empties it when it starts, agents write their files there, and
|
|
30
|
+
`record.cypher` reads them with `--fetch-params` — a node's outputs, notes, symbols and checklist reach
|
|
31
|
+
the store only through that record.
|
|
32
|
+
|
|
33
|
+
The store lives outside every checkout: untracked, machine-local, never committed. It is created with
|
|
34
|
+
the JSON codec (`--snapshot-format json`, see `reference/cypher.md`) for cross-build compatibility: a
|
|
35
|
+
parquet-enabled CypherLite build defaults a new store to Parquet, and a JSON-pinned store opens in every
|
|
36
|
+
build.
|
|
29
37
|
|
|
30
38
|
## Nodes
|
|
31
39
|
|
|
@@ -36,12 +44,14 @@ data **and** constraints.
|
|
|
36
44
|
| `GbuildNode` | one unit of work (always with a PascalCase type label) | one per node |
|
|
37
45
|
| `Field` | one named input or output of a GbuildNode's contract | one per field |
|
|
38
46
|
| `Review` | one `gbuild-reviewer` verdict on a GbuildNode | one per review attempt |
|
|
47
|
+
| `Note` | a fact an implementer left for later agents | 0–5 per completed node |
|
|
48
|
+
| `Symbol` | a reusable helper a completed node added | 0–10 per completed node |
|
|
39
49
|
|
|
40
50
|
### `Feature`
|
|
41
51
|
|
|
42
52
|
| property | type | meaning |
|
|
43
53
|
| -------------- | -------------- | ---------------------------------------------------- |
|
|
44
|
-
| `feature` | string | the slug — matches the
|
|
54
|
+
| `feature` | string | the slug — matches the `<feature>` segment of the store path, immutable |
|
|
45
55
|
| `destination` | string | one line: what is true once this is done |
|
|
46
56
|
| `context` | string | why this exists, one or two lines |
|
|
47
57
|
| `out_of_scope` | list of string | bullets, may be empty |
|
|
@@ -62,7 +72,7 @@ universal label is for `MATCH (n:GbuildNode)`; the type label enables `MATCH (n:
|
|
|
62
72
|
|
|
63
73
|
| property | type | meaning |
|
|
64
74
|
| ---------------- | -------------- | -------------------------------------------------------------- |
|
|
65
|
-
| `slug` | string | kebab-case, unique, immutable — the identity every skill keys on; `tree`
|
|
75
|
+
| `slug` | string | kebab-case, unique, immutable — the identity every skill keys on; `tree`, `db` and `inbox` are reserved (they name the directories beside the node worktrees) |
|
|
66
76
|
| `title` | string | what the user reads; refer to nodes by title in conversation |
|
|
67
77
|
| `type` | string | `research \| decision \| code \| test \| verify \| chore` |
|
|
68
78
|
| `acceptance` | list of string | node-level criteria — mandatory, non-empty, concrete |
|
|
@@ -107,8 +117,36 @@ map as a property value. JSON-encode anything nested into a string and say so in
|
|
|
107
117
|
### `Review` — one reviewer verdict
|
|
108
118
|
|
|
109
119
|
`attempt` (integer, 1-based, assigned by `record.cypher`), `verdict` (`pass | fail`),
|
|
110
|
-
`failed_criteria` (list of the acceptance criteria the reviewer failed; empty on a pass), `
|
|
111
|
-
|
|
120
|
+
`failed_criteria` (list of the acceptance criteria the reviewer failed; empty on a pass), `checklist`
|
|
121
|
+
(list of string — the reviewer's per-check lines from `<slug>.review.json`, at most 40, each cut to 300
|
|
122
|
+
chars; `[]` when it wrote none), `at` (string). The verdict comes from the reviewer's reply, never from
|
|
123
|
+
the checklist file. Reviews are never updated or deleted — the repair history stays visible to `status`.
|
|
124
|
+
|
|
125
|
+
### `Note` — a fact left for later agents
|
|
126
|
+
|
|
127
|
+
| property | type | meaning |
|
|
128
|
+
| --------- | ------ | -------------------------------------------------------------------- |
|
|
129
|
+
| `topic` | string | `setup \| testing \| conventions \| gotcha` |
|
|
130
|
+
| `text` | string | non-blank, trimmed, at most 300 chars |
|
|
131
|
+
| `by_node` | string | slug of the writing GbuildNode — the note's only `WROTE` source |
|
|
132
|
+
| `at` | string | the completing `Review`'s `at` |
|
|
133
|
+
|
|
134
|
+
### `Symbol` — a registered helper
|
|
135
|
+
|
|
136
|
+
| property | type | meaning |
|
|
137
|
+
| --------- | ------ | -------------------------------------------------------------------- |
|
|
138
|
+
| `name` | string | non-blank identifier, as written in code |
|
|
139
|
+
| `file` | string | non-blank repo-relative path |
|
|
140
|
+
| `purpose` | string | one line, at most 160 chars |
|
|
141
|
+
| `by_node` | string | slug of the defining GbuildNode — the symbol's only `DEFINES` source |
|
|
142
|
+
| `at` | string | the completing `Review`'s `at` |
|
|
143
|
+
|
|
144
|
+
Only `record.cypher` writes them, from the optional `notes` and `symbols` in a node's result file, and
|
|
145
|
+
only when it completes the node — so they exist only for completed nodes; a fail or a downgraded pass
|
|
146
|
+
stores none. A node keeps at most 5 notes (any whose exact `topic` and `text` is already stored is
|
|
147
|
+
skipped) and 10 symbols (the first of each `name` and `file`). `brief.cypher` gives a node its
|
|
148
|
+
transitive upstream nodes' notes plus every `setup` and `testing` note; `symbols.cypher` lists every
|
|
149
|
+
symbol.
|
|
112
150
|
|
|
113
151
|
## Relationships
|
|
114
152
|
|
|
@@ -121,6 +159,8 @@ Reviews are never updated or deleted — the repair history stays visible to `st
|
|
|
121
159
|
| `OUTPUT` | `GbuildNode` → `Field` | the node produces this field |
|
|
122
160
|
| `FROM` | `Field` → `Field` | an input field reads an upstream node's output field |
|
|
123
161
|
| `REVIEWED` | `GbuildNode` → `Review` | a verdict on the node |
|
|
162
|
+
| `WROTE` | `GbuildNode` → `Note` | the node left this note |
|
|
163
|
+
| `DEFINES` | `GbuildNode` → `Symbol` | the node added this helper |
|
|
124
164
|
|
|
125
165
|
No other edge shapes are valid. `MATCH (n)-[:DEPENDS_ON]->(d)` is "what does n need";
|
|
126
166
|
`MATCH (n)<-[:DEPENDS_ON]-(d)` is "what waits on n".
|
|
@@ -175,9 +215,9 @@ with a value for every `OUTPUT` — a pass missing outputs is recorded as a fail
|
|
|
175
215
|
|
|
176
216
|
| layer | enforces |
|
|
177
217
|
| ------------------------- | ------------------------------------------------------------------------ |
|
|
178
|
-
| engine constraints (`cypher/schema.cypher`) | required properties and their types; unique `slug` and `Acceptance.id` — a violating write never lands |
|
|
179
|
-
| `cypher/validate.cypher` | exactly one `Feature`; enums; type label matches `type`; non-blank acceptance; `verify` target exists; acceptance linked and covered; allowed edge shapes only; acyclic `DEPENDS_ON`; every chore depended on; every node has an output; field ownership and uniqueness; the `FROM` cut test; completed nodes have a passing review and all output values |
|
|
180
|
-
| write scripts (`cypher/*.cypher`) | `dispatch` claims only frontier nodes; `record` completes a node only on a pass with every output; `set-status` never sets `completed` |
|
|
218
|
+
| engine constraints (`cypher/schema.cypher`) | required properties and their types (including every `Note` and `Symbol` property, and `Review.checklist` as a list of strings); unique `slug` and `Acceptance.id` — a violating write never lands |
|
|
219
|
+
| `cypher/validate.cypher` | exactly one `Feature`; enums; kebab-case slugs other than `tree`, `db`, `inbox` (`bad-slug`); type label matches `type`; non-blank acceptance; `verify` target exists; acceptance linked and covered; allowed labels and edge shapes only (`WROTE`, `DEFINES` included); acyclic `DEPENDS_ON`; every chore depended on; every node has an output; field ownership and uniqueness; the `FROM` cut test; completed nodes have a passing review and all output values; note topic and non-blank text (`bad-note`); non-blank symbol name and file (`bad-symbol`); each note and symbol owned by exactly its `by_node` (`note-owner`, `symbol-owner`) |
|
|
220
|
+
| write scripts (`cypher/*.cypher`) | `dispatch` claims only frontier nodes; `record` completes a node only on a pass with every output, and stores notes and symbols only then; `set-status` never sets `completed` |
|
|
181
221
|
|
|
182
222
|
## Querying
|
|
183
223
|
|
package/reference/queries.md
CHANGED
|
@@ -1,9 +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 query "<query>" --database DB --mode
|
|
5
|
-
time — `DB` is the feature store (
|
|
6
|
-
|
|
4
|
+
the questions in between. Run them with `cypherlite query "<query>" --read-only --database DB --mode
|
|
5
|
+
jsonl`, one at a time — `DB` is the feature store (`reference/cypher.md`); return only the columns you
|
|
6
|
+
need and keep a `LIMIT` on anything that can grow.
|
|
7
7
|
|
|
8
8
|
```cypher
|
|
9
9
|
-- the whole plan with progress
|
|
@@ -35,4 +35,8 @@ MATCH (a:Acceptance)<-[:SATISFIES]-(n:GbuildNode) RETURN a.id, collect(n.slug)
|
|
|
35
35
|
|
|
36
36
|
-- nodes that needed a repair pass
|
|
37
37
|
MATCH (n:GbuildNode)-[:REVIEWED]->(r:Review {verdict: 'fail'}) RETURN n.slug, count(r) AS failed_reviews
|
|
38
|
+
|
|
39
|
+
-- notes by topic, newest first (topics: setup, testing, conventions, gotcha)
|
|
40
|
+
MATCH (nt:Note) WHERE nt.topic IN ['setup'] RETURN nt.topic AS topic, nt.text AS text, nt.by_node AS by
|
|
41
|
+
ORDER BY nt.at DESC, text LIMIT 20
|
|
38
42
|
```
|
package/skills/plan/SKILL.md
CHANGED
|
@@ -7,9 +7,9 @@ Plan the work described in `$ARGUMENTS` as a graph.
|
|
|
7
7
|
## The loop
|
|
8
8
|
|
|
9
9
|
The intended workflow: plan in ordinary conversation first, then chart that plan here. After this
|
|
10
|
-
skill: `/gbuild:run <slug>` once — it
|
|
11
|
-
`/gbuild:review <slug>`; if it finds issues, reopen with
|
|
12
|
-
`/gbuild:run` again; if clean, `/gbuild:pr <slug>`.
|
|
10
|
+
skill: `/gbuild:run <slug>` once — it starts each node as its dependencies complete, until the graph
|
|
11
|
+
completes. Then `/gbuild:review <slug>`; if it finds issues, reopen with
|
|
12
|
+
`/gbuild:plan <slug> --add "<fix>"` and `/gbuild:run` again; if clean, `/gbuild:pr <slug>`.
|
|
13
13
|
|
|
14
14
|
Read `${CLAUDE_PLUGIN_ROOT}/reference/graph-format.md` (the normative graph model) and
|
|
15
15
|
`${CLAUDE_PLUGIN_ROOT}/reference/cypher.md` (how to talk to the store) before doing anything, and run
|
|
@@ -24,23 +24,24 @@ 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 store is a description, not a slug — chart it. A slug's store lives
|
|
28
|
-
worktree at `<git-common-dir>/gbuild/<slug>/
|
|
29
|
-
|
|
27
|
+
A bare slug with no store is a description, not a slug — chart it. A slug's store lives beside its
|
|
28
|
+
feature worktree at `<git-common-dir>/gbuild/<slug>/db`, machine-local and never committed (a gbuild ≤ 0.7.1
|
|
29
|
+
store needs the move in `reference/cypher.md`).
|
|
30
30
|
|
|
31
31
|
## Chart
|
|
32
32
|
|
|
33
33
|
### 1. Slug, branch, and feature worktree
|
|
34
34
|
|
|
35
35
|
Derive the feature slug from `$ARGUMENTS`: kebab-case, ≤4 words, names the outcome not the mechanism
|
|
36
|
-
(`oauth-device-flow`, not `add-some-auth-stuff`). It's the
|
|
36
|
+
(`oauth-device-flow`, not `add-some-auth-stuff`). It's the `gbuild/<slug>/` directory name and the
|
|
37
37
|
`Feature` node's `feature` property — immutable once charted.
|
|
38
38
|
|
|
39
39
|
gbuild never changes the branch of the user's checkout: the feature branch lives in its own **feature
|
|
40
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
|
|
42
|
-
|
|
43
|
-
`GC/gbuild/<slug
|
|
41
|
+
and uncommitted work in the main tree is irrelevant — there is no dirty-tree check. Resolve these paths,
|
|
42
|
+
all absolute: `GC` = `git rev-parse --git-common-dir` (resolve it to an absolute path), `WT` =
|
|
43
|
+
`GC/gbuild/<slug>` — the feature's worktree root, inside `.git/` so nothing under it is ever committed —
|
|
44
|
+
and `FT` = `WT/tree`, the feature worktree, the one place the feature branch is checked out. Then:
|
|
44
45
|
|
|
45
46
|
- `FT` is already a registered worktree (`git worktree list --porcelain`) → reuse it, say so.
|
|
46
47
|
- A branch named `<slug>` or ending `/<slug>` already exists → if it's checked out somewhere (a feature
|
|
@@ -55,14 +56,13 @@ both absolute: `GC` = `git rev-parse --git-common-dir` (resolve it to an absolut
|
|
|
55
56
|
Take `<prefix>` from the repo's convention — project or user instructions first, else the dominant
|
|
56
57
|
pattern in `git branch --list` — and fall back to `gbuild` when there is none.
|
|
57
58
|
|
|
58
|
-
Report the branch and `FT`. Everything below happens in `FT`; `DB` below is
|
|
59
|
-
`FT/.gbuild/<slug>/db`.
|
|
59
|
+
Report the branch and `FT`. Everything below happens in `FT`; `DB` below is `WT/db`.
|
|
60
60
|
|
|
61
61
|
### 2. Analyse the codebase
|
|
62
62
|
|
|
63
63
|
Read the project manifest(s), directory structure, `README.md`, and — for related in-flight work — the
|
|
64
|
-
`Feature` of every existing store (`feature.cypher` against each `GC/gbuild/*/
|
|
65
|
-
|
|
64
|
+
`Feature` of every existing store (`feature.cypher` with `--read-only` against each `GC/gbuild/*/db`,
|
|
65
|
+
one at a time). Resolve
|
|
66
66
|
file paths or URLs in `$ARGUMENTS`; if a reference fails to load, say so and ask.
|
|
67
67
|
|
|
68
68
|
### 3. Validate and explore
|
|
@@ -146,13 +146,13 @@ design, don't annotate around a failing item.
|
|
|
146
146
|
|
|
147
147
|
### 8. Create the store and write the graph
|
|
148
148
|
|
|
149
|
-
Create the store exactly as `reference/cypher.md` § Creating a store shows (`mkdir`,
|
|
150
|
-
`--snapshot-format json
|
|
149
|
+
Create the store exactly as `reference/cypher.md` § Creating a store shows (`mkdir -p DB`, then
|
|
150
|
+
`schema.cypher` with `--snapshot-format json`).
|
|
151
151
|
|
|
152
152
|
Then write the whole graph as **one** Cypher script, piped through a quoted heredoc.
|
|
153
153
|
`${CLAUDE_PLUGIN_ROOT}/templates/example.cypher` is a complete worked example — follow its structure:
|
|
154
154
|
`.begin`, a single statement of `CREATE` clauses (so variables like `a1` or `a_value` stay in scope for
|
|
155
|
-
the edges), `.commit
|
|
155
|
+
the edges), `.commit`.
|
|
156
156
|
|
|
157
157
|
```
|
|
158
158
|
cypherlite query --database DB <<'CYPHER'
|
|
@@ -168,7 +168,6 @@ CREATE (m)-[:DEPENDS_ON]->(n)
|
|
|
168
168
|
CREATE (m)-[:INPUT]->(:Field {name: '...', shape: '<...>'})-[:FROM]->(n_out)
|
|
169
169
|
CREATE (m)-[:SATISFIES]->(a1);
|
|
170
170
|
.commit
|
|
171
|
-
.checkpoint
|
|
172
171
|
CYPHER
|
|
173
172
|
```
|
|
174
173
|
|
|
@@ -178,30 +177,26 @@ slug) fails the transaction and writes nothing — fix the script and run it aga
|
|
|
178
177
|
### 9. Validate
|
|
179
178
|
|
|
180
179
|
```
|
|
181
|
-
cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
|
|
180
|
+
cypherlite query --read-only --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/validate.cypher
|
|
182
181
|
```
|
|
183
182
|
|
|
184
183
|
No output means valid. Every row is a violation (`check`, `detail`): fix it with Cypher — a `.begin` …
|
|
185
|
-
`.commit`
|
|
186
|
-
hand off
|
|
184
|
+
`.commit` script of `MATCH … SET/CREATE/DELETE` — and re-run until it prints nothing. Never
|
|
185
|
+
hand off a graph that fails validation, and never edit `graph.json` directly.
|
|
187
186
|
|
|
188
187
|
Then confirm run-readiness:
|
|
189
188
|
|
|
190
189
|
```
|
|
191
|
-
cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
|
|
190
|
+
cypherlite query --read-only --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
|
|
192
191
|
```
|
|
193
192
|
|
|
194
193
|
One row per wave: each row's `open` slugs are the decomposition, and the `frontier` counts are what
|
|
195
194
|
`run` claims first. Nodes you expected to run together should share a wave.
|
|
196
195
|
|
|
197
|
-
### 10.
|
|
196
|
+
### 10. Report and stop
|
|
198
197
|
|
|
199
|
-
|
|
200
|
-
|
|
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"`.
|
|
203
|
-
|
|
204
|
-
Report the branch, `FT`, the waves, and the frontier. Close with `next: /gbuild:run <slug>`.
|
|
198
|
+
There is nothing to commit — the store is machine-local, outside every checkout. Report the branch,
|
|
199
|
+
`FT`, the waves, and the frontier. Close with `next: /gbuild:run <slug>`.
|
|
205
200
|
|
|
206
201
|
## Reopen
|
|
207
202
|
|
|
@@ -230,13 +225,11 @@ Report the branch, `FT`, the waves, and the frontier. Close with `next: /gbuild:
|
|
|
230
225
|
CREATE (n)-[:OUTPUT]->(:Field {name: '...', shape: '<...>'})
|
|
231
226
|
CREATE (n)-[:SATISFIES]->(a);
|
|
232
227
|
.commit
|
|
233
|
-
.checkpoint
|
|
234
228
|
CYPHER
|
|
235
229
|
```
|
|
236
230
|
|
|
237
|
-
4. **Re-run `validate.cypher`** until it prints nothing, then `progress.cypher
|
|
238
|
-
|
|
239
|
-
same ignore rule as Chart's step 10), report the new frontier.
|
|
231
|
+
4. **Re-run `validate.cypher`** until it prints nothing, then `progress.cypher`, and report the new
|
|
232
|
+
frontier.
|
|
240
233
|
|
|
241
234
|
If the requirement needs no new nodes (an existing node's contract already covers it), say so and stop
|
|
242
235
|
— don't manufacture graph churn.
|
package/skills/pr/SKILL.md
CHANGED
|
@@ -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 — 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/*/
|
|
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/*/db`. 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`. `DB` below is
|
|
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 `--read-only --mode jsonl` (no output = no rows — this skill only reads the store), and on `storage locked` wait and retry up to 3 times. Anything else: `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`. `DB` below is `<git-common-dir>/gbuild/<slug>/db`, beside the feature worktree (a gbuild ≤ 0.7.1 store needs the move in `reference/cypher.md`).
|
|
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
|
|
@@ -27,7 +27,7 @@ Verify the environment before touching the remote. Stop with a clear, actionable
|
|
|
27
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
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
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.
|
|
30
|
+
7. **Graph finished.** When `<slug>` resolved, run `cypherlite query --read-only --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
|
|
|
@@ -48,7 +48,7 @@ Gather context from:
|
|
|
48
48
|
|
|
49
49
|
- Commit subjects: `git log <base>..<branch> --oneline`.
|
|
50
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
|
+
- When `<slug>` resolved: `cypherlite query --read-only --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher` — the `destination` and the acceptance bar (with the nodes covering each criterion).
|
|
52
52
|
|
|
53
53
|
Derive a concise, conventional-commit-style title from the feature/branch and the changes (e.g. `feat(auth): add OAuth login`).
|
|
54
54
|
|
|
@@ -103,4 +103,6 @@ next: /gbuild:status <slug>
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
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
|
|
106
|
+
graph is no longer needed, `git worktree remove WT/tree && rm -rf WT && git branch -d <branch>` (with
|
|
107
|
+
`WT` = `<git-common-dir>/gbuild/<slug>`) cleans up: the whole `WT`, store included, dies with the
|
|
108
|
+
feature.
|
package/skills/review/SKILL.md
CHANGED
|
@@ -12,21 +12,22 @@ missed abstractions that only exist *between* nodes, which no per-node review ca
|
|
|
12
12
|
|
|
13
13
|
`$ARGUMENTS` is optional. If present, treat it as the feature slug. If absent, infer it — if exactly one
|
|
14
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/*/
|
|
16
|
-
features charted before the worktree layout, `.gbuild/*/db/graph.json` in the main tree). Do NOT write
|
|
15
|
+
most recently modified `graph.json` under `<git-common-dir>/gbuild/*/db`. Do NOT write
|
|
17
16
|
any file — this skill outputs to chat exclusively.
|
|
18
17
|
|
|
19
18
|
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
|
|
19
|
+
worktree, attach if the feature branch is checked out nowhere). `DB` below is
|
|
20
|
+
`<git-common-dir>/gbuild/<slug>/db`, beside the worktree (a gbuild ≤ 0.7.1 store needs the move in
|
|
21
|
+
`reference/cypher.md`).
|
|
21
22
|
|
|
22
23
|
Store rules: run `cypherlite --version` first (missing → stop; gbuild needs it). One `cypherlite`
|
|
23
|
-
command at a time, always `--mode jsonl` (no output = no rows
|
|
24
|
-
3 times. Anything else: `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`.
|
|
24
|
+
command at a time, always `--read-only --mode jsonl` (no output = no rows — this skill only reads); on
|
|
25
|
+
`storage locked` wait and retry up to 3 times. Anything else: `${CLAUDE_PLUGIN_ROOT}/reference/cypher.md`.
|
|
25
26
|
|
|
26
27
|
Check the graph is finished — one row per wave, cheap:
|
|
27
28
|
|
|
28
29
|
```
|
|
29
|
-
cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
|
|
30
|
+
cypherlite query --read-only --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/progress.cypher
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
If any row's `open` is non-empty, say the graph isn't finished (name the open nodes) and ask whether to
|
|
@@ -38,7 +39,7 @@ address.
|
|
|
38
39
|
Fetch the feature context:
|
|
39
40
|
|
|
40
41
|
```
|
|
41
|
-
cypherlite query --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher
|
|
42
|
+
cypherlite query --read-only --database DB --mode jsonl < ${CLAUDE_PLUGIN_ROOT}/cypher/feature.cypher
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
Launch the `gbuild-auditor` subagent (agents/gbuild-auditor.md — this plugin's own copy, gbuild does not
|