space-architect 7.1.0 → 8.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 +54 -0
- data/README.md +5 -3
- data/lib/space_architect/architect_project.rb +40 -180
- data/lib/space_architect/cli/architect.rb +49 -96
- data/lib/space_architect/cli/research.rb +1 -1
- data/lib/space_architect/harness.rb +97 -344
- data/lib/space_architect/pi/builder-guard.ts +103 -0
- data/lib/space_architect/research/mux.rb +47 -32
- data/lib/space_architect/research/pi_events.rb +70 -0
- data/lib/space_architect/research/renderer.rb +39 -39
- data/lib/space_architect/research/supervisor.rb +35 -14
- data/lib/space_architect/research.rb +1 -6
- data/lib/space_architect.rb +0 -1
- data/lib/space_core/version.rb +1 -1
- data/skill/architect/SKILL.md +27 -23
- data/skill/architect/dispatch.md +137 -115
- data/skill/architect/research.md +14 -8
- data/skill/architect-research/SKILL.md +18 -11
- data/skill/architect-vocabulary/SKILL.md +1 -1
- metadata +3 -2
- data/lib/space_architect/dispatcher.rb +0 -25
data/skill/architect/dispatch.md
CHANGED
|
@@ -1,51 +1,58 @@
|
|
|
1
1
|
# Builder dispatch reference
|
|
2
2
|
|
|
3
|
-
Verified against
|
|
4
|
-
harness
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Verified against `pi` 1.1.0 (badlogic/pi-mono) headless print mode — the reference
|
|
4
|
+
harness. The builder is `pi -p` (`--print`, the non-interactive mode) pinned to
|
|
5
|
+
the configured builder model (`<builder-model>`) — a cheaper model run headless
|
|
6
|
+
via the same harness the architect uses. Key facts the skill encodes: lane-prompts
|
|
7
|
+
go in on **stdin** (piped stdin is prepended to the message, and a big quoted
|
|
8
|
+
lane-prompt as a shell argument gets mangled); the model is pinned with
|
|
9
9
|
`--model <builder-model>` (a floating alias drifts to whatever ships next — pin
|
|
10
|
-
the full id); there is **no
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
`--
|
|
14
|
-
`
|
|
10
|
+
the full id); there is **no working-dir flag**, so per-lane dispatch `cd`s into
|
|
11
|
+
the worktree; there is **no tool allow/deny flag surface and no turn cap** — tool
|
|
12
|
+
control is the injected **builder guard** extension (`-e`), hermeticity comes from
|
|
13
|
+
`--no-approve` (project-trust pinned to untrusted: the lane's `.pi/` project
|
|
14
|
+
resources never load; `AGENTS.md`/`CLAUDE.md` context files still load; explicit
|
|
15
|
+
`-e` paths still load), and the run's bound is the dispatch wall-clock timeout
|
|
16
|
+
(TERM → grace → KILL); web access comes from the web tools loaded by the pi
|
|
17
|
+
extensions configured in `~/.pi/agent` (no per-dispatch setup).
|
|
15
18
|
|
|
16
19
|
**The one load-bearing difference from the Codex design:** Codex's
|
|
17
|
-
`--sandbox workspace-write` made `.git` physically read-only.
|
|
18
|
-
**no
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
(
|
|
22
|
-
|
|
20
|
+
`--sandbox workspace-write` made `.git` physically read-only. pi has
|
|
21
|
+
**no filesystem sandbox**, so `.git` is not hardware-protected. "Builders never
|
|
22
|
+
commit" (hard rule 7) is enforced in three layers, weakest to strongest:
|
|
23
|
+
(1) a runtime first line — the **builder guard** extension denies git-write
|
|
24
|
+
commands (and `bash`-unparseable commands) before evaluation (see `### The
|
|
25
|
+
builder guard` below); (2) worktree isolation between
|
|
23
26
|
lanes; (3) the authoritative check — an architect post-flight
|
|
24
|
-
`git -C <worktree> log <repo-base>..` that must be empty. The deny
|
|
27
|
+
`git -C <worktree> log <repo-base>..` that must be empty. The guard's deny is
|
|
25
28
|
not airtight (a builder can shell out — `sh -c 'git commit …'` — past the
|
|
26
|
-
|
|
29
|
+
parse), so the post-flight `git log` is what the loop actually trusts.
|
|
27
30
|
If a lane committed, treat the worktree as tampered: reset and re-dispatch.
|
|
28
31
|
|
|
29
|
-
**Preflight (once per environment):** run `
|
|
30
|
-
builder model resolves with a one-
|
|
31
|
-
(`echo ok |
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
see `docs/DESIGN.md` §4). Past that one-time check, no dispatch needs a manual
|
|
32
|
+
**Preflight (once per environment):** run `pi --version`, and confirm the
|
|
33
|
+
builder model resolves with a one-message probe
|
|
34
|
+
(`echo ok | pi -p --model <builder-model>`). No API-key/env ceremony — provider
|
|
35
|
+
config (e.g. the Fireworks key) resolves from `~/.pi/agent`; pi needs no
|
|
36
|
+
`ANTHROPIC_*` env. Past that one-time check, no dispatch needs a manual
|
|
35
37
|
start-check: every foreground dispatch self-verifies. Shortly after launch it
|
|
36
38
|
prints a liveness line to stderr naming the streamed model and confirming the
|
|
37
39
|
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.
|
|
40
|
+
with the pinned `<builder-model>` or the log isn't growing. (Liveness source:
|
|
41
|
+
the run log is pi's JSONL event stream — ~20 event types, `session` →
|
|
42
|
+
`agent_start`/`turn_start` → `message_*`/`tool_execution_*` →
|
|
43
|
+
`turn_end`/`agent_end`/`agent_settled`; the line is parsed from the first
|
|
44
|
+
assistant-role event carrying a `model` field.)
|
|
39
45
|
|
|
40
46
|
## Canonical dispatch — `architect dispatch <iteration> <lane>`
|
|
41
47
|
|
|
42
48
|
The canonical path is `architect dispatch <iteration> <lane> --prompt <file>`.
|
|
43
49
|
The tool copies your prompt file to `build/<id>-<lane>/prompt.md` (the CLI owns
|
|
44
|
-
that canonical path — you never write it directly),
|
|
45
|
-
|
|
50
|
+
that canonical path — you never write it directly), copies the vendored
|
|
51
|
+
builder-guard to `build/<id>-<lane>/builder-guard.ts`, assembles the canonical
|
|
52
|
+
`pi -p` argv, pins the builder model (the lane's
|
|
46
53
|
configured model or the CLI's reference default; see `docs/DESIGN.md` §4),
|
|
47
54
|
feeds the copied lane prompt to the builder on stdin, and streams
|
|
48
|
-
`--
|
|
55
|
+
pi's `--mode json` event stream to
|
|
49
56
|
`build/<id>-<lane>/run.jsonl`. Run each lane as its own **background Bash tool
|
|
50
57
|
call** (`run_in_background`) so your turn doesn't block for the full run (30–60
|
|
51
58
|
minutes is typical).
|
|
@@ -85,7 +92,7 @@ dead-end on a missing worktree.
|
|
|
85
92
|
Issue each dispatch as its **own background Bash tool call** — one call per
|
|
86
93
|
lane. Never use a shell `&` loop. A `for … & done` launcher is a *launcher*
|
|
87
94
|
process: it returns the instant it has spawned the lane children, the harness
|
|
88
|
-
reaps those now-orphaned `
|
|
95
|
+
reaps those now-orphaned `pi` processes, and every lane dies at once with no
|
|
89
96
|
`result` — partial diffs, no reports (this exact failure has happened: three
|
|
90
97
|
lanes killed at the same second, zero output). One blocking dispatch per
|
|
91
98
|
background Bash tool keeps each lane attached to a harness-tracked task that
|
|
@@ -97,26 +104,52 @@ survives the full run and reports completion per lane.
|
|
|
97
104
|
and as the manual fallback:
|
|
98
105
|
|
|
99
106
|
```bash
|
|
100
|
-
# dispatch --prompt copies the scratch prompt to build/<id>-<lane>/prompt.md
|
|
101
|
-
#
|
|
107
|
+
# dispatch --prompt copies the scratch prompt to build/<id>-<lane>/prompt.md and
|
|
108
|
+
# the vendored guard to build/<id>-<lane>/builder-guard.ts; the manual equivalent
|
|
109
|
+
# is those copies followed by:
|
|
102
110
|
( cd build/<id>-<lane>/wt && \
|
|
103
|
-
|
|
104
|
-
--
|
|
105
|
-
--
|
|
106
|
-
--
|
|
107
|
-
|
|
108
|
-
--
|
|
111
|
+
pi -p --mode json \
|
|
112
|
+
--model <builder-model> \
|
|
113
|
+
--session-dir <space>/build/<id>-<lane> \
|
|
114
|
+
--no-approve \
|
|
115
|
+
-e <space>/build/<id>-<lane>/builder-guard.ts \
|
|
116
|
+
[--thinking <level>] \
|
|
109
117
|
< <space>/build/<id>-<lane>/prompt.md \
|
|
110
118
|
> <space>/build/<id>-<lane>/run.jsonl 2>&1 )
|
|
111
119
|
```
|
|
112
120
|
|
|
113
|
-
`
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
`--no-approve` pins project-trust to untrusted for the run — a **hermetic
|
|
122
|
+
builder**: the lane's `.pi/` project resources (settings, extensions, skills,
|
|
123
|
+
prompts, themes, SYSTEM.md) never load, while `AGENTS.md`/`CLAUDE.md` context
|
|
124
|
+
files still load root-down and the explicit `-e` guard always loads — the guard
|
|
125
|
+
cannot be shrunk by project-local config. `--session-dir` points pi's session
|
|
126
|
+
storage and lookup at the lane's build dir, which is what makes `--continue`
|
|
127
|
+
follow-ups (below) deterministic per lane. Redirect stderr (`2>&1`) into the
|
|
128
|
+
run-log so a dispatch error lands somewhere instead of vanishing.
|
|
129
|
+
|
|
130
|
+
### The builder guard — what a builder sees on a deny
|
|
131
|
+
|
|
132
|
+
The guard is a pi extension (`lib/space_architect/pi/builder-guard.ts` in the
|
|
133
|
+
space-architect repo, copied into the lane's build dir and injected via `-e`).
|
|
134
|
+
Its `tool_call` handler intercepts every `bash` call **before evaluation** and
|
|
135
|
+
blocks two classes, returning the deny reason as the bash tool result text —
|
|
136
|
+
that reason is the builder's only feedback, so it says what to do instead:
|
|
137
|
+
|
|
138
|
+
- **Git write operations** — `commit`, `push`, `pull`, `reset`, `merge`,
|
|
139
|
+
`rebase`, `checkout`, `switch`, `branch`, `tag`, and the rest of git's write
|
|
140
|
+
verbs — denied because the architect CLI owns all commits (hard rule 7). The
|
|
141
|
+
deny text surfaces as `architect builder guard: …` in the tool result. The
|
|
142
|
+
parse walks each command segment past git's flags (`git -C <path> commit` is
|
|
143
|
+
still a deny), and reads like `git log --grep=commit` pass — the guard
|
|
144
|
+
classifies the subcommand, it never substring-matches the raw command.
|
|
145
|
+
- **`bash`-unparseable commands** — the guard runs `bash -n` and blocks on a
|
|
146
|
+
parse error (unterminated quote, missing `;` before `then`), with bash's
|
|
147
|
+
stderr in the reason. The parse-check is parse-level only: an unclosed `[`
|
|
148
|
+
(`[ -f /etc/hosts` with no `]`) is a *runtime* `test` failure, syntactically
|
|
149
|
+
valid — not the guard's domain.
|
|
150
|
+
|
|
151
|
+
The guard is a first line, not a sandbox — the post-flight `git log` check
|
|
152
|
+
(Operating guidance below) stays the authoritative no-commits proof.
|
|
120
153
|
|
|
121
154
|
### Integration (judging session — after per-lane post-flight passes)
|
|
122
155
|
|
|
@@ -307,13 +340,15 @@ after.
|
|
|
307
340
|
echo "The sweep at <run dir> (pid <pid>) has exited. Audit its output
|
|
308
341
|
against the spec's acceptance criteria, finish the report you
|
|
309
342
|
pre-structured in phase A, and end at STATUS: COMPLETE." \
|
|
310
|
-
|
|
|
311
|
-
--
|
|
312
|
-
--
|
|
313
|
-
|
|
314
|
-
--output-format stream-json --verbose \
|
|
343
|
+
| pi -p --continue --session-dir <space>/build/<id>-<lane> \
|
|
344
|
+
--mode json --model <builder-model> \
|
|
345
|
+
--no-approve \
|
|
346
|
+
-e <space>/build/<id>-<lane>/builder-guard.ts \
|
|
315
347
|
> build/<id>-<lane>/run-b.jsonl 2>&1 )
|
|
316
348
|
```
|
|
349
|
+
(`--mode json` because the audit run gets its own run log — `run-b.jsonl`
|
|
350
|
+
stays a pi JSONL event stream like the first. Piped stdin is prepended to
|
|
351
|
+
the resumed message, same as a fresh dispatch.)
|
|
317
352
|
Phase B runs the post-run audits the spec asked for, then finishes the
|
|
318
353
|
report at `STATUS: COMPLETE` (or `COMPLETE_WITH_CONCERNS`/`BLOCKED`, per the
|
|
319
354
|
template) exactly as any other lane would.
|
|
@@ -340,43 +375,33 @@ instruction.
|
|
|
340
375
|
model bump can't silently change builder behavior mid-project.
|
|
341
376
|
- Effort = thinking budget. Set it per dispatch: `architect dispatch --effort
|
|
342
377
|
<level>` (aliases `--thinking`/`--reasoning`) accepts
|
|
343
|
-
`off`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
`
|
|
348
|
-
|
|
378
|
+
`off`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`, normalizes to that
|
|
379
|
+
canonical set, and pi accepts the full set unchanged (`--thinking <level>`) —
|
|
380
|
+
no harness-side clamping; each model's `thinkingLevelMap` in
|
|
381
|
+
`~/.pi/agent/models.json` clamps further per model (e.g. a model whose map
|
|
382
|
+
sends `off`/`minimal` to a higher floor). The escalation keywords (`think` <
|
|
383
|
+
`think hard` < `think harder` < `ultrathink`) still raise depth from inside
|
|
384
|
+
the block. Default unattended builder work to a high budget; downgrade
|
|
349
385
|
a routine, tightly-specified lane (record which and why in the spec).
|
|
350
|
-
- **
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
that needs an MCP or other non-default tool is granted it reviewably, judged
|
|
355
|
-
like every other lane boundary, rather than buried in a shell invocation. The
|
|
356
|
-
CLI flag wins over the lane's yaml key when both are given, and dispatch
|
|
357
|
-
reports which source resolved. This exists because `claude -p` **denies** a
|
|
358
|
-
tool not on the allow list rather than prompting for it — a lane missing a
|
|
359
|
-
needed grant doesn't error, it silently can't use the tool and files a
|
|
360
|
-
confident, dataless report instead. `--disallowedTools`/`DISALLOWED_TOOLS`
|
|
361
|
-
(the builder-never-commits deny rules above) has no override route, by design
|
|
362
|
-
— hard rule 7 depends on it staying fixed.
|
|
363
|
-
- **Builders never commit, and the architect verifies it.** Claude Code has no
|
|
364
|
-
sandbox to make `.git` read-only, so this is enforced by the deny rules at
|
|
365
|
-
dispatch *and* checked after the run: before integrating a lane, confirm
|
|
386
|
+
- **Builders never commit, and the architect verifies it.** pi has no sandbox
|
|
387
|
+
to make `.git` read-only, so the injected guard denies git-write commands at
|
|
388
|
+
dispatch (see `### The builder guard` above) *and* it is checked after the
|
|
389
|
+
run: before integrating a lane, confirm
|
|
366
390
|
`git -C build/<id>-<lane>/wt log <repo-base>..` is empty and
|
|
367
391
|
`git -C build/<id>-<lane>/wt status` shows only files inside the lane's
|
|
368
392
|
declared set. A commit or an out-of-bounds write fails the lane — reset and
|
|
369
393
|
re-dispatch (lanes are cheap, hard rule 7).
|
|
370
394
|
- Same-iteration follow-up (e.g. answering PHASE 0 disagreements after the
|
|
371
|
-
human rules): from the lane's worktree, `
|
|
372
|
-
proceed>"` resumes
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
every iteration gets a fresh
|
|
395
|
+
human rules): from the lane's worktree, `pi -p --continue --session-dir
|
|
396
|
+
<space>/build/<id>-<lane> "<rulings + proceed>"` resumes the lane's most
|
|
397
|
+
recent session in that session dir with full context — `-c` continues that
|
|
398
|
+
most recent session, so follow-ups are deterministic even with parallel
|
|
399
|
+
lanes. Resume the **same way you dispatch** — one background Bash tool call
|
|
400
|
+
per lane, each a single blocking `pi -p --continue …`, never a `&` loop (a
|
|
401
|
+
`&` launcher orphans the resumed lanes exactly as it does fresh ones). Add
|
|
402
|
+
`--mode json` and redirect when you want the resumed run logged like a fresh
|
|
403
|
+
dispatch. Never resume across iterations — every iteration gets a fresh
|
|
404
|
+
context.
|
|
380
405
|
- Capability-gap review gate (high-stakes iterations): the architect outranks the
|
|
381
406
|
builder, so the architect reading the diff is already a stronger-model,
|
|
382
407
|
fresh-context pass over it. How independent that read is depends on the pairing
|
|
@@ -387,7 +412,7 @@ instruction.
|
|
|
387
412
|
```bash
|
|
388
413
|
{ echo "Review this diff against the spec. Flag ONLY correctness/requirement/invariant gaps with file:line evidence. No style."; \
|
|
389
414
|
git -C <repo-root> diff <base>...HEAD; } \
|
|
390
|
-
|
|
|
415
|
+
| pi -p --model <builder-model> --tools read,grep,find,ls
|
|
391
416
|
```
|
|
392
417
|
- `build/` is already gitignored by the space, so no extra `.gitignore` entry
|
|
393
418
|
is needed. Scratch never reaches the space repo; only `architecture/` is
|
|
@@ -397,25 +422,26 @@ instruction.
|
|
|
397
422
|
|
|
398
423
|
A dispatched run is STALLED when its `run.jsonl`
|
|
399
424
|
(`build/<id>-<lane>/run.jsonl`) has not grown for 15+ minutes AND the last
|
|
400
|
-
event is an in-flight `
|
|
401
|
-
matching `
|
|
402
|
-
thinking; a shell command that should take seconds sitting in
|
|
403
|
-
minutes is not.
|
|
425
|
+
event is an in-flight `bash` tool call (a `tool_execution_start` for `bash`
|
|
426
|
+
with no matching `tool_execution_end` yet). Silent gaps between events are
|
|
427
|
+
normal model thinking; a shell command that should take seconds sitting in
|
|
428
|
+
flight for 15+ minutes is not.
|
|
404
429
|
|
|
405
|
-
Diagnose before killing: find the command's child under the `
|
|
406
|
-
(
|
|
430
|
+
Diagnose before killing: find the command's child under the `pi` PID
|
|
431
|
+
(pi → shell → child). Hot-spinning (high CPU) or blocked (zero CPU and none
|
|
407
432
|
of its expected side effects on disk) — hung either way.
|
|
408
433
|
|
|
409
|
-
Kill the NARROWEST thing: the stuck child process, not the `
|
|
434
|
+
Kill the NARROWEST thing: the stuck child process, not the `pi` run. The
|
|
410
435
|
command returns a failure to the builder, which adapts with its full context
|
|
411
436
|
intact. Kill the whole run only when the builder re-enters the same hang or the
|
|
412
437
|
worktree is broken; then discard the lane and re-dispatch (hard rule 7).
|
|
413
438
|
|
|
414
|
-
|
|
439
|
+
pi runs the `bash` tool directly with no sandbox, so the Codex-era
|
|
415
440
|
sandbox-specific hang sources don't apply — but long-running and interactive
|
|
416
|
-
commands still hang an unattended run
|
|
417
|
-
|
|
418
|
-
|
|
441
|
+
commands still hang an unattended run, and pi has no turn cap (there is no
|
|
442
|
+
`--max-turns` flag), so the dispatch's wall-clock timeout (TERM → grace →
|
|
443
|
+
KILL) is the run's bound, not a turn budget. Spec consequence: give every
|
|
444
|
+
potentially long command an explicit timeout in the lane-prompt, steer
|
|
419
445
|
builders toward the repo's existing test fixtures over hand-rolled long-running
|
|
420
446
|
harnesses, and when a gate needs a runtime that can't run unattended
|
|
421
447
|
(interactive prompts, servers without a timeout), have the builder record the
|
|
@@ -427,10 +453,9 @@ look broken).
|
|
|
427
453
|
|
|
428
454
|
## Manual alternative (human-driven)
|
|
429
455
|
|
|
430
|
-
Paste the lane-prompt into an interactive `
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
first. Use when the human wants to babysit a run.
|
|
456
|
+
Paste the lane-prompt into an interactive `pi` session (no `-p`). pi's agent
|
|
457
|
+
loop runs plan→act→test against the block's stopping condition while you watch
|
|
458
|
+
and steer. Use when the human wants to babysit a run.
|
|
434
459
|
|
|
435
460
|
## Lane-prompt template
|
|
436
461
|
|
|
@@ -516,22 +541,19 @@ against them, do not edit or work around) ===
|
|
|
516
541
|
|
|
517
542
|
## Builder-side standing setup (one time per machine/repo)
|
|
518
543
|
|
|
519
|
-
- The builder is the same `
|
|
544
|
+
- The builder is the same `pi` binary as the architect (reference harness),
|
|
520
545
|
running a cheaper model — nothing extra to install. `architect dispatch` pins
|
|
521
|
-
the model per dispatch (`--model <builder-model>`); a
|
|
522
|
-
`
|
|
523
|
-
default can't silently swap the builder.
|
|
524
|
-
- Repo `CLAUDE.md`
|
|
525
|
-
root-down automatically
|
|
526
|
-
the loop's PHASE rules stay in
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
There's no per-window quota that dies mid-run the way a chat session can, but
|
|
536
|
-
a long parallel fan-out does spend that pool. The architect runs as your
|
|
537
|
-
interactive Claude Code session.
|
|
546
|
+
the model per dispatch (`--model <builder-model>`); a default model in
|
|
547
|
+
`~/.pi/agent` config is fine interactively, but automations pin it explicitly
|
|
548
|
+
so a default can't silently swap the builder.
|
|
549
|
+
- Repo `AGENTS.md`/`CLAUDE.md` are the builder's standing context — pi loads
|
|
550
|
+
them root-down automatically (`--no-approve` does not gate them). Put exact
|
|
551
|
+
build/test commands and repo gotchas there; the loop's PHASE rules stay in
|
|
552
|
+
the dispatch block so they version with the skill.
|
|
553
|
+
- The builder is a bare `pi -p` over the block — it is not invoking the
|
|
554
|
+
`/architect` skills, the block is its entire instruction set.
|
|
555
|
+
- Billing: the builder draws on the provider account configured in
|
|
556
|
+
`~/.pi/agent` (per-token API usage — e.g. the Fireworks key). There's no
|
|
557
|
+
per-window quota that dies mid-run the way a chat session can, but a long
|
|
558
|
+
parallel fan-out does spend that account. The architect runs as your
|
|
559
|
+
interactive session.
|
data/skill/architect/research.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# Research fan-out reference
|
|
2
2
|
|
|
3
3
|
Read this only when a research trigger fires (see SKILL.md step 3). The fan-out
|
|
4
|
-
uses `architect research dispatch` to launch parallel
|
|
5
|
-
researchers (a cheaper model, read-only
|
|
4
|
+
uses `architect research dispatch` to launch parallel detached `pi -p --mode
|
|
5
|
+
json` researchers (a cheaper model, read-only by prompt contract — the
|
|
6
|
+
supervisor injects the builder guard, so git writes are denied) and
|
|
7
|
+
`architect research wait` to
|
|
6
8
|
collect their results. The architect keeps all judgment: it verifies
|
|
7
9
|
load-bearing claims and writes the iteration's **Grounds** section itself.
|
|
8
10
|
|
|
9
|
-
**Note:** the
|
|
10
|
-
synced separately and is NOT updated by this repo.
|
|
11
|
+
**Note:** the installed copy of this skill (`architect install-skills`
|
|
12
|
+
destination) is synced separately and is NOT updated by this repo.
|
|
11
13
|
|
|
12
14
|
## Fan out
|
|
13
15
|
|
|
@@ -48,10 +50,14 @@ Verbosity flags for `wait`:
|
|
|
48
50
|
- `--thinking`: reveal assistant thinking blocks
|
|
49
51
|
- `--jsonl`: emit raw lane-tagged JSONL instead of human text
|
|
50
52
|
|
|
51
|
-
The researchers are READ-ONLY by
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
The researchers are READ-ONLY by prompt contract (pi has no tool allow-list in
|
|
54
|
+
the loop's machinery — the lane prompt forbids writes, and the supervisor-injected
|
|
55
|
+
builder guard still denies git-write commands), so they cannot touch the repo.
|
|
56
|
+
Their final report
|
|
57
|
+
is extracted from the run's JSONL event stream (the final assistant message)
|
|
58
|
+
and written to `build/research/<id>/report.md` automatically by `wait`.
|
|
59
|
+
Research sessions land under `build/research/<id>/` (`run.jsonl` + pi session
|
|
60
|
+
files), like builder lanes' `build/<id>-<lane>/`.
|
|
55
61
|
|
|
56
62
|
## Research-prompt template
|
|
57
63
|
|
|
@@ -44,7 +44,7 @@ use LLM-designed, topic-specific decomposition rather than a fixed lane
|
|
|
44
44
|
taxonomy. Lanes are designed per topic, not taken from a template.
|
|
45
45
|
|
|
46
46
|
**Scout (brainstorm scale only):** dispatch ONE cheap researcher (~10
|
|
47
|
-
searches, same `
|
|
47
|
+
searches, same `pi -p` command as step 3) to map the terrain: canonical
|
|
48
48
|
terminology, the 5–10 load-bearing systems/papers/repos, the named people,
|
|
49
49
|
which source classes look rich vs empty, and the topic's natural fault lines.
|
|
50
50
|
The scout returns a map, not findings — discovering the topic's actual
|
|
@@ -70,26 +70,33 @@ dispatch. State the plan in a few lines; proceed unless the user redirects.
|
|
|
70
70
|
One fresh researcher per lane, each launched as its own **background Bash tool
|
|
71
71
|
call** (`run_in_background`) — one call per lane, not a shell `&` loop (a `&`
|
|
72
72
|
launcher orphans the lanes and the harness reaps them all at once). Read-only by
|
|
73
|
-
|
|
74
|
-
report is the
|
|
73
|
+
prompt contract (the injected builder guard still denies git writes); the
|
|
74
|
+
report is the extracted final assistant message (see `research.md`):
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
|
|
78
|
-
--
|
|
79
|
-
--
|
|
77
|
+
pi -p --mode json --model <researcher-model> \
|
|
78
|
+
--session-dir <space>/build/research/<NN>-<lane> \
|
|
79
|
+
--no-approve -e <guard> \
|
|
80
80
|
< build/research/<NN>-<lane>.prompt.md \
|
|
81
|
-
> build/research/<NN>-<lane
|
|
81
|
+
> build/research/<NN>-<lane>/run.jsonl 2>&1
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
(`architect research dispatch` runs this for you — it injects the supervisor-
|
|
85
|
+
copied guard and records the run; the block above is the transparency
|
|
86
|
+
equivalent. There is no `--max-turns`: scope the researcher with the prompt's
|
|
87
|
+
search budget, not a turn cap.)
|
|
88
|
+
|
|
84
89
|
Write each lane block to a `.prompt.md` file and feed it on stdin — never as a
|
|
85
90
|
shell argument; a quote-mangling shell will corrupt a big block, the stdin
|
|
86
91
|
redirect injects it verbatim.
|
|
87
92
|
|
|
88
|
-
(Web comes from
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
(Web comes from pi's loaded web tools — `web_search`/`fetch_content` from the
|
|
94
|
+
extensions configured in `~/.pi/agent`; no per-run setup. The read-only
|
|
95
|
+
discipline is the prompt contract plus the guard — a researcher that tries to
|
|
96
|
+
write or commit is denied, and a lane that misbehaves is killed and re-dispatched
|
|
97
|
+
with a tighter prompt. Launch ONE canary lane
|
|
91
98
|
and confirm it actually fetches live URLs before fanning out. These lane blocks
|
|
92
|
-
also run verbatim as read-only
|
|
99
|
+
also run verbatim as read-only subagents with web search if you'd rather
|
|
93
100
|
keep research inside the architect's own session.)
|
|
94
101
|
|
|
95
102
|
Every lane block carries the full contract — objective, output format, source
|
|
@@ -34,7 +34,7 @@ the loop.
|
|
|
34
34
|
judgment mode): arbitrates disagreements, writes and freezes iteration files,
|
|
35
35
|
calls kill/continue, merges builder output. Never writes implementation code.
|
|
36
36
|
- **builder** — the implementation role: a cheaper model run headless via
|
|
37
|
-
`architect dispatch` (reference harness: `
|
|
37
|
+
`architect dispatch` (reference harness: `pi -p`), one per lane in its own
|
|
38
38
|
worktree. Reports
|
|
39
39
|
raw evidence; never grades its own work; never edits `architecture/`.
|
|
40
40
|
|
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: 8.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Jacobs
|
|
@@ -261,13 +261,14 @@ files:
|
|
|
261
261
|
- lib/space_architect/cli/space.rb
|
|
262
262
|
- lib/space_architect/cli/src.rb
|
|
263
263
|
- lib/space_architect/conversations_client.rb
|
|
264
|
-
- lib/space_architect/dispatcher.rb
|
|
265
264
|
- lib/space_architect/gate_evaluator.rb
|
|
266
265
|
- lib/space_architect/gate_lint.rb
|
|
267
266
|
- lib/space_architect/harness.rb
|
|
268
267
|
- lib/space_architect/jobs_client.rb
|
|
268
|
+
- lib/space_architect/pi/builder-guard.ts
|
|
269
269
|
- lib/space_architect/research.rb
|
|
270
270
|
- lib/space_architect/research/mux.rb
|
|
271
|
+
- lib/space_architect/research/pi_events.rb
|
|
271
272
|
- lib/space_architect/research/registry.rb
|
|
272
273
|
- lib/space_architect/research/renderer.rb
|
|
273
274
|
- lib/space_architect/research/run.rb
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "harness"
|
|
4
|
-
|
|
5
|
-
module Space::Architect
|
|
6
|
-
# Thin backward-compat wrapper around Harness::ClaudeCodeHarness.
|
|
7
|
-
# Existing callers that construct Dispatcher.new(...).run(...) continue to work byte-for-byte.
|
|
8
|
-
class Dispatcher
|
|
9
|
-
# Keep constants here so any code referencing Dispatcher::ALLOWED_TOOLS still works.
|
|
10
|
-
ALLOWED_TOOLS = Harness::ClaudeCodeHarness::ALLOWED_TOOLS
|
|
11
|
-
DISALLOWED_TOOLS = Harness::ClaudeCodeHarness::DISALLOWED_TOOLS
|
|
12
|
-
|
|
13
|
-
def initialize(model: "claude-sonnet-4-6", max_turns: 200, claude_bin: nil)
|
|
14
|
-
@harness = Harness::ClaudeCodeHarness.new(model: model, max_turns: max_turns, bin: claude_bin)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def run(prompt_path:, run_log_path:, chdir:)
|
|
18
|
-
@harness.run(prompt_path: prompt_path, run_log_path: run_log_path, chdir: chdir)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def run_detached(prompt_path:, run_log_path:, chdir:)
|
|
22
|
-
@harness.run_detached(prompt_path: prompt_path, run_log_path: run_log_path, chdir: chdir)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|