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.
@@ -1,51 +1,58 @@
1
1
  # Builder dispatch reference
2
2
 
3
- Verified against the `claude` CLI (Claude Code) headless mode — the reference
4
- harness — June 2026. The builder is `claude -p` (`--print`, the non-interactive
5
- headless mode) pinned to the configured builder model (`<builder-model>`) — a
6
- cheaper model run headless via the same harness the architect uses. Key facts the
7
- skill encodes: lane-prompts go in on **stdin** (Claude Code has no `@file`, and a
8
- big quoted lane-prompt as a shell argument gets mangled); the model is pinned with
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 `-C`/working-dir flag**, so
11
- per-lane dispatch `cd`s into the worktree; permissions are the **tool
12
- allow/deny lists** (`--allowedTools`/`--disallowedTools`) plus
13
- `--permission-mode`, not a sandbox; web access is the built-in
14
- `WebSearch`/`WebFetch` tools (no extension to install).
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. Claude Code has
18
- **no automatic filesystem sandbox** in headless mode (the sandbox is opt-in via
19
- settings, off by default), so `.git` is not hardware-protected. "Builders never
20
- commit" (hard rule 7) is now enforced in three layers, weakest to strongest:
21
- (1) a runtime first line — deny the git-write tools with
22
- `--disallowedTools 'Bash(git commit:*)' …`; (2) worktree isolation between
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 rules are
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
- pattern match), so the post-flight `git log` is what the loop actually trusts.
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 `claude --version`, and confirm the
30
- builder model resolves with a one-shot
31
- (`echo ok | claude -p --model <builder-model> --max-turns 1`). No API key —
32
- the builder runs on your Claude plan — but note headless `claude -p` draws on
33
- the Agent SDK credit pool (separate from interactive usage since June 15 2026;
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), assembles the canonical
45
- `claude -p` argv, pins the builder model (the lane's
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
- `--output-format stream-json --verbose` output to
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 `claude` processes, and every lane dies at once with no
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; the
101
- # manual equivalent is that copy followed by:
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
- claude -p --model <builder-model> \
104
- --permission-mode acceptEdits \
105
- --allowedTools 'Read,Edit,Write,Grep,Glob,Bash,WebSearch,WebFetch' \
106
- --disallowedTools 'Bash(git commit:*),Bash(git push:*),Bash(git reset:*),Bash(git merge:*),Bash(git rebase:*),Bash(git checkout:*),Bash(git branch:*)' \
107
- --output-format stream-json --verbose \
108
- --max-turns 200 \
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
- `acceptEdits` auto-approves file writes; listing `Bash` in `--allowedTools`
114
- auto-approves shell commands so the run never blocks on a prompt; any tool *not*
115
- on the allow list is denied rather than prompted in `-p` mode (so the builder
116
- can't wander outside its toolset), and the `--disallowedTools` deny rules win
117
- over the allow list (deny always takes precedence) as the runtime first line
118
- against commits. Redirect stderr (`2>&1`) into the run-log so a dispatch error
119
- lands somewhere instead of vanishing.
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
- | claude -p --continue --model <builder-model> \
311
- --permission-mode acceptEdits \
312
- --allowedTools 'Read,Edit,Write,Grep,Glob,Bash,WebSearch,WebFetch' \
313
- --disallowedTools 'Bash(git commit:*),Bash(git push:*),Bash(git reset:*),Bash(git merge:*),Bash(git rebase:*),Bash(git checkout:*),Bash(git branch:*)' \
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` and translates it to the
344
- lane's harness — `claude-code` passes `low`…`max` straight through to its own
345
- `--effort` flag, unclamped (`minimal` clamps to `low`; `off` omits the flag).
346
- The escalation keywords (`think` < `think hard` < `think harder` <
347
- `ultrathink`) and the `MAX_THINKING_TOKENS` env var still raise depth from
348
- inside the block. Default unattended builder work to a high budget; downgrade
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
- - **Tool grant.** The default `--allowedTools` list is overridable per dispatch:
351
- `--allowed-tools <list>` replaces it, `--append-allowed-tools <list>` appends
352
- to it. The same is settable in the frozen lane declaration's ` ```lanes ` block
353
- — `allowed_tools:` (replaces) / `append_allowed_tools:` (appends) — so a lane
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, `claude -p --continue "<rulings +
372
- proceed>"` resumes that worktree's most recent session with full context —
373
- sessions are scoped per directory, so `--continue` (`-c`) is deterministic
374
- even with parallel lanes. (Alternatively pin `--session-id <uuid>` at
375
- dispatch and resume with `--resume <uuid>`.) Resume the **same way you
376
- dispatch** — one background Bash tool call per lane, each a single blocking
377
- `claude -p --continue …`, never a `&` loop (a `&` launcher orphans the
378
- resumed lanes exactly as it does fresh ones). Never resume across iterations —
379
- every iteration gets a fresh context.
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
- | claude -p --model <builder-model> --allowedTools 'Read,Grep,Glob'
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 `Bash` tool call (a `tool_use` for `Bash` with no
401
- matching `tool_result` yet). Silent gaps between events are normal model
402
- thinking; a shell command that should take seconds sitting in flight for 15+
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 `claude` PID
406
- (claude → shell → child). Hot-spinning (high CPU) or blocked (zero CPU and none
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 `claude` run. 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
- Claude Code runs the `Bash` tool directly with no sandbox, so the Codex-era
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. Spec consequence: give every potentially
417
- long command an explicit timeout in the lane-prompt (the `Bash` tool also takes
418
- a per-call timeout), cap the run with `--max-turns` as a loop backstop, steer
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 `claude` session (no `-p`). Claude
431
- Code's agent loop runs plan→act→test against the block's stopping condition
432
- while you watch and steer — approve tools as they come, or set `/permissions`
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 `claude` binary as the architect (reference harness),
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 `~/.claude/settings.json`
522
- `"model"` default is fine interactively, but automations pin it explicitly so a
523
- default can't silently swap the builder.
524
- - Repo `CLAUDE.md` is the builder's standing context — Claude Code loads it
525
- root-down automatically. Put exact build/test commands and repo gotchas there;
526
- the loop's PHASE rules stay in the dispatch block so they version with the
527
- skill. (Claude Code does **not** auto-read `AGENTS.md`; if the repo keeps its
528
- build/test docs there, add `@AGENTS.md` to `CLAUDE.md` to pull it in.)
529
- - The builder is a bare `claude -p` over the block — it is not invoking the
530
- `/architect` skills, the block is its entire instruction set. (`--bare` would
531
- give a leaner builder context but also drops `CLAUDE.md`/skills/hooks — keep
532
- `CLAUDE.md`, so skip `--bare` unless the repo has no standing build/test doc.)
533
- - Billing: headless `claude -p` draws on the Agent SDK credit pool on your
534
- Claude plan (separate from interactive usage limits since June 15 2026).
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.
@@ -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 read-only `claude -p`
5
- researchers (a cheaper model, read-only — no Edit/Write/Bash) and `architect research wait` to
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 `~/.claude/skills/architect-research/` copy of this skill is
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 toolset (`Read,Grep,Glob,WebSearch,WebFetch`
52
- with no Edit/Write/Bash) so they cannot touch the repo. Their final report
53
- is extracted from the terminal `result` event in the stream-json log and
54
- written to `build/research/<id>/report.md` automatically by `wait`.
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 `claude -p` command as step 3) to map the terrain: canonical
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
- toolset (`Read,Grep,Glob`) plus the web tools (`WebSearch,WebFetch`); the
74
- report is the redirected stdout:
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
- claude -p --model <researcher-model> \
78
- --allowedTools 'Read,Grep,Glob,WebSearch,WebFetch' \
79
- --max-turns 40 \
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>.md
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 the built-in `WebSearch` and `WebFetch` tools — no extension or
89
- key. With only the read-only + web tools on the allow list, every write/bash
90
- call is denied, so the researcher can't touch the repo. Launch ONE canary lane
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 Claude subagents with web search if you'd rather
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: `claude -p`), one per lane in its own
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: 7.1.0
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