space-architect 1.2.0 β 1.3.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/README.md +9 -0
- data/lib/space_architect/cli/architect.rb +26 -0
- data/lib/space_architect/skill_installer.rb +107 -0
- data/lib/space_architect/terminal.rb +15 -0
- data/lib/space_architect/version.rb +1 -1
- data/lib/space_architect.rb +1 -0
- data/skill/architect/SKILL.md +329 -0
- data/skill/architect/dispatch.md +308 -0
- data/skill/architect/research.md +89 -0
- data/skill/architect-research/SKILL.md +165 -0
- data/skill/architect-research/lanes.md +191 -0
- data/skill/architect-vocabulary/SKILL.md +141 -0
- data/vendor/repo-tender/lib/space_architect/pristine/cli.rb +1 -10
- data/vendor/repo-tender/lib/space_architect/pristine.rb +7 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db24aa20a68fdf981176f7e51a46794a4fc2ec5cef9765c33de5832f0a314a9e
|
|
4
|
+
data.tar.gz: 8912085239c76c1228e01d8986d789ba0fde42b4517459830c1f08cdb6e0fcfc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06a4e3382403542421679272136d6c1f6a99ac5dece354b5175f5d895b1afb9cce902f9e11ea2cf64c940dd4a00f11d3dfa7390a0941f387ae4a28f47cdbc3fb
|
|
7
|
+
data.tar.gz: 73c71c080f640540c4cfb57f347f12eae5a2486c45a373a525c44dc35ea54edf7b43ee415d979352f6569406f0f261e8d4a601af869eae5bd9f9055df02d9807
|
data/README.md
CHANGED
|
@@ -69,6 +69,7 @@ architect space status done # mark the current mission comp
|
|
|
69
69
|
**Architect Loop (run from inside a space):**
|
|
70
70
|
|
|
71
71
|
```sh
|
|
72
|
+
architect install-skills # install skills for your harness (once per machine)
|
|
72
73
|
architect init # scaffold ARCHITECT.md + architecture/
|
|
73
74
|
architect new <iteration> # scaffold next iteration file
|
|
74
75
|
architect dispatch <iteration> <lane> # dispatch a builder for a lane
|
|
@@ -77,6 +78,14 @@ architect freeze <iteration> # freeze Acceptance Criteria
|
|
|
77
78
|
architect verify <iteration> # post-flight mechanical checks
|
|
78
79
|
```
|
|
79
80
|
|
|
81
|
+
`architect install-skills` installs the bundled `architect`, `architect-research`,
|
|
82
|
+
and `architect-vocabulary` skills for a harness. (`architect-vocabulary` loads the
|
|
83
|
+
system's terms and a short orientation when you're in a space but don't want to run
|
|
84
|
+
the loop β see [The Architect Loop](#the-architect-loop-).) Default is `claude`
|
|
85
|
+
(`~/.claude/skills/`); use `--provider
|
|
86
|
+
opencode|codex|pi` for other harnesses, and `--project` to install into the current
|
|
87
|
+
directory instead of globally. See the [command reference](docs/reference.md) for details.
|
|
88
|
+
|
|
80
89
|
## Usage π°οΈ
|
|
81
90
|
|
|
82
91
|
```sh
|
|
@@ -348,6 +348,31 @@ module SpaceArchitect
|
|
|
348
348
|
end
|
|
349
349
|
end
|
|
350
350
|
|
|
351
|
+
class InstallSkills < Dry::CLI::Command
|
|
352
|
+
include GlobalOptions
|
|
353
|
+
include Helpers
|
|
354
|
+
|
|
355
|
+
desc "Install bundled skills (architect, architect-research, architect-vocabulary) for a harness"
|
|
356
|
+
option :provider, default: "claude", desc: "Harness: claude, codex, opencode, pi"
|
|
357
|
+
option :project, type: :boolean, default: false, desc: "Install to CWD instead of global"
|
|
358
|
+
option :force, type: :boolean, default: false, desc: "Overwrite existing skills that differ"
|
|
359
|
+
option :dry_run, type: :boolean, default: false, desc: "Print what would happen without writing files"
|
|
360
|
+
|
|
361
|
+
def call(provider: "claude", project: false, force: false, dry_run: false, **opts)
|
|
362
|
+
setup_terminal(**opts.slice(:color, :colors))
|
|
363
|
+
handle_errors do
|
|
364
|
+
result = SkillInstaller.install(provider, project: project, force: force,
|
|
365
|
+
env: project_config.env, dry_run: dry_run)
|
|
366
|
+
verb = dry_run ? "Would install" : "Installed"
|
|
367
|
+
terminal.say "#{verb} skills for #{provider} β #{terminal.path(result[:dest_root])}"
|
|
368
|
+
result[:skills].each do |s|
|
|
369
|
+
terminal.say " #{s[:name]}: #{terminal.style_skill_action(s[:action])} (#{terminal.path(s[:path])})"
|
|
370
|
+
end
|
|
371
|
+
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
351
376
|
module Brief
|
|
352
377
|
class New < Dry::CLI::Command
|
|
353
378
|
include GlobalOptions
|
|
@@ -559,6 +584,7 @@ SpaceArchitect::CLI::Registry.register "evidence", SpaceArchitect::CLI::Archite
|
|
|
559
584
|
SpaceArchitect::CLI::Registry.register "merge", SpaceArchitect::CLI::Architect::Merge
|
|
560
585
|
SpaceArchitect::CLI::Registry.register "integrate", SpaceArchitect::CLI::Architect::Integrate
|
|
561
586
|
SpaceArchitect::CLI::Registry.register "gate", SpaceArchitect::CLI::Architect::Gate
|
|
587
|
+
SpaceArchitect::CLI::Registry.register "install-skills", SpaceArchitect::CLI::Architect::InstallSkills
|
|
562
588
|
SpaceArchitect::CLI::Registry.register "brief" do |b|
|
|
563
589
|
b.register "new", SpaceArchitect::CLI::Architect::Brief::New
|
|
564
590
|
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "pathname"
|
|
5
|
+
|
|
6
|
+
module SpaceArchitect
|
|
7
|
+
module SkillInstaller
|
|
8
|
+
PROVIDERS = %w[claude codex opencode pi].freeze
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def source_root
|
|
12
|
+
Pathname.new(__dir__).parent.parent.join("skill")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def dest_root(provider, project:, env:, cwd: Dir.pwd)
|
|
16
|
+
case provider.to_s
|
|
17
|
+
when "claude"
|
|
18
|
+
base = project ? Pathname.new(cwd) : Pathname.new(XDG.home(env: env))
|
|
19
|
+
base.join(".claude", "skills")
|
|
20
|
+
when "codex"
|
|
21
|
+
base = project ? Pathname.new(cwd) : Pathname.new(XDG.home(env: env))
|
|
22
|
+
base.join(".agents", "skills")
|
|
23
|
+
when "opencode"
|
|
24
|
+
project ? Pathname.new(cwd).join(".opencode", "skills") : XDG.config_home(env: env).join("skills")
|
|
25
|
+
when "pi"
|
|
26
|
+
base = project ? Pathname.new(cwd) : Pathname.new(pi_agent_dir(env: env))
|
|
27
|
+
base.join("skills")
|
|
28
|
+
else
|
|
29
|
+
raise Error, "Unknown provider '#{provider}'. Expected one of: #{PROVIDERS.join(', ')}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def install(provider, project:, force:, env:, cwd: Dir.pwd, dry_run: false)
|
|
34
|
+
validate_provider!(provider)
|
|
35
|
+
dest = dest_root(provider, project: project, env: env, cwd: cwd)
|
|
36
|
+
results = []
|
|
37
|
+
|
|
38
|
+
source_skills.each do |skill_dir|
|
|
39
|
+
name = skill_dir.basename.to_s
|
|
40
|
+
skill_dest = dest.join(name)
|
|
41
|
+
results << install_skill(skill_dir, skill_dest, force: force, dry_run: dry_run)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
{ dest_root: dest, skills: results, dry_run: dry_run }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def source_skills
|
|
48
|
+
source_root.children.select(&:directory?)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def validate_provider!(provider)
|
|
54
|
+
return if PROVIDERS.include?(provider.to_s)
|
|
55
|
+
|
|
56
|
+
raise Error, "Unknown provider '#{provider}'. Expected one of: #{PROVIDERS.join(', ')}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def pi_agent_dir(env:)
|
|
60
|
+
Pathname.new(env.fetch("PI_CODING_AGENT_DIR", File.join(XDG.home(env: env), ".pi", "agent")))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def install_skill(source, dest, force:, dry_run:)
|
|
64
|
+
name = source.basename.to_s
|
|
65
|
+
|
|
66
|
+
if dest.exist?
|
|
67
|
+
if same_content?(source, dest)
|
|
68
|
+
return { name: name, action: :unchanged, path: dest }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
unless force
|
|
72
|
+
return { name: name, action: :conflict, path: dest } if dry_run
|
|
73
|
+
|
|
74
|
+
raise Error,
|
|
75
|
+
"Refusing to overwrite existing skill at #{dest}. Re-run with --force."
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless dry_run
|
|
79
|
+
FileUtils.rm_rf(dest)
|
|
80
|
+
FileUtils.cp_r(source, dest)
|
|
81
|
+
end
|
|
82
|
+
{ name: name, action: dry_run ? :would_update : :updated, path: dest }
|
|
83
|
+
else
|
|
84
|
+
unless dry_run
|
|
85
|
+
FileUtils.mkdir_p(dest.parent)
|
|
86
|
+
FileUtils.cp_r(source, dest)
|
|
87
|
+
end
|
|
88
|
+
{ name: name, action: dry_run ? :would_install : :installed, path: dest }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def same_content?(source, dest)
|
|
93
|
+
return false unless dest.directory?
|
|
94
|
+
|
|
95
|
+
source_files = Dir.glob("#{source}/**/*").reject { |f| File.directory?(f) }
|
|
96
|
+
dest_files = Dir.glob("#{dest}/**/*").reject { |f| File.directory?(f) }
|
|
97
|
+
|
|
98
|
+
return false if source_files.length != dest_files.length
|
|
99
|
+
|
|
100
|
+
source_files.sort.zip(dest_files.sort).all? do |sf, df|
|
|
101
|
+
rel = sf.sub("#{source}/", "")
|
|
102
|
+
df.end_with?(rel) && File.read(sf) == File.read(df)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -74,6 +74,21 @@ module SpaceArchitect
|
|
|
74
74
|
end.wait
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
def style_skill_action(action)
|
|
78
|
+
case action.to_s
|
|
79
|
+
when "installed", "updated"
|
|
80
|
+
pastel.green(action)
|
|
81
|
+
when "would_install", "would_update"
|
|
82
|
+
pastel.cyan(action)
|
|
83
|
+
when "unchanged"
|
|
84
|
+
pastel.bright_black(action)
|
|
85
|
+
when "conflict"
|
|
86
|
+
pastel.yellow(action)
|
|
87
|
+
else
|
|
88
|
+
action.to_s
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
77
92
|
private
|
|
78
93
|
|
|
79
94
|
def color_mode
|
data/lib/space_architect.rb
CHANGED
|
@@ -19,6 +19,7 @@ require_relative "space_architect/git_client"
|
|
|
19
19
|
require_relative "space_architect/mise_client"
|
|
20
20
|
require_relative "space_architect/space_store"
|
|
21
21
|
require_relative "space_architect/shell_integration"
|
|
22
|
+
require_relative "space_architect/skill_installer"
|
|
22
23
|
require_relative "space_architect/terminal"
|
|
23
24
|
require_relative "space_architect/harness"
|
|
24
25
|
require_relative "space_architect/dispatcher"
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architect
|
|
3
|
+
description: >
|
|
4
|
+
Run the Architect Loop: Opus 4.8 in Claude Code is the ARCHITECT β judgment
|
|
5
|
+
only: arbitration, judging raw evidence against frozen Acceptance Criteria,
|
|
6
|
+
splitting iterations into disjoint lanes, kill/continue calls. The BUILDERS
|
|
7
|
+
are 1-4 parallel Sonnet 4.6 agents run headless via `claude -p`, each in its
|
|
8
|
+
own git worktree; the architect reviews, merges, and integrates their work.
|
|
9
|
+
The space is the memory: one file per iteration at
|
|
10
|
+
architecture/I<NN>-<name>.md (Grounds / Specification / Acceptance Criteria /
|
|
11
|
+
Builder Prompt / Builder Report / Verdict), indexed by
|
|
12
|
+
architecture/ARCHITECT.md; a mission spans the repos under repos/. Use when
|
|
13
|
+
asked to "architect", "run the loop", "next iteration", "judge the builder's
|
|
14
|
+
work", or at the start of a work block in a space using the handoff system.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Architect
|
|
18
|
+
|
|
19
|
+
You are the ARCHITECT (Opus 4.8 in Claude Code). Sonnet 4.6 via headless
|
|
20
|
+
`claude -p` is the BUILDER β the same harness, one tier down. The space is the
|
|
21
|
+
memory β mission artifacts live in the space's `architecture/` dir (committed),
|
|
22
|
+
scratch in `build/` (gitignored); the mission spans the repos under `repos/`.
|
|
23
|
+
Your output is judgment and a dispatch β never implementation code. When you
|
|
24
|
+
have enough information to act, act.
|
|
25
|
+
|
|
26
|
+
Each iteration is **one self-contained file**,
|
|
27
|
+
`architecture/I<NN>-<name>.md` (`<NN>` = zero-padded ordinality), grown section
|
|
28
|
+
by section. The `architect` CLI writes and commits each section for you with the
|
|
29
|
+
canonical message β the commits give the differentiation and git gives the
|
|
30
|
+
change guarantees, so there are no separate `gates/`, `lanes/`, or `prd/` dirs:
|
|
31
|
+
|
|
32
|
+
| Section | Holds | How you persist it |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| **Grounds** | why β research/brief distilled (optional) | `architect section <it> grounds --from <f>` |
|
|
35
|
+
| **Specification** | what/how β the full delegation contract | `architect section <it> specification --from <f>` |
|
|
36
|
+
| **Acceptance Criteria** | proof β exact gate commands + thresholds | `architect freeze <it>` βοΈ **= the freeze** |
|
|
37
|
+
| **Builder Prompt** | the exact lane-prompt(s) dispatched | `architect section <it> prompt --append --lane <l> --from <f>` |
|
|
38
|
+
| **Builder Report** | raw evidence, transcribed verbatim from scratch | `architect evidence <it> --lane <l>` |
|
|
39
|
+
| **Verdict** | rulings + per-AC PASS/FAIL/INVALID + KILL/CONTINUE | `architect section <it> verdict --from <f>` (later session) |
|
|
40
|
+
|
|
41
|
+
Each command writes the section, commits it with the canonical `I<NN>: β¦`
|
|
42
|
+
message, and prints back what changed (SHA + diff stat; `freeze` prints the
|
|
43
|
+
frozen AC; `evidence` echoes the builder's STATUS line) β so you don't hand-edit
|
|
44
|
+
the file or run a separate `git add`/`commit`, and you don't run three follow-ups
|
|
45
|
+
to see what happened. You still author the *content*; the CLI owns the
|
|
46
|
+
*persistence*.
|
|
47
|
+
|
|
48
|
+
The builder **never** writes this file β the Acceptance Criteria must stay out
|
|
49
|
+
of its editable blast radius. Each lane builder writes raw evidence to a scratch
|
|
50
|
+
report in `build/<id>-<lane>/report.md`; `architect evidence` transcribes it
|
|
51
|
+
**verbatim** into Builder Report. Frozen sections
|
|
52
|
+
(Grounds/Specification/Acceptance Criteria) are read-only after the freeze
|
|
53
|
+
commit β `architect section` refuses to write a frozen section once frozen; only
|
|
54
|
+
Builder Prompt, Builder Report, and Verdict are appended after.
|
|
55
|
+
|
|
56
|
+
**The mission brief (`architecture/BRIEF.md`).** A mission with a durable spec
|
|
57
|
+
carries one brief β numbered Β§sections (Β§1 goal, Β§2 constraints, β¦ Β§N definition
|
|
58
|
+
of done) that span iterations. Every iteration's Grounds/Specification/Acceptance
|
|
59
|
+
Criteria/Verdict cites it as **BRIEF Β§N** (e.g. `(BRIEF Β§3.1)`), the way each gate
|
|
60
|
+
addresses its intent back to one frozen reference: the Acceptance Criteria table
|
|
61
|
+
carries a `Brief Β§` column, the Specification Objective cites it, the Verdict
|
|
62
|
+
reads "diff vs BRIEF Β§1/Β§3.3 β CONTINUE". Scaffold it with `architect brief new`.
|
|
63
|
+
The brief is frozen at the mission level β edits to a Β§section are logged
|
|
64
|
+
decisions in `ARCHITECT.md`, never silent per-iteration drift. Discovery missions
|
|
65
|
+
that are still finding their shape defer the brief, cite per-iteration Grounds,
|
|
66
|
+
and promote the consolidated picture into BRIEF.md once it stabilizes.
|
|
67
|
+
|
|
68
|
+
Full rationale and citations: `DESIGN.md` in this skill's repo. Exact dispatch
|
|
69
|
+
commands and the lane-prompt template: `dispatch.md` next to this file. To load
|
|
70
|
+
this system's vocabulary without running the loop (e.g. when working *on* the
|
|
71
|
+
skill), invoke the `/architect-vocabulary` skill β it is the glossary, not the
|
|
72
|
+
loop.
|
|
73
|
+
|
|
74
|
+
## Hard rules
|
|
75
|
+
|
|
76
|
+
1. **Never write implementation code.** Anything that must change goes in the
|
|
77
|
+
iteration Specification.
|
|
78
|
+
2. **Not in the space's committed architecture = didn't happen.** Refuse to
|
|
79
|
+
judge results that exist only in conversation or builder chat output.
|
|
80
|
+
3. **The Acceptance Criteria freeze before results exist** β written as the
|
|
81
|
+
iteration file's Acceptance Criteria section and committed (the freeze commit)
|
|
82
|
+
*before* dispatch. Quote them verbatim when judging, reading from the freeze
|
|
83
|
+
commit (`git show <freeze-sha>:architecture/I<NN>-<name>.md`); never restate
|
|
84
|
+
from memory; never edit after results. Any change to
|
|
85
|
+
Grounds/Specification/Acceptance Criteria lines since the freeze (caught by
|
|
86
|
+
`git diff <freeze-sha> HEAD`) is an automatic iteration FAIL β only Builder
|
|
87
|
+
Prompt/Report/Verdict may be appended afterward.
|
|
88
|
+
4. **Nobody grades their own work.** The builder reports raw evidence only (to
|
|
89
|
+
scratch); you transcribe it, run the gates yourself, and read the output β
|
|
90
|
+
builder claims are hearsay. You never judge a run in the same session that
|
|
91
|
+
dispatched it.
|
|
92
|
+
5. **Disagreement is mandatory.** Builder PHASE 0 must raise disagreements
|
|
93
|
+
citing real files; silent compliance = defect. You rule on every one in the
|
|
94
|
+
Verdict: ACCEPT / REJECT / MODIFY + one line why. Flag the human's scope
|
|
95
|
+
creep and goalpost-moving bluntly too.
|
|
96
|
+
6. **Audit every status claim** β yours and the builder's β against a tool
|
|
97
|
+
result from the session before reporting it.
|
|
98
|
+
7. **Fresh builder context per lane, worktree isolation between lanes.**
|
|
99
|
+
`claude -p --continue` (from the lane's worktree) only for follow-ups within
|
|
100
|
+
the current lane. Builders never commit β Claude Code has no sandbox to
|
|
101
|
+
enforce that, so verify it yourself post-flight (`git -C <worktree> log
|
|
102
|
+
<repo-base>..` must be empty). If a run leaves a worktree broken or
|
|
103
|
+
committed, discard that lane + re-dispatch over rescue prompting β lanes are
|
|
104
|
+
cheap by construction.
|
|
105
|
+
8. **Stop conditions:** failing verification you can't root-cause, instructions
|
|
106
|
+
conflicting with project docs, irreversible/destructive calls, or scope
|
|
107
|
+
growth beyond the iteration β checkpoint to the handoff and ask the human.
|
|
108
|
+
|
|
109
|
+
## Procedure
|
|
110
|
+
|
|
111
|
+
### 0. Ground (every session β never skip because the task "looks small")
|
|
112
|
+
|
|
113
|
+
- Read the project's operating docs in authority order: `CLAUDE.md` /
|
|
114
|
+
`AGENTS.md` β `README.md` β architecture docs. Learn the exact verification
|
|
115
|
+
gate (test/lint/typecheck/build commands) from docs or CI config.
|
|
116
|
+
- Once per environment: `claude --version` and confirm the builder model
|
|
117
|
+
resolves (`echo ok | claude -p --model claude-sonnet-4-6 --max-turns 1`;
|
|
118
|
+
details in `dispatch.md`). First dispatch in a new environment is a canary β
|
|
119
|
+
confirm it starts cleanly before fanning out.
|
|
120
|
+
- Read `architecture/ARCHITECT.md` (the cross-iteration table of contents),
|
|
121
|
+
`architecture/BRIEF.md` if present (the durable Β§-numbered mission contract you
|
|
122
|
+
cite as BRIEF Β§N), and the iteration file `architecture/I<NN>-<name>.md` for any
|
|
123
|
+
in-flight iteration. If `ARCHITECT.md` is missing, run `architect init` (scaffolds
|
|
124
|
+
`architecture/ARCHITECT.md` and the `architect:` block in `space.yaml`,
|
|
125
|
+
commits). Keep the handoff a short TOC (~150 lines): TL;DR + repos in scope +
|
|
126
|
+
an iteration index pointing at each iteration file; per-iteration detail lives
|
|
127
|
+
in the iteration file, never duplicated into the handoff. `architect status`
|
|
128
|
+
prints mission state (iterations, freeze_shas, lanes, verdicts) at any point.
|
|
129
|
+
- **Space setup (first time):** `architect space new "Mission Name" org/repo β¦`
|
|
130
|
+
(repos are variadic positionals after the title), then `architect init` inside
|
|
131
|
+
the space to scaffold `architecture/ARCHITECT.md`.
|
|
132
|
+
- Scale to the task: trivial fixes don't need the loop β say so and let the
|
|
133
|
+
human do it inline or in a normal session. The loop is for iteration-sized
|
|
134
|
+
work.
|
|
135
|
+
|
|
136
|
+
### 1. Arbitrate
|
|
137
|
+
|
|
138
|
+
Every open disagreement from the last iteration's Builder Report gets
|
|
139
|
+
**ACCEPT / REJECT / MODIFY + one line why**, written into that iteration's
|
|
140
|
+
Verdict. No deferrals.
|
|
141
|
+
|
|
142
|
+
### 2. Judge
|
|
143
|
+
|
|
144
|
+
Read the frozen Acceptance Criteria from the freeze commit (`architect freeze`
|
|
145
|
+
re-prints them, or `git show <freeze-sha>:architecture/I<NN>-<name>.md`). For each
|
|
146
|
+
gate: run the gate command yourself β `architect gate <iteration>` runs the
|
|
147
|
+
frozen gate commands in the resolved repo/worktree and streams raw output (it is a
|
|
148
|
+
runner, never a judge), or run them by hand β then compare the output against the
|
|
149
|
+
verbatim frozen text β **PASS / FAIL / INVALID** (INVALID = not measured the way
|
|
150
|
+
the gate specifies). Check `git diff <freeze-sha> HEAD --
|
|
151
|
+
architecture/I<NN>-<name>.md` β any change to Grounds/Specification/Acceptance
|
|
152
|
+
Criteria lines is an automatic FAIL.
|
|
153
|
+
Gate-pass is necessary, not sufficient: read the diff against the
|
|
154
|
+
Specification's intent **and the cited BRIEF Β§sections** before the verdict β
|
|
155
|
+
test-passing changes are frequently
|
|
156
|
+
unmergeable, and iterating against visible tests is a known gaming vector. Read
|
|
157
|
+
for **idiomaticity and style**, not just correctness: does the code match the
|
|
158
|
+
target repo's house conventions (naming, guards, predicates, error/persistence
|
|
159
|
+
idioms, the language's expressive forms), stay well-factored and DRY-ish, avoid
|
|
160
|
+
needless repetition or abstraction it doesn't need, and read like the
|
|
161
|
+
surrounding code? A gate-green diff that fights the house style β or introduces
|
|
162
|
+
an inconsistency a careful reader of that repo would never write β is a defect:
|
|
163
|
+
flag it in the Verdict with file:line, and weight it in a head-to-head. (Models
|
|
164
|
+
are strong on *local* idiom and restraint but weak on *structural*
|
|
165
|
+
re-derivation β the simplification that re-sees the shape is often the
|
|
166
|
+
architect's or human's to name.) Then
|
|
167
|
+
one iteration-level call: **KILL / CONTINUE**, with the single decisive reason,
|
|
168
|
+
written into the Verdict. For high-stakes iterations
|
|
169
|
+
(schema/API/persistence/security), add a review before the verdict. You
|
|
170
|
+
(Opus 4.8) reading the diff is already a stronger-model, fresh-context pass
|
|
171
|
+
over the Sonnet builder's work β a cross-tier read, though not cross-vendor
|
|
172
|
+
(both are Claude Code). For an extra adversarial pass, pipe the diff to a fresh
|
|
173
|
+
read-only `claude -p` reviewer (command in `dispatch.md`) or a
|
|
174
|
+
fresh-context subagent prompted to break confidence β calibrated to flag only
|
|
175
|
+
correctness/requirement/invariant gaps with file:line evidence, no style.
|
|
176
|
+
|
|
177
|
+
**Variant sets β human in the loop.** When the iteration was built as a variant
|
|
178
|
+
set (multiple `(harness, model)` lanes over one frozen spec), judge every
|
|
179
|
+
variant against the same frozen AC, then **do not pick the winner unilaterally**
|
|
180
|
+
β assume the human wants to be involved in selection. Present the head-to-head:
|
|
181
|
+
per-variant gate results plus the deltas that decide it (correctness/invariants,
|
|
182
|
+
idiomaticity + house-style, tests, user-facing behavior), with your
|
|
183
|
+
recommendation and its reasoning. Let the human choose (`AskUserQuestion` or a
|
|
184
|
+
checkpoint); you then execute the promote/merge they pick. Surface a
|
|
185
|
+
recommendation β the call is theirs. (A standing handoff that pre-delegates the
|
|
186
|
+
pick still gets the comparison surfaced before you act on it.)
|
|
187
|
+
|
|
188
|
+
### 3. Research fan-out (optional β most iterations skip this)
|
|
189
|
+
|
|
190
|
+
Two scales, two routes:
|
|
191
|
+
|
|
192
|
+
- **Discovery scale** β brainstorming what to build, technology selection,
|
|
193
|
+
state-of-the-art surveys β invoke the `/architect-research` skill (a scout
|
|
194
|
+
researcher maps the topic, the orchestrator designs topic-specific parallel
|
|
195
|
+
researcher lanes, claims verified against sources, synthesized into a cited
|
|
196
|
+
report). Its report then distills into `architecture/BRIEF.md` Β§sections when
|
|
197
|
+
it is mission-scope (a durable contract that spans iterations), or the
|
|
198
|
+
iteration's **Grounds** section when it is iteration-scope.
|
|
199
|
+
- **Iteration scale** β run the inline fan-out below only when at least one
|
|
200
|
+
trigger holds: (a) the iteration depends on external APIs, libraries, or
|
|
201
|
+
versions not already used in the target repo; (b) a narrow approach choice
|
|
202
|
+
needs facts neither you nor the repo has; (c) the human asked
|
|
203
|
+
(`/architect research: <question>`). Otherwise skip β the builder's
|
|
204
|
+
verify-against-reality requirement already covers routine API checks, and
|
|
205
|
+
researching well-understood iterations is pure cost.
|
|
206
|
+
|
|
207
|
+
When a trigger fires, read `research.md` next to this file and follow it:
|
|
208
|
+
3β5 narrow non-overlapping questions β parallel read-only `claude -p`
|
|
209
|
+
researchers (built-in `WebSearch`/`WebFetch`) in the background β you
|
|
210
|
+
adversarially verify the load-bearing claims β you write the iteration's
|
|
211
|
+
**Grounds** section with citations and commit it. Researchers gather; you judge
|
|
212
|
+
and write Grounds. Findings without a source URL don't enter Grounds.
|
|
213
|
+
|
|
214
|
+
### 4. Spec the next iteration
|
|
215
|
+
|
|
216
|
+
One-PR-sized. Run `architect new <name>` to scaffold
|
|
217
|
+
`architecture/I<NN>-<name>.md` (it allocates the next ordinal and records the
|
|
218
|
+
iteration in `space.yaml`), then write the **Specification** section with
|
|
219
|
+
`architect section <name> specification --from <file>` β the full delegation
|
|
220
|
+
contract, self-contained:
|
|
221
|
+
|
|
222
|
+
- **Objective** β what to build and why (give the reason, not just the ask).
|
|
223
|
+
Cite **BRIEF Β§N** for durable context and a Grounds section for
|
|
224
|
+
iteration-local research, rather than restating either.
|
|
225
|
+
- **Output format** β what the builder reports: raw tables, numbers, commit
|
|
226
|
+
SHAs, test output paths. No interpretation.
|
|
227
|
+
- **Tool guidance** β the exact verification commands for the target repo, and
|
|
228
|
+
the specific APIs/formats/versions the builder must verify against the live
|
|
229
|
+
dependencies *before* writing code.
|
|
230
|
+
- **Boundaries** β files it may touch, files it must not, explicit out-of-scope
|
|
231
|
+
list, "no placeholders; search before implementing", no refactors beyond the
|
|
232
|
+
task.
|
|
233
|
+
- **Lane plan** β split the iteration into 1β4 parallel lanes, each declaring
|
|
234
|
+
its **target repo + file-touch set, checked for overlap**: name the repo
|
|
235
|
+
(`repos/<repo>`) and every file each lane may touch. Lanes in *different*
|
|
236
|
+
repos are inherently disjoint; same-repo lanes with any file overlap run as
|
|
237
|
+
one. Each lane gets its own objective, output format, and boundaries. Most
|
|
238
|
+
iterations are one lane β fan out only when the work is genuinely parallel (a
|
|
239
|
+
cross-repo mission often is).
|
|
240
|
+
- **Effort call** β thinking budget set in the lane-prompt via the escalation
|
|
241
|
+
keywords (`think hard` β¦ `ultrathink`); default unattended builder work high,
|
|
242
|
+
downgrade a routine, tightly-specified lane (record which and why). Claude
|
|
243
|
+
Code has no per-invocation effort flag β see `dispatch.md`.
|
|
244
|
+
|
|
245
|
+
Then write the **Acceptance Criteria** section β exact gate commands +
|
|
246
|
+
thresholds, each row carrying a `Brief Β§` column that addresses it back to
|
|
247
|
+
intent β and run `architect freeze <name>`. What must be frozen before dispatch
|
|
248
|
+
is the Acceptance Criteria: `architect freeze` commits any pending content in the
|
|
249
|
+
frozen region (Grounds/Specification/Acceptance Criteria) in one freeze commit,
|
|
250
|
+
records the `freeze_sha` in `space.yaml`, and prints the frozen AC back; **that
|
|
251
|
+
commit is the freeze** βοΈ and is the last thing before dispatch. You needn't
|
|
252
|
+
sequence Grounds and Specification into separate commits first β the freeze
|
|
253
|
+
snapshots the whole frozen region and refuses to re-freeze once a frozen section
|
|
254
|
+
changed afterward.
|
|
255
|
+
|
|
256
|
+
### 5. Dispatch (one fresh `claude -p` per lane, worktree-isolated)
|
|
257
|
+
|
|
258
|
+
Per the mechanics in `dispatch.md`:
|
|
259
|
+
|
|
260
|
+
- **1 lane** β dispatch in the target repo's checkout (`repos/<repo>`).
|
|
261
|
+
- **2β4 lanes** β `architect worktree add <repo> <iteration> <lane>
|
|
262
|
+
[--base <repo-base>]` per lane (creates `build/<id>-<lane>/wt` off the target
|
|
263
|
+
repo's base commit β a repo commit, distinct from the freeze, which is a
|
|
264
|
+
space commit β and records it in `space.yaml`).
|
|
265
|
+
|
|
266
|
+
Assemble each lane's lane-prompt (the template in `dispatch.md` + this lane's
|
|
267
|
+
section of the Specification + the frozen Acceptance Criteria) and write it to
|
|
268
|
+
`build/<id>-<lane>/prompt.md` (fed to the builder on stdin); record it in the
|
|
269
|
+
iteration file's **Builder Prompt** section β the dispatched-prompt provenance β
|
|
270
|
+
with `architect section <iteration> prompt --append --lane <lane> --from
|
|
271
|
+
build/<id>-<lane>/prompt.md`. Then run `architect dispatch <iteration> <lane>` β it assembles the
|
|
272
|
+
canonical `claude -p` argv, pins the model, and streams stream-json to
|
|
273
|
+
`build/<id>-<lane>/run.jsonl`. Launch one dispatch per worktree β each as its
|
|
274
|
+
**own background Bash tool call** (your harness's `run_in_background`), **not**
|
|
275
|
+
shell `&`. The harness keeps each lane alive for its full run and notifies you
|
|
276
|
+
per lane; a `for β¦ & done` launcher instead orphans the lanes and the harness
|
|
277
|
+
reaps them all at once (see `dispatch.md`). Each lane builds only its declared
|
|
278
|
+
files and writes raw results to `build/<id>-<lane>/report.md` β it never
|
|
279
|
+
touches `architecture/`, so lanes never collide and the Acceptance Criteria
|
|
280
|
+
stay untouchable.
|
|
281
|
+
|
|
282
|
+
Do not block β end the turn or do other judgment work; multi-hour runs are
|
|
283
|
+
normal. Print the lane-prompts too, so the human can run any lane in an
|
|
284
|
+
interactive `claude` session instead. Whenever you return to a running lane,
|
|
285
|
+
check liveness: the lane's `run.jsonl` must still be growing. If it has been
|
|
286
|
+
silent 15+ minutes on one in-flight command, follow "Stall detection and
|
|
287
|
+
rescue" in `dispatch.md` β kill the stuck child process, not the run.
|
|
288
|
+
|
|
289
|
+
### 6. Post-flight and integrate (when the runs complete)
|
|
290
|
+
|
|
291
|
+
`architect verify <iteration>` REPORTS (it never judges) per lane: frozen
|
|
292
|
+
sections untouched, no builder commits, scratch report present, in-bounds.
|
|
293
|
+
Confirm each yourself with evidence: (a) the scratch report has raw results
|
|
294
|
+
only, (b) PHASE 0 disagreements were raised (silent compliance = defect to
|
|
295
|
+
log), (c) the iteration file's frozen sections are untouched β `git diff
|
|
296
|
+
<freeze-sha> HEAD -- architecture/I<NN>-<name>.md` shows no change to
|
|
297
|
+
Grounds/Specification/Acceptance Criteria, (d) `git status` in the worktree
|
|
298
|
+
shows **only files inside the lane's declared set** β an out-of-bounds write
|
|
299
|
+
fails the lane, (e) `git -C <worktree> log <repo-base>..` is empty β a builder
|
|
300
|
+
commit means a tampered worktree (reset and re-dispatch).
|
|
301
|
+
|
|
302
|
+
**Transcribe** each lane's scratch report into the **Builder Report** section
|
|
303
|
+
with `architect evidence <iteration> --lane <lane>` β it copies
|
|
304
|
+
`build/<id>-<lane>/report.md` **verbatim** (byte-for-byte, no interpretation),
|
|
305
|
+
commits it, and echoes the builder's STATUS line. The builder never wrote into
|
|
306
|
+
`architecture/`; the CLI transcribes, preserving raw-results-only.
|
|
307
|
+
|
|
308
|
+
**Then integrate** β you decide which lanes pass, the CLI does the git
|
|
309
|
+
mechanics. `architect integrate <iteration> --lanes <passing-set>` commits each
|
|
310
|
+
named lane on its branch and merges it `--no-ff` into the repo's integration
|
|
311
|
+
branch `lane/<iteration>`, in order; it **refuses** a lane that left builder
|
|
312
|
+
commits or wrote out-of-bounds, and stops on a merge conflict β which means the
|
|
313
|
+
lane plan wasn't disjoint, a spec defect: kill the conflicting lane and re-spec
|
|
314
|
+
it (never hand-resolve). Then run `architect gate <iteration>` against the
|
|
315
|
+
integration branch as a smoke check (raw output; the verdict stays yours). A
|
|
316
|
+
cross-repo mission yields one `lane/<iteration>` branch per touched repo. Update
|
|
317
|
+
the iteration index in `architecture/ARCHITECT.md` (recording each repo's
|
|
318
|
+
integration branch), remove the worktrees (`architect integrate β¦ --teardown`,
|
|
319
|
+
or `architect worktree remove <iteration> <lane>`), and commit the space.
|
|
320
|
+
|
|
321
|
+
**Do not judge now** β the Verdict on the integration branch belongs to the
|
|
322
|
+
next architect session; merge to each repo's main only on a CONTINUE verdict
|
|
323
|
+
there.
|
|
324
|
+
|
|
325
|
+
## Maintenance
|
|
326
|
+
|
|
327
|
+
Re-read this skill against each new model generation and delete what the models
|
|
328
|
+
now do unprompted β over-prescription degrades current-model output. The rules
|
|
329
|
+
above are invariants; everything else is prunable.
|