space-architect 9.7.0 → 9.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f12c744b4e442b1ba96e09e2ede78bf7f048932d61bd891c667b65f62c78674a
4
- data.tar.gz: 87e62449c0d0539fdf688fc7faa43909cbc0dc034c5a0ee477b645f3ceac7a9e
3
+ metadata.gz: 2a29d0c5688ed96e02a49a159385f508a845714e1741577384f6a6369a1fbb7e
4
+ data.tar.gz: 2b46a7ffee6ee544c9f5e30b7926f4ee5d541d81032be53617cec19f5e519bfd
5
5
  SHA512:
6
- metadata.gz: c84129b96fa520324e1c15bf00f8f3de595bc642248ce3e72c9af0283b9802f63d2d3e7a15f1c450321bdf6c9840513ecce5cac477adf33cd0a8528bd63d585f
7
- data.tar.gz: f1fff1968103fd8108619aefbcfd69531c6f2c916c42c4748ea75247ef8bf208d850a3c6bc79b208f12eb1c35b0cb9fc906a10455a357e13cd8ef1c326dec656
6
+ metadata.gz: d418323e0ef3c26cfdf2ca7d06aac9fcec9da1d74c8ccbb0a339cc5f0e85eaf9838ab5bf380f7949f28d30a416e7d5b2a0c9043e1fe74d3659ffbfaf5d764641
7
+ data.tar.gz: 6fdbc0e2e89a34ff61f661202fb04e0f452a5f2b096db28b2dfd0a3adf41e6b9194a82b62253e2e1d4e9647886244d0ff0e38339d898726b93d8bcebc207800f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [9.8.0] - 2026-10-30
9
+
10
+ ### Removed
11
+
12
+ - **The coordinator role (I10)** — the architect session itself drives the
13
+ loop in driver mode (the pi extension's `/loop` and agent-side launch
14
+ install a driver playbook instead of spawning a detached coordinator).
15
+ The role leaves the routing known set and the `architect routing init`
16
+ template (byte-exact contract re-frozen through both parsers), and
17
+ install no longer ships its playbook — a stale user-level
18
+ `architect-coordinator.md` is removed on install.
19
+
20
+ ### Changed
21
+
22
+ - **Routing deprecation tolerance** — unknown `architect.yaml` role keys
23
+ (a retired key, or a typo) inform instead of rejecting: every command
24
+ proceeds, with one inform line naming the key and the known-role set.
25
+ Upgrading never breaks on an older config.
26
+ - **Shipped skills rewritten for driver mode** — architect,
27
+ architect-action, and architect-discuss describe the session-driven
28
+ loop: one-hop escalations, state re-derived from disk each wake,
29
+ compaction after each child launch.
30
+
8
31
  ## [9.7.0] - 2026-10-30
9
32
 
10
33
  ### Added
@@ -4,8 +4,10 @@ require "open3"
4
4
 
5
5
  module Space
6
6
  module Architect
7
- # Installs the role playbooks (architect-coordinator/spec/builder/judge/
8
- # narrator) to ~/.pi/agent/agents. The playbook text is rendered by the
7
+ # Installs the role playbooks (architect-spec/builder/judge/narrator) to
8
+ # ~/.pi/agent/agents and removes a stale architect-coordinator.md left by a
9
+ # pre-driver-era install (I10: the coordinator role retired — the architect
10
+ # session itself drives the loop). The playbook text is rendered by the
9
11
  # installed architect-loop extension itself — the extension owns that text
10
12
  # (it renders the per-space .pi/agents files from the same source), so this
11
13
  # installer carries no second copy to drift. Routing pins come from the
@@ -14,7 +16,7 @@ module Space
14
16
  # machine-wide fallback for sessions that cannot see a space's .pi dir;
15
17
  # a space's own .pi/agents files always win inside that space.
16
18
  module AgentInstaller
17
- ROLES = %w[coordinator spec builder judge narrator].freeze
19
+ ROLES = %w[spec builder judge narrator].freeze
18
20
 
19
21
  # Renders each role's agent file and prints it between ===FILE===<role>
20
22
  # markers. Dynamic import (node -e runs CJS; import() is legal there) so
@@ -29,7 +31,7 @@ module Space
29
31
  console.error("no architect.yaml found — run `architect routing init --global` to scaffold the machine-wide config first");
30
32
  process.exit(1);
31
33
  }
