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.
@@ -1,9 +1,11 @@
1
1
  # Talking to the gbuild store
2
2
 
3
- Every gbuild skill reads and writes the feature store (`.gbuild/<feature>/db/` inside the feature's git
4
- worktree, modelled in
5
- `reference/graph-format.md`) with the `cypherlite` CLI and nothing else. This file is the full operating
6
- manual; the skills that only run plugin scripts carry the short version of these rules inline.
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
- CypherLite takes an exclusive lock on the store while a process has it open. A second process — reader
27
- or writer — fails with `storage locked` (exit 5). So:
28
-
29
- - **Only the skill running in the main context runs `cypherlite` against the store.** Subagents never
30
- do: give them what they need in their prompt, have them report results in their final message, and
31
- record those results yourself.
32
- - **Never run two `cypherlite` commands against the same store at once** — no parallel tool calls on it.
33
- Fan-out is for agents, not for store access.
34
- - On `storage locked` (exit 5), another process holds the store (another gbuild command, a REPL left
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. `DB` below is the feature store: `.gbuild/<feature>/db` inside the feature worktree
42
- (`<git-common-dir>/gbuild/<feature>/tree/`), or in the main tree for a feature charted before the
43
- worktree layout. Every call is `cypherlite query [STATEMENT] --database DB …`; with no STATEMENT the
44
- piped stdin is the script list.
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 and checkpointed.** Wrap them in `.begin` / `.commit` — if any statement
76
- fails, the whole transaction is discarded — and end with `.checkpoint` so `graph.json` on disk (what
77
- git commits) is always current. The plugin write scripts already do both.
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, with `DB` = `.gbuild/<feature>/db` inside the feature worktree
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, so `graph.json` exists from the start.
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/`. Writes end with `.checkpoint`.
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 the whole frontier (→ `in_progress`); per claimed node: `slug`, `title`, `type`, `acceptance`, `failure_policy`, `model_tier`, `verify`, `inputs` (`name`, `shape`, `value`), `outputs` (`name`, `shape`) | run |
118
- | `record.cypher` | `results` — list of `{slug, verdict, failed_criteria, outputs, started_at, completed_at}` | per recorded node: `attempt`, `verdict`, `completed`, `missing_outputs` | run |
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: `.gbuild/<feature>/db/` inside the feature's git worktree
6
- (`<git-common-dir>/gbuild/<feature>/tree/` — `plan` creates the worktree; a feature charted before the
7
- worktree layout keeps its store in the main tree). `plan` creates and charts it, `run` records
8
- progress in it, and every skill reads it — **all through Cypher**. No skill reads or writes `graph.json`
9
- directly; it is CypherLite's snapshot of the store. How to talk to the store (the single-writer rule,
10
- invocation, quoting, the script catalog) is in `reference/cypher.md`.
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
- .gbuild/<feature>/db/
19
- graph.json CypherLite JSON snapshot — committed; always current (every write ends with .checkpoint)
20
- .gitignore "*", "!.gitignore", "!graph.json" — committed
21
- manifest.json engine files — ignored
22
- wal/
23
- .cypherlite.lock
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 store is created with the JSON codec (`--snapshot-format json`, see `reference/cypher.md`) so
27
- `graph.json` diffs are reviewable in a PR, and a clone holding only `graph.json` is a complete store —
28
- data **and** constraints.
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 `.gbuild/<feature>/` dir in the feature worktree, immutable |
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` is reserved (the feature worktree's directory name) |
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), `at` (string).
111
- Reviews are never updated or deleted — the repair history stays visible to `status`.
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
 
@@ -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 jsonl`, one at a
5
- time — `DB` is the feature store (`.gbuild/<feature>/db` inside the feature worktree);
6
- return only the columns you need and keep a `LIMIT` on anything that can grow.
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
  ```
@@ -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 iterates waves until the graph completes. Then
11
- `/gbuild:review <slug>`; if it finds issues, reopen with `/gbuild:plan <slug> --add "<fix>"` and
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 in its feature
28
- worktree at `<git-common-dir>/gbuild/<slug>/tree/.gbuild/<slug>/db` (a feature charted before the
29
- worktree layout keeps its store at `.gbuild/<slug>/db` in the main tree).
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 `.gbuild/<slug>/` directory name and 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 two paths,
42
- both absolute: `GC` = `git rev-parse --git-common-dir` (resolve it to an absolute path), and `FT` =
43
- `GC/gbuild/<slug>/tree` — the feature worktree, the one place the feature branch is checked out. Then:
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/*/tree/.gbuild/*/db`, plus
65
- any legacy `.gbuild/*/db` in the main tree, one at a time). Resolve
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`, `schema.cypher` with
150
- `--snapshot-format json`, the `db/.gitignore`).
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`, `.checkpoint`.
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` … `.checkpoint` script of `MATCH … SET/CREATE/DELETE` — and re-run until it prints nothing. Never
186
- hand off or commit a graph that fails validation, and never edit `graph.json` directly.
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. Commit and stop
196
+ ### 10. Report and stop
198
197
 
199
- Commit `.gbuild/<slug>/` on the feature branch unless the repo ignores it (`git -C FT check-ignore -q
200
- .gbuild/`; if true, don't stage anything under it). The store's own `.gitignore` limits that to
201
- `db/graph.json` and `db/.gitignore`. Stage and commit from the feature worktree:
202
- `git -C FT add .gbuild/<slug>` and `git -C FT commit -m "<slug>: chart graph"`.
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`. Commit in `FT`
238
- (`git -C FT commit -m "<slug>: reopen graph — <what was added>"` after staging `.gbuild/<slug>`,
239
- same ignore rule as Chart's step 10), report the new frontier.
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.
@@ -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/*/tree/.gbuild/*/db` (or, for features charted before the worktree layout, `.gbuild/*/db/graph.json` in the main tree). If neither resolves, leave `<slug>` unresolved (the auto-fix step degrades gracefully — see Step 4).
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 `FT/.gbuild/<slug>/db` (see Step 1 for `FT`).
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 FT` (and `git branch -d <branch>`) cleans up.
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.
@@ -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/*/tree/.gbuild/*/db` (or, for
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 `FT/.gbuild/<slug>/db`.
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); on `storage locked` wait and retry up to
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