space-architect 3.0.0 → 5.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -0
- data/README.md +25 -8
- data/lib/space_architect/architect_project.rb +245 -54
- data/lib/space_architect/cli/architect.rb +158 -47
- data/lib/space_architect/harness.rb +63 -9
- data/lib/space_architect/templates/iteration.md.erb +15 -1
- data/lib/space_core/cli/help.rb +88 -18
- data/lib/space_core/cli/loop_status.rb +47 -0
- data/lib/space_core/cli/repeatable_options.rb +52 -43
- data/lib/space_core/cli/status.rb +54 -13
- data/lib/space_core/shell_integration.rb +1 -1
- data/lib/space_core/space.rb +51 -1
- data/lib/space_core/space_store.rb +1 -1
- data/lib/space_core/version.rb +1 -1
- data/lib/space_src/cli/clone.rb +0 -1
- data/lib/space_src/cli/config.rb +0 -1
- data/lib/space_src/cli/daemon.rb +0 -1
- data/lib/space_src/cli/org.rb +0 -1
- data/lib/space_src/cli/repo.rb +0 -1
- data/lib/space_src/cli/shell.rb +0 -1
- data/lib/space_src/cli/status.rb +0 -1
- data/lib/space_src/cli/sync.rb +0 -1
- data/lib/space_src/cli.rb +0 -1
- data/skill/architect/SKILL.md +63 -20
- data/skill/architect/dispatch.md +54 -32
- metadata +2 -1
data/skill/architect/SKILL.md
CHANGED
|
@@ -46,7 +46,20 @@ message, and prints back what changed (SHA + diff stat; `freeze` prints the
|
|
|
46
46
|
frozen AC; `evidence` echoes the builder's STATUS line) — so you don't hand-edit
|
|
47
47
|
the file or run a separate `git add`/`commit`, and you don't run three follow-ups
|
|
48
48
|
to see what happened. You still author the *content*; the CLI owns the
|
|
49
|
-
*persistence*.
|
|
49
|
+
*persistence*. Two disciplines fall out of that split:
|
|
50
|
+
|
|
51
|
+
- **Author in fresh files; canonical paths are CLI-owned.** Write your content
|
|
52
|
+
to a fresh scratch file (timestamp it — `tmp/` in the space) and hand it to
|
|
53
|
+
the command via `--from`/`--prompt`/`--stdin`. Never `Write`/`Edit` a path you
|
|
54
|
+
haven't read this session — the pre-existing file trips your harness's
|
|
55
|
+
read-before-write guard and burns a failed-write → read → rewrite round trip.
|
|
56
|
+
- **Write detailed commit messages.** Every committing command takes
|
|
57
|
+
`-m`/`--message` and `--message-from <file>`: your first line completes the
|
|
58
|
+
subject after a short canonical prefix (`I01 spec: <your subject>`), the rest
|
|
59
|
+
becomes the body. The space's git log is the loop's durable memory — record
|
|
60
|
+
the *why* (rejected alternatives, BRIEF §refs, judgment reasoning) there, not
|
|
61
|
+
just in your head. Prefer `--message-from <fresh file>` for multiline bodies
|
|
62
|
+
(shells mangle quotes).
|
|
50
63
|
|
|
51
64
|
The builder **never** writes this file — the Acceptance Criteria must stay out
|
|
52
65
|
of its editable blast radius. Each lane builder writes raw evidence to a scratch
|
|
@@ -62,7 +75,9 @@ of done) that span iterations. Every iteration's Grounds/Specification/Acceptanc
|
|
|
62
75
|
Criteria/Verdict cites it as **BRIEF §N** (e.g. `(BRIEF §3.1)`), the way each gate
|
|
63
76
|
addresses its intent back to one frozen reference: the Acceptance Criteria table
|
|
64
77
|
carries a `Brief §` column, the Specification Objective cites it, the Verdict
|
|
65
|
-
reads "diff vs BRIEF §1/§3.3 — CONTINUE".
|
|
78
|
+
reads "diff vs BRIEF §1/§3.3 — CONTINUE". Author it in a fresh scratch file and
|
|
79
|
+
persist with `architect brief new --from <file>` (bare `architect brief new`
|
|
80
|
+
drops a placeholder template for a human to fill).
|
|
66
81
|
The brief is frozen at the project level — edits to a §section are logged
|
|
67
82
|
decisions in `ARCHITECT.md`, never silent per-iteration drift. Discovery projects
|
|
68
83
|
that are still finding their shape defer the brief, cite per-iteration Grounds,
|
|
@@ -121,8 +136,11 @@ loop.
|
|
|
121
136
|
gate (test/lint/typecheck/build commands) from docs or CI config.
|
|
122
137
|
- Once per environment: `claude --version` and confirm the builder model
|
|
123
138
|
resolves (`echo ok | claude -p --model <builder-model> --max-turns 1`;
|
|
124
|
-
details in `dispatch.md`).
|
|
125
|
-
|
|
139
|
+
details in `dispatch.md`). Past that one-time check, every foreground dispatch
|
|
140
|
+
self-verifies — it prints a liveness line naming the streamed model and
|
|
141
|
+
confirming the run log is growing (a WARN line instead if the streamed model
|
|
142
|
+
disagrees with the pin or the log isn't growing) — so no lone lane needs
|
|
143
|
+
launching-and-watching before the fan-out.
|
|
126
144
|
- Read `architecture/ARCHITECT.md` (the cross-iteration table of contents),
|
|
127
145
|
`architecture/BRIEF.md` if present (the durable §-numbered project contract you
|
|
128
146
|
cite as BRIEF §N), and the iteration file `architecture/I<NN>-<name>.md` for any
|
|
@@ -245,9 +263,17 @@ contract, self-contained:
|
|
|
245
263
|
task.
|
|
246
264
|
- **Lane plan** — split the iteration into 1–4 parallel lanes, each declaring
|
|
247
265
|
its **target repo + file-touch set, checked for overlap**: name the repo
|
|
248
|
-
(`repos/<repo>`) and every file each lane may touch.
|
|
249
|
-
|
|
250
|
-
|
|
266
|
+
(`repos/<repo>`) and every file each lane may touch. The machine-readable
|
|
267
|
+
declaration lives in a fenced ` ```lanes ` block in the Specification — one
|
|
268
|
+
entry per lane (`name`, `repo`, `touch` globs) — the single frozen source of
|
|
269
|
+
truth `architect freeze` records into `space.yaml` and `architect provision`
|
|
270
|
+
materializes. The touch-set now lives *with* the frozen spec by design: it
|
|
271
|
+
closes the drift where a `worktree add --touch` flag could diverge from the
|
|
272
|
+
spec's intent. The scaffold ships a commented ` ```lanes ` stub in the
|
|
273
|
+
Specification (see `templates/iteration.md.erb`) — uncomment it. Lanes in
|
|
274
|
+
*different* repos are inherently disjoint; same-repo lanes with any file
|
|
275
|
+
overlap run as one. Each lane gets its own objective, output format, and
|
|
276
|
+
boundaries. Most
|
|
251
277
|
iterations are one lane — fan out only when the work is genuinely parallel (a
|
|
252
278
|
cross-repo project often is). Two first-class patterns — runnable recipes in `dispatch.md`: **parallel +
|
|
253
279
|
fast-follow** (disjoint lanes integrate first; a fast-follow lane off
|
|
@@ -293,20 +319,35 @@ once a frozen section changed afterward.
|
|
|
293
319
|
|
|
294
320
|
### 5. Dispatch (one fresh `claude -p` per lane, worktree-isolated)
|
|
295
321
|
|
|
296
|
-
Per the mechanics in `dispatch.md
|
|
297
|
-
|
|
298
|
-
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
322
|
+
Per the mechanics in `dispatch.md`. The lane lifecycle is **declare → freeze →
|
|
323
|
+
provision → write prompts → dispatch** — every lane gets a worktree; there is no
|
|
324
|
+
dispatch-in-the-checkout path:
|
|
325
|
+
|
|
326
|
+
- **Declare** — at spec time, each lane is one entry in the Specification's
|
|
327
|
+
fenced ` ```lanes ` block (§4): `name`, `repo`, `touch` globs.
|
|
328
|
+
- **Freeze** — `architect freeze` parses that block and records each lane
|
|
329
|
+
(name, repo, touch_set) into `space.yaml`.
|
|
330
|
+
- **Provision** — `architect provision <iteration>` materializes every declared
|
|
331
|
+
lane's worktree + `lane/<id>-<lane>` branch in one shot, each off the resolved
|
|
332
|
+
base (`--base` override, else `project/<slug>` when it exists, else the repo's
|
|
333
|
+
default branch — a repo commit, distinct from the freeze, which is a space
|
|
334
|
+
commit) and recorded in `space.yaml`; it is idempotent, and `--lane <name>`
|
|
335
|
+
provisions a single lane. The manual `architect worktree add` it wraps stays
|
|
336
|
+
registered as an internal primitive for edge cases — not a step you run.
|
|
337
|
+
- `dispatch`, `integrate`, and `gate` auto-materialize a lane whose worktree is
|
|
338
|
+
missing from its frozen declaration, so a lane you didn't pre-provision can't
|
|
339
|
+
dead-end the flow.
|
|
303
340
|
|
|
304
341
|
Assemble each lane's lane-prompt (the template in `dispatch.md` + this lane's
|
|
305
342
|
section of the Specification + the frozen Acceptance Criteria) and write it to
|
|
306
|
-
`
|
|
343
|
+
a **fresh timestamped scratch file**, `tmp/prompts/<id>-<lane>-<hhmmss>.md` —
|
|
344
|
+
never to `build/<id>-<lane>/prompt.md` directly (a pre-existing canonical file
|
|
345
|
+
trips your harness's read-before-write guard). Record it in the
|
|
307
346
|
iteration file's **Builder Prompt** section — the dispatched-prompt provenance —
|
|
308
347
|
with `architect section <iteration> prompt --append --lane <lane> --from
|
|
309
|
-
|
|
348
|
+
<that scratch file>`. Then run `architect dispatch <iteration> <lane> --prompt
|
|
349
|
+
<that scratch file>` — it copies the prompt to the canonical
|
|
350
|
+
`build/<id>-<lane>/prompt.md` (fed to the builder on stdin), assembles the
|
|
310
351
|
canonical `claude -p` argv, pins the model, and streams stream-json to
|
|
311
352
|
`build/<id>-<lane>/run.jsonl`. Launch one dispatch per worktree — each as its
|
|
312
353
|
**own background Bash tool call** (your harness's `run_in_background`), **not**
|
|
@@ -363,9 +404,9 @@ it (never hand-resolve). A cross-repo project yields one `project/<slug>` branch
|
|
|
363
404
|
per touched repo. `main` is never touched per-iteration — `--teardown` deletes
|
|
364
405
|
only the per-lane `lane/<iteration>-<lane>` branches and worktrees, never the
|
|
365
406
|
project branch. Update the iteration index in `architecture/ARCHITECT.md`
|
|
366
|
-
(recording the `project/<slug>` branch), remove the worktrees
|
|
367
|
-
integrate … --teardown`, or `architect worktree remove <iteration>
|
|
368
|
-
commit the space.
|
|
407
|
+
(recording the `project/<slug>` branch), remove the provisioned worktrees
|
|
408
|
+
(`architect integrate … --teardown`, or `architect worktree remove <iteration>
|
|
409
|
+
<lane>`), and commit the space.
|
|
369
410
|
|
|
370
411
|
**Run the frozen gates cold** — `architect gate <iteration>` runs the frozen
|
|
371
412
|
gate commands against the integration tree and streams raw output (a runner, not
|
|
@@ -377,7 +418,9 @@ PASS/FAIL/INVALID, the KILL/CONTINUE call.
|
|
|
377
418
|
At project end, landing is yours, not the CLI's — the PR body is judgment
|
|
378
419
|
output, the same class as a Verdict. Per touched repo: write the PR body
|
|
379
420
|
yourself — from the iteration verdicts, the integrated diff, and the BRIEF —
|
|
380
|
-
to `build/land/<repo>-pr-body
|
|
421
|
+
to a fresh timestamped file `build/land/<repo>-pr-body-<yyyymmdd-hhmm>.md`
|
|
422
|
+
(never rewrite a prior session's body in place — fresh file, fresh Write),
|
|
423
|
+
then present the paste-and-run block to the
|
|
381
424
|
human: `cd` to the repo checkout, `git push -u origin project/<slug>`, and
|
|
382
425
|
`gh pr create --base main --head project/<slug> --title … --body-file …` —
|
|
383
426
|
paths `~`-contracted, the multi-flag command broken with trailing ` \` at flag
|
data/skill/architect/dispatch.md
CHANGED
|
@@ -27,45 +27,60 @@ pattern match), so the post-flight `git log` is what the loop actually trusts.
|
|
|
27
27
|
If a lane committed, treat the worktree as tampered: reset and re-dispatch.
|
|
28
28
|
|
|
29
29
|
**Preflight (once per environment):** run `claude --version`, and confirm the
|
|
30
|
-
builder model resolves with a one-shot
|
|
30
|
+
builder model resolves with a one-shot
|
|
31
31
|
(`echo ok | claude -p --model <builder-model> --max-turns 1`). No API key —
|
|
32
32
|
the builder runs on your Claude plan — but note headless `claude -p` draws on
|
|
33
33
|
the Agent SDK credit pool (separate from interactive usage since June 15 2026;
|
|
34
|
-
see `docs/DESIGN.md` §4).
|
|
35
|
-
|
|
34
|
+
see `docs/DESIGN.md` §4). Past that one-time check, no dispatch needs a manual
|
|
35
|
+
start-check: every foreground dispatch self-verifies. Shortly after launch it
|
|
36
|
+
prints a liveness line to stderr naming the streamed model and confirming the
|
|
37
|
+
run log is growing — or a WARN line instead when the streamed model disagrees
|
|
38
|
+
with the pinned `<builder-model>` or the log isn't growing.
|
|
36
39
|
|
|
37
40
|
## Canonical dispatch — `architect dispatch <iteration> <lane>`
|
|
38
41
|
|
|
39
|
-
The canonical path is `architect dispatch <iteration> <lane
|
|
40
|
-
|
|
42
|
+
The canonical path is `architect dispatch <iteration> <lane> --prompt <file>`.
|
|
43
|
+
The tool copies your prompt file to `build/<id>-<lane>/prompt.md` (the CLI owns
|
|
44
|
+
that canonical path — you never write it directly), assembles the canonical
|
|
45
|
+
`claude -p` argv, pins the builder model (the lane's
|
|
41
46
|
configured model or the CLI's reference default; see `docs/DESIGN.md` §4),
|
|
42
|
-
|
|
47
|
+
feeds the copied lane prompt to the builder on stdin, and streams
|
|
43
48
|
`--output-format stream-json --verbose` output to
|
|
44
49
|
`build/<id>-<lane>/run.jsonl`. Run each lane as its own **background Bash tool
|
|
45
50
|
call** (`run_in_background`) so your turn doesn't block for the full run (30–60
|
|
46
51
|
minutes is typical).
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
Author the lane's prompt in a **fresh timestamped scratch file**
|
|
54
|
+
(`tmp/prompts/<id>-<lane>-<hhmmss>.md` — never a pre-existing canonical path,
|
|
55
|
+
which trips your harness's read-before-write guard; never a shell argument —
|
|
56
|
+
shells mangle quotes), then hand it to dispatch:
|
|
50
57
|
|
|
51
58
|
```bash
|
|
52
|
-
# single-lane iteration — run from the space root
|
|
53
|
-
|
|
59
|
+
# single-lane iteration — run from the space root; dispatch copies the prompt to
|
|
60
|
+
# build/<id>-<lane>/prompt.md and runs in the lane's provisioned worktree
|
|
61
|
+
# (materialized on demand from the frozen declaration)
|
|
62
|
+
architect dispatch <iteration> <lane> --prompt tmp/prompts/<id>-<lane>-<hhmmss>.md
|
|
54
63
|
```
|
|
55
64
|
|
|
56
|
-
For multi-lane iterations,
|
|
57
|
-
each lane from its worktree:
|
|
65
|
+
For multi-lane iterations, materialize every declared lane in one shot with
|
|
66
|
+
`architect provision`, then dispatch each lane from its worktree:
|
|
58
67
|
|
|
59
68
|
```bash
|
|
60
|
-
#
|
|
61
|
-
architect
|
|
62
|
-
architect dispatch <iteration> <lane>
|
|
69
|
+
architect provision <iteration> # all declared lanes: worktree + lane/<id>-<lane> branch
|
|
70
|
+
architect dispatch <iteration> lane-a --prompt <lane-a scratch file> # own background Bash call each
|
|
71
|
+
architect dispatch <iteration> lane-b --prompt <lane-b scratch file>
|
|
63
72
|
```
|
|
64
73
|
|
|
65
|
-
`architect
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
`architect provision` reads the frozen lane declarations from `space.yaml` and,
|
|
75
|
+
per lane, creates `build/<id>-<lane>/wt` off the resolved base (`--base`
|
|
76
|
+
override, else `project/<slug>` when it exists, else the repo's default branch —
|
|
77
|
+
a repo commit, distinct from the freeze, which is a space commit), adds it with a
|
|
78
|
+
`lane/<iteration>-<lane>` branch, and records it in `space.yaml`. It is
|
|
79
|
+
idempotent (`--lane <name>` provisions a single lane), and wraps the
|
|
80
|
+
`architect worktree add` primitive — still registered for edge cases, not a step
|
|
81
|
+
in the flow. `dispatch`, `integrate`, and `gate` also lazily materialize a lane
|
|
82
|
+
whose worktree is missing from its frozen declaration, so the flow can't
|
|
83
|
+
dead-end on a missing worktree.
|
|
69
84
|
|
|
70
85
|
Issue each dispatch as its **own background Bash tool call** — one call per
|
|
71
86
|
lane. Never use a shell `&` loop. A `for … & done` launcher is a *launcher*
|
|
@@ -82,7 +97,8 @@ survives the full run and reports completion per lane.
|
|
|
82
97
|
and as the manual fallback:
|
|
83
98
|
|
|
84
99
|
```bash
|
|
85
|
-
#
|
|
100
|
+
# dispatch --prompt copies the scratch prompt to build/<id>-<lane>/prompt.md; the
|
|
101
|
+
# manual equivalent is that copy followed by:
|
|
86
102
|
( cd build/<id>-<lane>/wt && \
|
|
87
103
|
claude -p --model <builder-model> \
|
|
88
104
|
--permission-mode acceptEdits \
|
|
@@ -113,13 +129,16 @@ architect integrate <iteration> --lanes <passing-set> # e.g. --lanes lane-a,la
|
|
|
113
129
|
architect gate <iteration> # integration smoke (raw output; verdict stays yours)
|
|
114
130
|
architect integrate <iteration> --lanes <passing-set> --teardown # or remove worktrees + lane branches after
|
|
115
131
|
# end of project: landing is the architect's, not a CLI command — write the PR
|
|
116
|
-
# body to build/land/<repo>-pr-body
|
|
117
|
-
# block (cd, git push -u origin project/<slug>,
|
|
132
|
+
# body to a fresh build/land/<repo>-pr-body-<yyyymmdd-hhmm>.md yourself, then
|
|
133
|
+
# present the paste-and-run block (cd, git push -u origin project/<slug>,
|
|
134
|
+
# gh pr create) — see SKILL.md §6
|
|
118
135
|
```
|
|
119
136
|
|
|
120
137
|
`architect integrate` commits each named lane on its branch and merges it
|
|
121
138
|
`--no-ff` into the repo's stable `project/<slug>` branch (slug of `space.title`,
|
|
122
|
-
persistent across all iterations), in order.
|
|
139
|
+
persistent across all iterations), in order. Pass `-m`/`--message-from` — the
|
|
140
|
+
lane commit lands in the repo's PR history, so say what the lane did and why,
|
|
141
|
+
not just that it integrated. It **refuses** a lane that left
|
|
123
142
|
builder commits or wrote out-of-bounds (the mechanical post-flight checks), and
|
|
124
143
|
aborts on a merge conflict. A merge conflict = the lane plan wasn't disjoint = a
|
|
125
144
|
spec defect: kill the conflicting lane and re-spec; don't hand-resolve builder
|
|
@@ -140,8 +159,9 @@ git -C repos/<repo> merge --no-ff lane/<iteration>-<lane>
|
|
|
140
159
|
<run the gate commands> # integration smoke after every merge
|
|
141
160
|
architect worktree remove <iteration> <lane>
|
|
142
161
|
git -C repos/<repo> branch -d lane/<iteration>-<lane>
|
|
143
|
-
# at project end there is no CLI step: the architect writes
|
|
144
|
-
# build/land/<repo>-pr-body
|
|
162
|
+
# at project end there is no CLI step: the architect writes a fresh
|
|
163
|
+
# build/land/<repo>-pr-body-<yyyymmdd-hhmm>.md and presents the push +
|
|
164
|
+
# gh pr create block
|
|
145
165
|
```
|
|
146
166
|
|
|
147
167
|
### Parallel + fast-follow
|
|
@@ -157,10 +177,12 @@ integrate without conflict.
|
|
|
157
177
|
fast-follow lane's `--touch` set and exclude them from every parallel lane's
|
|
158
178
|
touch-set — so the parallel set is disjoint by construction.
|
|
159
179
|
|
|
160
|
-
2. **
|
|
180
|
+
2. **Provision and dispatch the parallel lanes** (off the repo base). Provision
|
|
181
|
+
only the parallel lanes by name — leave the fast-follow lane unmaterialized
|
|
182
|
+
until step 4, so it can root at the integrated tip:
|
|
161
183
|
```bash
|
|
162
|
-
architect
|
|
163
|
-
architect
|
|
184
|
+
architect provision <iteration> --lane lane-a
|
|
185
|
+
architect provision <iteration> --lane lane-b
|
|
164
186
|
architect dispatch <iteration> lane-a # own background Bash call each
|
|
165
187
|
architect dispatch <iteration> lane-b
|
|
166
188
|
```
|
|
@@ -172,11 +194,11 @@ integrate without conflict.
|
|
|
172
194
|
architect gate <iteration>
|
|
173
195
|
```
|
|
174
196
|
|
|
175
|
-
4. **
|
|
176
|
-
git ref — passing `project/<slug>` roots the new worktree at the merged
|
|
177
|
-
(the keystone move):
|
|
197
|
+
4. **Provision the fast-follow lane off the integrated tip.** `--base` accepts
|
|
198
|
+
any git ref — passing `project/<slug>` roots the new worktree at the merged
|
|
199
|
+
tip (the keystone move); `--lane ff` provisions just that lane:
|
|
178
200
|
```bash
|
|
179
|
-
architect
|
|
201
|
+
architect provision <iteration> --lane ff --base project/<slug>
|
|
180
202
|
architect dispatch <iteration> ff
|
|
181
203
|
```
|
|
182
204
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: space-architect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Jacobs
|
|
@@ -272,6 +272,7 @@ files:
|
|
|
272
272
|
- lib/space_core/cli/helpers.rb
|
|
273
273
|
- lib/space_core/cli/init.rb
|
|
274
274
|
- lib/space_core/cli/list.rb
|
|
275
|
+
- lib/space_core/cli/loop_status.rb
|
|
275
276
|
- lib/space_core/cli/new.rb
|
|
276
277
|
- lib/space_core/cli/pack.rb
|
|
277
278
|
- lib/space_core/cli/path.rb
|