32
- for (const role of ["coordinator", "spec", "builder", "judge", "narrator"]) {
34
+ for (const role of ["spec", "builder", "judge", "narrator"]) {
33
35
  console.log("===FILE===" + role);
34
36
  console.log(renderAgentFile(role, config));
35
37
  }
@@ -53,11 +55,23 @@ module Space
53
55
  results = ROLES.map do |role|
54
56
  write_agent(dest.join("architect-#{role}.md"), rendered.fetch(role), force: force, dry_run: dry_run)
55
57
  end
58
+ results.concat(remove_stale_coordinator(dest, dry_run: dry_run))
56
59
  { dest_root: dest, agents: results, dry_run: dry_run }
57
60
  end
58
61
 
59
62
  private
60
63
 
64
+ # The retired coordinator playbook: remove, never update — the loop is
65
+ # driven by the architect session itself, so a leftover fifth playbook
66
+ # can only mislead. A dry run reports the would-be removal untouched.
67
+ def remove_stale_coordinator(dest, dry_run:)
68
+ stale = dest.join("architect-coordinator.md")
69
+ return [] unless stale.exist?
70
+
71
+ FileUtils.rm(stale) unless dry_run
72
+ [{ name: stale.basename.to_s, action: dry_run ? :would_remove : :removed, path: stale }]
73
+ end
74
+
61
75
  def default_dest(env)
62
76
  Pathname.new(env.fetch("PI_AGENTS_DIR", File.join(pi_agent_dir(env), "agents")))
63
77
  end
@@ -907,12 +907,12 @@ module Space::Architect
907
907
  def worktree_add(repo, iteration, lane, base: nil, base_explicit: !base.nil?, harness: nil, model: nil,
908
908
  variant: false, effort: nil, touch: nil, force: false, err: $stderr)
909
909
  model, suffix_level = Harness.parse_model_suffix(model)
910
- harness, model, inform = resolve_harness_model(harness, model)
910
+ harness, model, inform = resolve_harness_model(harness, model, err: err)
911
911
  err.puts inform if inform
912
912
  # The config layer sits above project.effort — architect.yaml is the
913
913
  # dedicated routing layer; unconfigured spaces fall through unchanged.
914
914
  resolved_level = effort || suffix_level ||
915
- Routing.for_space(space.path)&.dig("builder", "thinking") || project_defaults["effort"]
915
+ Routing.for_space(space.path, err: err)&.dig("builder", "thinking") || project_defaults["effort"]
916
916
  Harness.validate_thinking_level!(resolved_level)
917
917
  if effort.nil? && suffix_level
918
918
  err.puts "thinking: model suffix ':#{suffix_level}' parsed from lane model → level=#{suffix_level} (model stripped)"
@@ -1992,12 +1992,14 @@ module Space::Architect
1992
1992
  # Returns [harness, model, inform] — inform is the loud last-resort line (nil
1993
1993
  # otherwise) for the caller to print once to its err stream when the model
1994
1994
  # fell all the way through to Harness::DEFAULT_MODEL (no flag, no stored
1995
- # value, no architect.yaml builder role, no project.model).
1996
- def resolve_harness_model(harness, model, stored_harness: nil, stored_model: nil)
1995
+ # value, no architect.yaml builder role, no project.model). +err+ is additive
1996
+ # (I10): given a stream, a config carrying an unknown role key prints the one
1997
+ # per-process deprecation inform on it; nil (the default) resolves silently.
1998
+ def resolve_harness_model(harness, model, stored_harness: nil, stored_model: nil, err: nil)
1997
1999
  defaults = project_defaults
1998
2000
  resolved_harness = (harness || stored_harness || defaults["harness"] || "pi").to_s
1999
2001
  stored_model = nil unless stored_harness.nil? || stored_harness.to_s == resolved_harness
2000
- config_model = Routing.for_space(space.path)&.dig("builder", "model")
2002
+ config_model = Routing.for_space(space.path, err: err)&.dig("builder", "model")
2001
2003
  resolved_model = model || stored_model || config_model || defaults["model"] || Harness::DEFAULT_MODEL
2002
2004
  inform = default_model_inform if model.nil? && stored_model.nil? && config_model.nil? && defaults["model"].nil?
2003
2005
  [resolved_harness, resolved_model, inform]
@@ -2037,7 +2039,7 @@ module Space::Architect
2037
2039
  def resolve_dispatch_harness(lane_entry, model:, harness:, effort:, force:, err:)
2038
2040
  model, suffix_level = Harness.parse_model_suffix(model)
2039
2041
  resolved_harness, resolved_model, inform = resolve_harness_model(harness, model,
2040
- stored_harness: lane_entry["harness"], stored_model: lane_entry["model"])
2042
+ stored_harness: lane_entry["harness"], stored_model: lane_entry["model"], err: err)
2041
2043
  err.puts inform if inform
2042
2044
 
2043
2045
  resolved_effort =
@@ -2050,7 +2052,7 @@ module Space::Architect
2050
2052
  else
2051
2053
  # Dispatch's tail is the lane's stored effort (project.effort was never
2052
2054
  # in the dispatch chain); architect.yaml builder thinking slots in above it.
2053
- lane_entry["effort"] || Routing.for_space(space.path)&.dig("builder", "thinking")
2055
+ lane_entry["effort"] || Routing.for_space(space.path, err: err)&.dig("builder", "thinking")
2054
2056
  end
