space-architect 9.8.0 → 9.9.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: 2a29d0c5688ed96e02a49a159385f508a845714e1741577384f6a6369a1fbb7e
4
- data.tar.gz: 2b46a7ffee6ee544c9f5e30b7926f4ee5d541d81032be53617cec19f5e519bfd
3
+ metadata.gz: bf5117e5de28d8f40811d45561c37ccd7852a1dc27758af06f08cc565aa90b65
4
+ data.tar.gz: 9c5fe91d46305b83d8f0fe3f17a689238ad6e81d620f95009b96c9f74da38b14
5
5
  SHA512:
6
- metadata.gz: d418323e0ef3c26cfdf2ca7d06aac9fcec9da1d74c8ccbb0a339cc5f0e85eaf9838ab5bf380f7949f28d30a416e7d5b2a0c9043e1fe74d3659ffbfaf5d764641
7
- data.tar.gz: 6fdbc0e2e89a34ff61f661202fb04e0f452a5f2b096db28b2dfd0a3adf41e6b9194a82b62253e2e1d4e9647886244d0ff0e38339d898726b93d8bcebc207800f
6
+ metadata.gz: 4698ad55f2c91d9a71fd996ad3523c6155aefad548c2ac9dba8a1f51bc115f2cfdad0223cc0353aefda83eed5c874625b02a58fc2b065feb124adaf0202c0674
7
+ data.tar.gz: d031b8fc81071a4c8a68892d07af6f8b4b0bf0c458a605c437fb513dd982fd7f79afb8cae8ee82bdd6281e5bd05cbdad8bb2c22edaa07f753e7a6a73e1fd9b14
data/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ 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.9.0] - 2026-10-30
9
+
10
+ ### Added
11
+
12
+ - **`architect ping` (I11)** — phase pings for driver-mode loops: `architect
13
+ ping ITERATION PHASE` emits an iteration phase transition (spec-launched,
14
+ builders-settled, judge-running) to the configured space server via the I16
15
+ emission surface. Vocabulary-first validation (unknown phase = 422-style
16
+ error naming the set), `--note` rides along, `--space` overrides the cwd
17
+ walk, and an unconfigured emission stack is inert — one inform line, exit 0.
18
+ A ping never gates the loop: every failure mode is one line.
19
+
20
+ ### Changed
21
+
22
+ - **Narration cadence (I11)** — shipped skills and docs carry the three-kind
23
+ cadence: narrations are exactly verdict-landed, exception, and
24
+ plan-complete; builders-settled and judge-settled stop being narration
25
+ kinds (the driver pings those transitions instead). Back-to-back rule:
26
+ when two narration-worthy events are back-to-back, one narration lands —
27
+ the more-final kind absorbs the earlier. dispatch.md's narrator contract
28
+ now asks for composed prose (situates the project, the iteration's place,
29
+ what happened and what it means), never transcribed STATUS lines, run ids,
30
+ or gate vectors.
31
+
8
32
  ## [9.8.0] - 2026-10-30
9
33
 
10
34
  ### Removed
@@ -904,6 +904,59 @@ module Space::Architect
904
904
  end
905
905
  end
906
906
 
907
+ # I11: the phase ping — one small "where is the loop now" POST per
908
+ # playbook step; the between-command currency that lets narrations stay
909
+ # rare prose. An automated playbook step, deliberately quieter than
910
+ # emit: unconfigured is one inert stdout line; a POST failure NEVER
911
+ # fails the command (exit 0 — the Emitter's single stderr line is the
912
+ # whole report); the token never appears in any output. Writes nothing
913
+ # to disk — a ping is a fact about the loop, not a loop step, so the
914
+ # entry lookup is read-only (no ArchitectProject mutation).
915
+ class Ping < BaseCommand
916
+ desc "POST one phase ping for an iteration (spec-launched/builders-settled/judge-running; best-effort — never fails the loop step)"
917
+ phase 53, "Project"
918
+ argument :iteration, required: true, desc: "Iteration name"
919
+ argument :phase, required: true, desc: "Phase ping kind: spec-launched, builders-settled, or judge-running"
920
+ option :space, default: nil, desc: "Space identifier (default: $PWD)"
921
+ option :note, default: nil, desc: "Optional note carried in the ping payload (nil/empty omitted)"
922
+
923
+ def call(iteration:, phase:, space: nil, note: nil, **opts)
924
+ setup_terminal(**opts.slice(:color, :colors))
925
+ handle_errors do
926
+ # Vocabulary is validated BEFORE space resolution — a typo'd phase
927
+ # must name the known set, not space-resolution noise.
928
+ unless IterationEmission::PHASE_KINDS.include?(phase)
929
+ raise Space::Core::Error,
930
+ "unknown phase '#{phase}' — one of: #{IterationEmission::PHASE_KINDS.join(', ')}"
931
+ end
932
+
933
+ render(store.find(space)) do |sp|
934
+ # Read-only slice of the space.yaml iteration record — the exact
935
+ # entry lookup and error text of ArchitectProject#slice_entry,
936
+ # mirrored here rather than instantiated: the ping path never
937
+ # mutates anything on disk (a ping is a fact, not a loop step).
938
+ entry = ((sp.data["project"] || {})["iterations"] || []).find { |s| s["name"] == iteration }
939
+ unless entry
940
+ raise Space::Core::Error,
941
+ "Iteration '#{iteration}' not recorded in space.yaml — run `architect new #{iteration}` first"
942
+ end
943
+
944
+ emitter = IterationEmission.emitter_for(sp)
945
+ if emitter
946
+ res = emitter.post("phase", IterationEmission.build_phase(
947
+ ordinal: entry["ordinal"], name: entry["name"], kind: phase, note: note))
948
+ # res nil: the Emitter already printed its one stderr line —
949
+ # a failed ping stays off stdout and never fails the step.
950
+ terminal.say "pinged #{iteration} #{phase} → #{res[:status]}" if res
951
+ else
952
+ terminal.say "emission off (unconfigured)"
953
+ end
954
+ CLI.record_outcome(Outcome.new(exit_code: 0))
955
+ end
956
+ end
957
+ end
958
+ end
959
+
907
960
  # Pretty-print a pi agent run.jsonl as it streams — the ported runlog