2055
2057
 
2056
2058
  [resolved_harness, resolved_model, resolved_effort]
@@ -172,7 +172,10 @@ module Space::Architect
172
172
  # constant as the last resort it is, not ordinary behavior.
173
173
  # Fully qualified: the Architect::Routing subcommand namespace
174
174
  # (I09) lexically shadows Space::Architect::Routing inside this module.
175
- config_model = Space::Architect::Routing.for_space(sp.path)&.dig("builder", "model")
175
+ # err: $stderr makes status one of the three deprecation-inform surfaces
176
+ # (I10) — a config still carrying an unknown role key informs once per
177
+ # process, here or on whichever surface resolves routing first.
178
+ config_model = Space::Architect::Routing.for_space(sp.path, err: $stderr)&.dig("builder", "model")
176
179
 
177
180
  terminal.say "Project status: #{block['status'] || '(none)'}"
178
181
  terminal.say "Current iteration: #{block['current_iteration'] || '(none)'}"
@@ -1162,7 +1165,7 @@ module Space::Architect
1162
1165
  # global-routing pins — the machine-wide fallback for sessions that
1163
1166
  # cannot see a space's .pi dir. Needs install-extensions to have run.
1164
1167
  class InstallAgents < BaseCommand
1165
- desc "Install the role playbooks (architect-coordinator/spec/builder/judge/narrator) to ~/.pi/agent/agents, rendered from the installed architect-loop extension"
1168
+ desc "Install the role playbooks (architect-spec/builder/judge/narrator) to ~/.pi/agent/agents, rendered from the installed architect-loop extension, and remove a stale architect-coordinator.md"
1166
1169
  phase 53, "Project"
1167
1170
  option :force, type: :boolean, default: false, desc: "Overwrite existing playbooks that differ"
1168
1171
  option :dry_run, type: :boolean, default: false, desc: "Print what would happen without writing files"
@@ -17,48 +17,47 @@ module Space::Architect
17
17
  # XDG_CONFIG_HOME); space-local is architect.yaml beside the loaded
18
18
  # space.yaml (the CLI already resolved the space — no walk-up).
19
19
  module Routing
20
- ROLES = %w[architect spec builder judge coordinator narrator].freeze
21
- # The five agent roles that must be routed after the merge; `architect` is optional.
22
- REQUIRED_ROLES = %w[spec builder judge coordinator narrator].freeze
20
+ ROLES = %w[architect spec builder judge narrator].freeze
21
+ # The four agent roles that must be routed after the merge; `architect` is optional.
22
+ REQUIRED_ROLES = %w[spec builder judge narrator].freeze
23
23
  ENTRY_KEYS = %w[model thinking].freeze
24
24
 
25
25
  # I09: the `architect routing init` scaffold — the exact bytes the command
26
26
  # writes, living beside the parsers that must accept it. Example models are
27
27
  # loud REPLACE_MEs; the header comment carries both locations and the merge
28
- # order so the file is self-documenting. Byte-exact contract: the extension
29
- # lane's tests carry an inline copy, and the command round-trips the write
30
- # through parse below — a template the machinery rejects is a bug.
28
+ # order so the file is self-documenting. Byte-exact contract (1021 B): the
29
+ # extension lane's tests carry an inline copy, and the command round-trips
30
+ # the write through parse below — a template the machinery rejects is a bug.
31
+ # I10: the detached driver role retired (the architect session drives the
32
+ # loop itself); the scaffold routes the four required roles plus optional architect.
31
33
  TEMPLATE = <<~'YAML'.freeze
32
- # architect.yaml — space-architect loop role routing, scaffolded by `architect routing init`.
33
- # Locations (merged in this order; the space-local entry wins per role):
34
- # global: ~/.config/space-architect/architect.yaml (machine-wide)
35
- # local: architect.yaml beside this space's space.yaml (this file)
36
- # A local entry overrides the global entry for THAT ROLE only; after the
37
- # merge every agent role must be routed: spec, builder, judge, coordinator,
38
- # narrator (architect is optional).
39
- # Replace every example/REPLACE_ME below with a real provider/model-id
40
- # (e.g. zai/glm-5.3). thinking is optional: off|minimal|low|medium|high|xhigh|max.
41
- # Once saved, the next /loop or architect command picks the file up.
42
- version: 1
43
- roles:
44
- architect:
45
- model: example/REPLACE_ME
46
- thinking: high
47
- spec:
48
- model: example/REPLACE_ME
49
- thinking: high
50
- builder:
51
- model: example/REPLACE_ME
52
- thinking: high
53
- judge:
54
- model: example/REPLACE_ME
55
- thinking: high
56
- coordinator:
57
- model: example/REPLACE_ME
58
- thinking: high
59
- narrator:
60
- model: example/REPLACE_ME
61
- thinking: low
34
+ # architect.yaml — space-architect loop role routing, scaffolded by `architect routing init`.
35
+ # Locations (merged in this order; the space-local entry wins per role):
36
+ # global: ~/.config/space-architect/architect.yaml (machine-wide)
37
+ # local: architect.yaml beside this space's space.yaml (this file)
38
+ # A local entry overrides the global entry for THAT ROLE only; after the
39
+ # merge every agent role must be routed: spec, builder, judge, narrator
40
+ # (architect is optional).
41
+ # Replace every example/REPLACE_ME below with a real provider/model-id
42
+ # (e.g. zai/glm-5.3). thinking is optional: off|minimal|low|medium|high|xhigh|max.
43
+ # Once saved, the next /loop or architect command picks the file up.
44
+ version: 1
45
+ roles:
46
+ architect:
47
+ model: example/REPLACE_ME
48
+ thinking: high
49
+ spec:
50
+ model: example/REPLACE_ME
51
+ thinking: high
52
+ builder:
53
+ model: example/REPLACE_ME
54
+ thinking: high
55
+ judge:
56
+ model: example/REPLACE_ME
57
+ thinking: high
58
+ narrator:
59
+ model: example/REPLACE_ME
60
+ thinking: low
62
61
  YAML