908
961
  # printer. No space resolution: the stream is the whole input.
909
962
  class Runlog < BaseCommand
@@ -1498,6 +1551,7 @@ Space::Architect::CLI::Registry.register "install-agents", Space::Architect::C
1498
1551
  Space::Architect::CLI::Registry.register "runlog", Space::Architect::CLI::Architect::Runlog
1499
1552
  Space::Architect::CLI::Registry.register "looplog", Space::Architect::CLI::Architect::Looplog
1500
1553
  Space::Architect::CLI::Registry.register "emit", Space::Architect::CLI::Architect::Emit
1554
+ Space::Architect::CLI::Registry.register "ping", Space::Architect::CLI::Architect::Ping
1501
1555
  Space::Architect::CLI::Registry.register "bug-report", Space::Architect::CLI::Architect::BugReport
1502
1556
  Space::Architect::CLI::Registry.register "cost", Space::Architect::CLI::Architect::Cost
1503
1557
  Space::Architect::CLI::Registry.register "brief" do |b|
@@ -14,6 +14,10 @@ module Space::Architect
14
14
  # ```rulings block parser. ArchitectProject's five hook call sites guard
15
15
  # through `emitter&.post` inside a rescue-all belt; nothing here may ever
16
16
  # raise into a loop command or change its exit code, stdout, or commits.
17
+ # I11 extends the same layer additively: the phase mouth (a fifth server
18
+ # mouth) and the `architect ping` payload — one small "where is the loop
19
+ # now" fact per playbook step, the between-command currency that lets
20
+ # narrations stay rare prose.
17
21
  module IterationEmission
18
22
  # Frozen tuning (mirrors the spec; session-sink pins its own constants the
19
23
  # same way): per attempt 2s open / 5s read, at most 2 attempts, retry on
@@ -22,13 +26,20 @@ module Space::Architect
22
26
  READ_TIMEOUT = 5
23
27
  MAX_ATTEMPTS = 2
24
28
 
25
- # The four server mouths (I15, frozen): POST <host>/iterations/ingest/<mouth>.
26
- MOUTHS = %w[freeze lane verdict integration].freeze
29
+ # The server mouths (I15's four frozen; I11 appends the fifth): POST
30
+ # <host>/iterations/ingest/<mouth>.
31
+ MOUTHS = %w[freeze lane verdict integration phase].freeze
27
32
 
28
33
  # Iteration/lane status vocabularies (frozen): nothing else is emitted.
29
34
  ITERATION_STATUSES = %w[frozen integrated].freeze
30
35
  LANE_STATUSES = %w[complete integrated].freeze
31
36
 
37
+ # The phase-ping vocabulary (I11, CLI-frozen): nothing else pings. Sibling
38
+ # of the narration kinds — narrations stay rare prose at verdict-landed
39
+ # (+ exception, plan-complete); these pings carry the between-command
40
+ # "where is the loop now" currency.
41
+ PHASE_KINDS = %w[spec-launched builders-settled judge-running].freeze
42
+
32
43
  # The space-slug rule (architect_project.rb :1642), with the emission-layer
33
44
  # fallback: space.data["id"] when the title is absent or slugifies empty.
34
45
  def self.slug_for(space)
@@ -269,6 +280,18 @@ module Space::Architect
269
280
  }
270
281
  end
271
282
 
283
+ # One phase ping (I11, the phase mouth): the iteration identity plus where
284
+ # the loop stands. The iteration hash carries ONLY ordinal + name — the
285
+ # mouth's other iteration keys belong to the other mouths and the ping
286
+ # path never sends them. note rides when given; nil/empty is omitted
287
+ # entirely (compact_scalars discipline).
288
+ def self.build_phase(ordinal:, name:, kind:, note: nil)
289
+ {
290
+ "iteration" => compact_scalars("ordinal" => ordinal, "name" => name),
291
+ "phase" => compact_scalars("kind" => kind, "note" => note)
292
+ }
293
+ end
294
+
272
295
  # ---- the ```rulings block (S3) ------------------------------------------
273
296
  #
274
297
  # A fenced YAML block inside the ## Verdict body — the ```gates/```lanes
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Space
4
4
  module Architect
5
- VERSION = "9.8.0"
5
+ VERSION = "9.9.0"
6
6
  end
7
7
  end
@@ -84,8 +84,10 @@ the space durable across sessions:
84
84
  `build/<id>-<lane>/run.jsonl`).
85
85
  - **judge** — a fresh session that runs the cold gates and rules per-AC
86
86
  verdicts; nobody grades their own work.
87
- - **narrator** — writes one `notes/narrative-*` best-effort update per
88
- settled loop event; facts only, never a gate.
87
+ - **narrator** — writes rare prose: one best-effort `notes/narrative-*` per
88
+ iteration at verdict-landed (plus exception, plan-complete), written for a
89
+ human whose context went stale; never a gate — the between-command currency
90
+ is `architect ping` phase pings.
89
91
 
90
92
  **The workspace**
91
93
 
@@ -56,7 +56,7 @@ project — loads them.
56
56
  | **spec** | subagent | one iteration's Grounds/Specification/Acceptance Criteria; rehearses; freezes; writes the `notes/spec-digest-*` handoff |
57
57
  | **builder** | subagent per lane | implementation in the lane worktree; raw-results report; never commits |
58
58
  | **judge** | subagent, fresh session | mechanical post-flight, cold gates, per-AC verdicts, PHASE 0 rulings, integration |
59
- | **narrator** | subagent | one `notes/narrative-*` file per settled loop event — facts only, never a gate |
59
+ | **narrator** | subagent | rare prose: one `notes/narrative-*` file per iteration at verdict-landed (plus exception, plan-complete), written for a human whose context went stale — never a gate; the between-command currency is `architect ping` phase pings |
60
60
 
61
61
  ## Visibility while a loop runs
62
62
 
@@ -11,7 +11,9 @@ completion wakes, not `architect dispatch` headless runs. Per iteration:
11
11
  ENTRY verbatim. Writes Grounds/Specification/Acceptance Criteria via
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
- `notes/spec-digest-*` handoff. `timeoutMs` 3600000 (1h).
14
+ `notes/spec-digest-*` handoff. `timeoutMs` 3600000 (1h). Once the spec
15
+ spawn is confirmed running, ping the phase — best-effort bash, never a
16
+ gate: `architect ping <iteration> spec-launched || true`.
15
17
  2. **provision** — the driver runs `architect provision <iteration>` via
16
18
  bash; the CLI materializes every declared lane's worktree + lane branch.
17
19
  3. **prompt copy** — the driver copies each spec-assembled scratch prompt
@@ -20,14 +22,18 @@ completion wakes, not `architect dispatch` headless runs. Per iteration:
20
22
  4. **builder per lane** — agent `architect-builder`, cwd = that lane's
21
23
  worktree, task = read `build/<id>-<lane>/prompt.md` before anything else.
22
24
  `timeoutMs` 7200000 (2h). The lane-prompt template below is the contract
23
- the spec assembles against.
25
+ the spec assembles against. When every builder is tended to completion,
26
+ ping the phase — best-effort bash, never a gate:
27
+ `architect ping <iteration> builders-settled || true`.
24
28
  5. **judge** — agent `architect-judge`, cwd = the space root, task = judge the
25
29
  iteration by id. `timeoutMs` 7200000 (2h). The judge is a fresh session —
26
30
  §1 fresh-session judgment holds structurally: the judge session never
27
- dispatched or built anything.
28
- 6. **narrator** — one `architect-narrator` spawn per settled event
29
- (builders-settled, judge-settled, verdict-landed, exception, plan-complete),
30
- `timeoutMs` 300000 (5m), best-effort, never a gate on the loop.
31
+ dispatched or built anything. Once the judge spawn is confirmed running,
32
+ ping the phase — best-effort bash, never a gate:
33
+ `architect ping <iteration> judge-running || true`.
34
+ 6. **narrator** — one `architect-narrator` spawn per narration
35
+ (verdict-landed, exception, plan-complete), `timeoutMs` 300000 (5m),
36
+ best-effort, never a gate on the loop.
31
37
 
32
38
  Spawn contract (all paths): every spawn pins its model explicitly
33
39
  (provider/id[:thinking]) and carries an explicit `timeoutMs` — never the
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.8.0
4
+ version: 9.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Jacobs