63
62
 
64
63
  # The one canonical cure line appended to default_model_inform's last-resort
@@ -75,14 +74,34 @@ module Space::Architect
75
74
  Pathname.new(space_dir).join("architect.yaml")
76
75
  end
77
76
 
78
- # Parse one file's text into { role => {"model" => id, "thinking" => level|nil} }.
79
- # +path+ is used only in error messages. Frozen v1 rules: integer version 1,
80
- # the six known roles, entries ⊆ {model, thinking} with a non-empty model,
81
- # duplicate mapping keys rejected (psych's plain load silently keeps the
82
- # last — the npm yaml the extension parses with throws), aliases accepted,
83
- # a trailing :level suffix on model parsed via Harness.parse_model_suffix
84
- # and beating the thinking key.
77
+ # Parse one file's text into { role => {"model" => id, "thinking" => level|nil} }
78
+ # for the KNOWN roles. +path+ is used only in error messages. Frozen v1 rules:
79
+ # integer version 1, the five known roles, entries ⊆ {model, thinking} with a
80
+ # non-empty model, duplicate mapping keys rejected (psych's plain load silently
81
+ # keeps the last — the npm yaml the extension parses with throws), aliases
82
+ # accepted, a trailing :level suffix on model parsed via Harness.parse_model_suffix
83
+ # and beating the thinking key. Unknown role keys (I10 deprecation tolerance: a
84
+ # config still carrying a retired role key, or a typo) are skipped for
85
+ # routing and collected — see split_known_and_unknown.
85
86
  def self.parse(text, path)
87
+ split_known_and_unknown(text, path).first
88
+ end
89
+
90
+ # The pure deprecation seam (I10): the unknown role keys under `roles:` in one
91
+ # file's text, in document order. Same parse rules as
92
+ # parse (known-role validation included); only unknown entries are skipped
93
+ # unvalidated, so the inform never fires for a config the parser rejects.
94
+ def self.unknown_role_keys(text, path)
95
+ split_known_and_unknown(text, path).last
96
+ end
97
+
98
+ # One parse, two results: [known-role hash, unknown role keys]. An unknown
99
+ # STRING key is skipped whole for routing — its entry content is never
100
+ # validated (an ignored key is ignored whole; garbage under
101
+ # roles.<retired-role>.thinking must not raise) — and collected for the one
102
+ # deprecation inform. A non-string key is corruption, not a retired role
103
+ # name, and still fails loud under the known-role error.
104
+ def self.split_known_and_unknown(text, path)
86
105
  assert_unique_keys!(text, path)
87
106
  doc = YAML.safe_load(text, aliases: true)
88
107
  unless doc.is_a?(Hash)
@@ -98,8 +117,14 @@ module Space::Architect
98
117
  end
99
118
 
100
119
  parsed = {}
120
+ unknown = []
101
121
  roles.each do |role, entry|
102
- unless ROLES.include?(role)
122
+ if role.is_a?(String)
123
+ unless ROLES.include?(role)
124
+ unknown << role # an ignored key is ignored whole: skip for routing, no entry validation
125
+ next
126
+ end
127
+ else
103
128
  raise Space::Core::Error,
104
129
  "architect.yaml (#{path}): roles.#{role} — expected one of #{ROLES.join('|')}, got #{role.inspect}"
105
130
  end
@@ -136,24 +161,48 @@ module Space::Architect
136
161
  end
137
162
  parsed[role] = { "model" => model, "thinking" => suffix_level || thinking }
138
163
  end
139
- parsed
164
+ [parsed, unknown]
140
165
  rescue Psych::Exception => e
141
166
  raise Space::Core::Error, "architect.yaml (#{path}): malformed YAML — #{e.message}"
142
167
  end
143
168
 
169
+ # The one deprecation inform for unknown role keys (I10): names every
170
+ # roles.<key> found, the known-role set, and that the entries are ignored.
171
+ # Fired at most once per PROCESS via the class-level flag — worktree_add and
172
+ # dispatch each resolve routing twice per invocation, so per-call emission
173
+ # would double-print. A config with no unknown key never composes the
174
+ # message, so the memo never fires. Tests reset it with
175
+ # remove_instance_variable(:@deprecation_informed).
176
+ def self.inform_deprecated_roles!(err, keys, found)
177
+ return if keys.empty? || @deprecation_informed
178
+
179
+ @deprecation_informed = true
180
+ err.puts "architect.yaml (#{found.join(' + ')}): ignoring unknown role key(s) " \
181
+ "#{keys.map { |k| "roles.#{k}" }.join(', ')} — known roles: #{ROLES.join(', ')} (each entry is ignored)"
182
+ end
183
+
144
184
  # The merged routing for a space: [global, space-local] merged per role key
145
185
  # (a space file may override only builder and inherit the rest). nil when
146
186
  # NEITHER file exists — no config is a normal state: no error, no side
147
187
  # effects. Any found file that is malformed/invalid fails loud at parse;
148
- # after the merge all four agent roles must be routed.
149
- def self.for_space(space_dir)
188
+ # after the merge all four agent roles must be routed. +err+ is additive
189
+ # (I10): when given, a merged config carrying unknown role keys prints the
190
+ # one per-process deprecation inform on it; existing callers (no err)
191
+ # resolve silently as before.
192
+ def self.for_space(space_dir, err: nil)
150
193
  global = global_path
151
194
  local = space_path(space_dir)
152
195
  found = [global, local].select { |p| File.file?(p.to_s) }
153
196
  return nil if found.empty?
154
197
 
155
198
  merged = {}
156
- found.each { |p| merged.merge!(parse(File.read(p.to_s), p)) }
199
+ unknown = []
200
+ found.each do |p|
201
+ known, keys = split_known_and_unknown(File.read(p.to_s), p)
202
+ merged.merge!(known)
203
+ unknown |= keys
204
+ end
205
+ inform_deprecated_roles!(err, unknown, found) if err
157
206
  missing = REQUIRED_ROLES - merged.keys
158
207
  if missing.any?
159
208
  raise Space::Core::Error,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Space
4
4
  module Architect
5
- VERSION = "9.7.0"
5
+ VERSION = "9.8.0"
6
6
  end
7
7
  end
@@ -72,13 +72,14 @@ the space durable across sessions:
72
72
  - **architect** — the judgment seat (this session in either mode: inline
73
73
  here, judgment in `/architect-discuss`): plans, arbitrates, rules on
74
74
  escalations, judges, lands. In inline mode it also does the work directly.
75
- - **coordinator** — a detached, cheap-model session that drives one plan:
76
- spawns and tends spec → builders → judge per iteration; relays escalations
77
- verbatim; never does lane work.
75
+ - **driver** — the architect session itself, in driver mode (the /loop
76
+ tool installs the driver playbook as operating instructions): spawns and
77
+ tends spec → builders → judge per iteration; rules escalations in-seat;
78
+ never does lane work.
78
79
  - **spec** — writes one iteration's Grounds/Specification/Acceptance
79
80
  Criteria; rehearses; freezes; writes the `notes/spec-digest-*` handoff.
80
81
  - **builder** — implementation in one lane's worktree; reports raw evidence
81
- only; never commits. Spawned by the coordinator as a subagent, or run via
82
+ only; never commits. Spawned by the driver as a subagent, or run via
82
83
  the `architect dispatch` CLI (which streams to
83
84
  `build/<id>-<lane>/run.jsonl`).
84
85
  - **judge** — a fresh session that runs the cold gates and rules per-AC
@@ -146,13 +147,14 @@ Read-only orientation commands: `architect status`, `architect looplog
146
147
 
147
148
  - **`/architect-discuss`** — engineering exploration and arbitration,
148
149
  judging a finished run, landing PRs, iteration surgery.
149
- - **`/architect-action`** — write the plan doc and launch the coordinator.
150
+ - **`/architect-action`** — write the plan doc and drive the loop yourself
151
+ as the driver.
150
152
  - **`/architect-status`** — "what are we working on in here? is it done?"
151
153
  - **`/architect-research`** — discovery-scale research.
152
154
 
153
155
  ## Maintenance
154
156
 
155
157
  This skill carries the shared vocabulary inline. When loop mechanics change
156
- (the coordinator flow, dispatch, routing), re-read this file against the
158
+ (the driver flow, dispatch, routing), re-read this file against the
157
159
  `architect-discuss` skill and the gem README and update the glossary —
158
160
  drift between the two is silent confusion for every future session.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: architect-action
3
3
  description: >
4
- Take over when the discussion is done: write the plan doc and launch the
5
- coordinator yourself, end to end. Use when the user's intention is to
4
+ Take over when the discussion is done: write the plan doc and drive it
5
+ as the driver yourself, end to end. Use when the user's intention is to
6
6
  START a loop — planning iterations, writing notes/plan-*.md, pinning role
7
- routing, spawning the coordinator, and handing off. For engineering
7
+ routing, and driving the loop. For engineering
8
8
  discussion or judgment work, /architect-discuss. For working inline
9
9
  outside the loop, /architect.
10
10
  ---
@@ -13,8 +13,8 @@ description: >
13
13
 
14
14
  You are the ARCHITECT taking action: the discussion is over, the human has
15
15
  disengaged, and the deliverable of this skill is exactly one thing — a
16
- committed plan doc and a launched coordinator. The loop's roles
17
- (spec → builders → judge, driven by the coordinator) do everything after
16
+ committed plan doc and a launched loop that you drive yourself. The loop's
17
+ roles (spec → builders → judge, driven by you as the driver) do everything after
18
18
  that.
19
19
 
20
20
  Read `/architect-discuss` first for the loop's roles, routing, and hard
@@ -38,7 +38,7 @@ human again until the loop needs them.
38
38
  config — the plan's acceptance direction must name it.
39
39
  2. **Scale the work.** Trivial fixes don't need the loop — say so and do it
40
40
  inline (`/architect`). One PR-sized iteration = one ENTRY. Multi-iteration
41
- plans get ordered ENTRYs (the coordinator works them in order, skipping
41
+ plans get ordered ENTRYs (the driver works them in order, skipping
42
42
  finished ones). If the open question is too uncertain to build, make the
43
43
  ENTRY a spike (investigate-only; deliverable is a recommendation — see
44
44
  `/architect-discuss`).
@@ -57,7 +57,7 @@ human again until the loop needs them.
57
57
 
58
58
  **Objective.** What to build and why — the reason, not just the ask. Cite
59
59
  BRIEF §N. If this ENTRY pins model/thinking for a role, say it here (the
60
- coordinator passes it in that spawn's model field).
60
+ driver passes it in that spawn's model field).
61
61
 
62
62
  **Grounds to read.** Exact paths: research/bug reports, source files being
63
63
  ported, seam files, prior verdicts. The spec reads these before writing
@@ -77,7 +77,7 @@ human again until the loop needs them.
77
77
  constraints or grounds.
78
78
  4. **Pin routing if needed.** Models come from `architect.yaml` (space root
79
79
  overrides `~/.config/space-architect/architect.yaml` per role). A
80
- one-plan pin goes in the plan header/ENTRY (the coordinator passes it in
80
+ one-plan pin goes in the plan header/ENTRY (the driver passes it in
81
81
  that spawn's model field); a standing change edits `architect.yaml`. The
82
82
  extension writes the per-space `.pi/agents/` role files from it at
83
83
  session start, and every spawn still carries its pin explicitly — nothing
@@ -88,26 +88,20 @@ human again until the loop needs them.
88
88
  6. **Launch it with the `architect_loop` tool.** You cannot run `/loop` — it is
89
89
  a user command in the interactive session, and the human has disengaged.
90
90
  The launch is yours, and the tool is your equivalent of `/loop`: call
91
- `architect_loop` with `from` = the plan file path. It resolves and
92
- validates routing, regenerates the role agent files, spawns
93
- `architect-coordinator` detached (its pin, a 12h ceiling), waits for the
94
- run id, and then schedules a compaction of this session — after which you
95
- are a small on-call escalation seat that still knows where everything
96
- lives. Pass `compact: false` only if the human asks to keep full context.
97
- Fallback when the tool is missing (older extension): spawn the coordinator
98
- directly with the subagent tool — agent `architect-coordinator`, async,
99
- cwd = the space root, the coordinator's pin from `architect.yaml` in the
100
- model field, `timeoutMs` 43200000, task = the plan text verbatim — and
101
- tell the human a `/compact` whenever suits (no timing point, unlike
102
- `/loop`).
103
- 7. **Report the handoff, then stay on-call.** Tell the human: the plan path,
104
- the coordinator run id, and how to watch — `architect looplog` (live),
105
- `--list` (inventory), `architect status`, `architect cost`. Then: this
106
- session is the escalation seat — the human may keep it open, close it, or
107
- compact it further whenever they like; a coordinator with no listener parks
108
- questions in `notes/escalation-<ts>.md` and stops via the exception path
109
- rather than improvising. When you do get an escalation: rule on it; the
110
- coordinator steers your ruling to the child unchanged.
91
+ `architect_loop` with `from` = the plan file path. The tool installs the
92
+ driver playbook into THIS session as operating instructions and schedules
93
+ the entry compaction — after which this session IS the loop driver: it
94
+ spawns and tends spec → builders → judge itself, one hop from the human.
95
+ Pass `compact: false` only if the human asks to keep full context. An
96
+ older extension without the driver-mode `architect_loop` is updated via
97
+ `architect install-extensions` — never spawned around.
98
+ 7. **Report the launch, then drive.** Tell the human: the plan path and how
99
+ to watch — `architect looplog` (live), `--list` (inventory), `architect
100
+ status`, `architect cost`. Then this session is the on-call seat itself:
101
+ escalations from blocked children land here (one hop — you rule in-seat,
102
+ and the ruling goes to the child unchanged). A driver with no live human
103
+ parks the question in `notes/escalation-<ts>.md` and stops via the
104
+ exception path rather than improvising.
111
105
 
112
106
  ## Failure modes to check before launching
113
107
 
@@ -4,7 +4,7 @@ description: >
4
4
  The judgment seat: engineering discussion and exploration of a problem,
5
5
  arbitration of disagreements and escalations, judging finished loop runs,
6
6
  landing PRs, and iteration surgery (hand-running iteration steps when the
7
- human asks or the coordinator is down). Your output is judgment and
7
+ human asks). Your output is judgment and
8
8
  dispatch — never implementation code. Use /architect for working inline
9
9
  outside the loop, /architect-action to START a loop, /architect-status for
10
10
  a state summary.
@@ -19,8 +19,7 @@ space. Two postures, one set of hard rules:
19
19
  disagreements, rulings on escalations, taste. You stay in the loop's
20
20
  context without driving it.
21
21
  - **Hands-on loop work.** Judging finished runs, landing PRs, and running
22
- iteration steps by hand when the human asks for a hand-run or the
23
- coordinator is down.
22
+ iteration steps by hand when the human asks for a hand-run.
24
23
 
25
24
  Your output is judgment and dispatch — never implementation code. Anything
26
25
  that must change goes in a plan entry or an iteration Specification. (When
@@ -53,7 +52,7 @@ project — loads them.
53
52
  | Role | Session | Owns |
54
53
  |---|---|---|
55
54
  | **architect** | your interactive session | plans, arbitration, escalation rulings, landing, taste |
56
- | **coordinator** | detached, cheap model | drives the plan: spawns/tends spec → provision → builders → judge per iteration; relays escalations verbatim; never does lane work |
55
+ | **driver** | this session in driver mode (installed by /loop) | drives the plan: spawns/tends spec → provision → builders → judge per iteration; rules escalations in-seat; never does lane work |
57
56
  | **spec** | subagent | one iteration's Grounds/Specification/Acceptance Criteria; rehearses; freezes; writes the `notes/spec-digest-*` handoff |
58
57
  | **builder** | subagent per lane | implementation in the lane worktree; raw-results report; never commits |
59
58
  | **judge** | subagent, fresh session | mechanical post-flight, cold gates, per-AC verdicts, PHASE 0 rulings, integration |
@@ -71,16 +70,17 @@ architect cost # spend + latency by role/iteration
71
70
 
72
71
  ## Escalations and exceptions land on you
73
72
 
74
- A blocked child asks its supervisor; the coordinator relays the question
75
- **verbatim** with a one-line triage (never re-authored — a re-authored
76
- escalation suppresses your independent read). Rule and the coordinator
77
- steers it to the child unchanged. If the coordinator has no live listener,
78
- it writes `notes/escalation-<ts>.md` and stops via the exception path — a
79
- loop that needs you and can't reach you does not improvise; it parks the
80
- facts and terminates. Exception reports (`notes/exception-<ts>.md`) and
81
- completion reports (`notes/report-<ts>.md`) are how a dead or finished loop
82
- explains itself; narrations (`notes/narrative-*.md`) are best-effort updates
83
- in between. Read them before resuming anything.
73
+ In driver mode a blocked child's escalation lands directly in this session —
74
+ one hop: you ARE the child's supervisor. Rule in-seat and steer the ruling
75
+ to the child **unchanged** (never re-authored — a re-authored
76
+ escalation suppresses your independent read). A driver with no live human
77
+ and no session left writes `notes/escalation-<ts>.md` and stops via the
78
+ exception path — a loop that needs you and can't reach you does not
79
+ improvise; it parks the facts and terminates. Exception reports
80
+ (`notes/exception-<ts>.md`) and completion reports (`notes/report-<ts>.md`)
81
+ are how a dead or finished loop explains itself; narrations
82
+ (`notes/narrative-*.md`) are best-effort updates in between. Read them
83
+ before resuming anything.
84
84
 
85
85
  ## Ground rules
86
86
 
@@ -150,7 +150,7 @@ push, no `gh` call by you; the human runs the block when ready.
150
150
  ## Iteration surgery (interactive path)
151
151
 
152
152
  Run the iteration steps in your own session when the human asks for a
153
- hand-run, wants to babysit, or the coordinator is down. Ground first, then
153
+ hand-run or wants to babysit. Ground first, then
154
154
  per iteration: arbitrate open disagreements from the last Builder Report;
155
155
  **spec the next iteration** (`architect new <name>`, then Specification via
156
156
  `architect section`); **write the Acceptance Criteria** (prose ACs + fenced
@@ -1,10 +1,10 @@
1
1
  # Builder dispatch reference
2
2
 
3
- ## The coordinator path (default) — how builders actually launch
3
+ ## The driver path (default) — how builders actually launch
4
4
 
5
- When a plan runs under the coordinator (see the /architect-action skill —
5
+ When a plan runs under the driver (see the /architect-action skill —
6
6
  launching is that skill's job), the
7
- coordinator itself spawns each role as a **pi subagent** — native async
7
+ driver itself spawns each role as a **pi subagent** — native async
8
8
  completion wakes, not `architect dispatch` headless runs. Per iteration:
9
9
 
10
10
  1. **spec** — agent `architect-spec`, cwd = the space root, task = the plan
@@ -12,11 +12,11 @@ completion wakes, not `architect dispatch` headless runs. Per iteration:
12
12
  `architect section`, rehearses, freezes, assembles each lane's prompt in a
13
13
  scratch file, records the scratch paths in its report, and writes the
14
14
  `notes/spec-digest-*` handoff. `timeoutMs` 3600000 (1h).
15
- 2. **provision** — the coordinator runs `architect provision <iteration>` via
15
+ 2. **provision** — the driver runs `architect provision <iteration>` via
16
16
  bash; the CLI materializes every declared lane's worktree + lane branch.
17
- 3. **prompt copy** — the coordinator copies each spec-assembled scratch prompt
17
+ 3. **prompt copy** — the driver copies each spec-assembled scratch prompt
18
18
  to `build/<id>-<lane>/prompt.md` (canonical path, CLI-owned in the
19
- interactive path; the coordinator's `cp` is the sanctioned equivalent).
19
+ interactive path; the driver's `cp` is the sanctioned equivalent).
20
20
  4. **builder per lane** — agent `architect-builder`, cwd = that lane's
21
21
  worktree, task = read `build/<id>-<lane>/prompt.md` before anything else.
22
22
  `timeoutMs` 7200000 (2h). The lane-prompt template below is the contract
@@ -39,13 +39,13 @@ start, crashed before work) gets one relaunch. Anything unrootable is an
39
39
  exception: `notes/exception-<ts>.md`, narrate, terminate — no improvised
40
40
  repairs.
41
41
 
42
- The coordinator never edits files except its own `notes/` reports and never
42
+ The driver never edits files except its own `notes/` reports and never
43
43
  writes into `architecture/`.
44
44
 
45
45
  `architect dispatch` (everything below) remains the **interactive-path**
46
46
  mechanism — and the reference implementation of the lane contract the
47
- coordinator's spawns inherit (same prompt template, same builder guard
48
- discipline — interactive dispatch injects the guard via `-e`; a coordinator
47
+ driver's spawns inherit (same prompt template, same builder guard
48
+ discipline — interactive dispatch injects the guard via `-e`; a driver
49
49
  spawn relies on the playbook's no-git-writes rule plus the judge's post-flight
50
50
  `git log` check, which stays the authoritative proof in both paths).
51
51
 
@@ -82,7 +82,7 @@ If a lane committed, treat the worktree as tampered: reset and re-dispatch.
82
82
  builder model resolves with a one-message probe
83
83
  (`echo ok | pi -p --model <builder-model>`), confirm the role playbooks are
84
84
  installed (`architect install-agents` — once per machine, idempotent), and
85
- for a coordinator run confirm the routing config resolves — an
85
+ for a driver run confirm the routing config resolves — an
86
86
  `architect.yaml` role pinning a model the registry doesn't know throws loudly
87
87
  at /loop pre-flight, before any spawn (/loop pre-flight does this for you).
88
88
  No API-key/env ceremony — provider
@@ -521,14 +521,6 @@ Paste the lane-prompt into an interactive `pi` session (no `-p`). pi's agent
521
521
  loop runs plan→act→test against the block's stopping condition while you watch
522
522
  and steer. Use when the human wants to babysit a run.
523
523
 
524
- A coordinator can be launched the same way when /loop is unavailable: spawn
525
- the agent `architect-coordinator` (role agents resolve from
526
- `~/.pi/agent/agents/`, user-level, installed by `architect install-agents` —
527
- no project trust involved) with cwd = the space root, the plan text verbatim
528
- as the task, the coordinator's model pin from `architect.yaml` in the spawn's
529
- model field, and `timeoutMs` 43200000 (12h) — the exact contract
530
- `coordinatorSpawnScript` in architect-loop.ts assembles.
531
-
532
524
  ## Lane-prompt template
533
525
 
534
526
  ```
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: 9.7.0
4
+ version: 9.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Jacobs