space-architect 2.0.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7251e017d780c7fc4accd3d4ee367d2b5063bb4d0c1692c0c69bc81a228c9fa7
4
- data.tar.gz: ad25da0cf6025716953685d7483e86898368170967a29465625335337f8427a3
3
+ metadata.gz: 135a6282cfe3e3073265b71867e7d228673cbfb9b7253feaaba5ad67a180b7cb
4
+ data.tar.gz: 0d0406a99feadadebb24a6167c11837de25bede933e752ef6507524208f4e5a7
5
5
  SHA512:
6
- metadata.gz: baf43e94bcd35987bb133f3958709bfc4083851732ed2d47ef72b25b92e042cfb72c2594f1067189e9581c554140cd2d6145dc7d607f9137931250522ba445dc
7
- data.tar.gz: 0ecd90d05ea7dc4c3832fea90f7af76650b1c8279ced9e7e48242b825aaeef8e7291d6dc9b4dacd6bfac00ad647ecca887a2f27abb3823181287885ccede7210
6
+ metadata.gz: c8b161eff4f4b5a788c2e3c9b423bcc31a495915a076afb70445f6b0cdaf1cc2573ee381ce0c3e09ca712a8398b70ca091b015260b6b4e9c24914ece4a0f5539
7
+ data.tar.gz: f61cdd897881530ad2e81df5bd846c4e644fb85ac6847c4afd147e643b6045b59c7ecb89fd12b83f9a7b5c2ef12813c5e88e7634449a919aac2e4bc75052a6b0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,37 @@ 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
+ ## [3.0.0] - 2026-07-01
9
+
10
+ ### Added
11
+
12
+ - **`architect bug-report`** — zero-friction bug-filing command. Gathers
13
+ diagnostics (gem version, Ruby version/platform, and when run inside a space:
14
+ space id, title, and iteration list with verdicts), writes a prefilled GitHub
15
+ issue-body template to `<space>/build/bug-report/architect-bug-report-YYYYMMDD-HHMMSS.md`
16
+ (or `./architect-bug-report-YYYYMMDD-HHMMSS.md` outside a space — timestamped so
17
+ back-to-back runs in the same session never overwrite each other), and prints —
18
+ never executes — the `gh issue create -R jetpks/space-architect` invocation to run.
19
+
20
+ ### Removed
21
+
22
+ - **BREAKING — `architect land` removed** (added in 2.0.0) — landing is the architect
23
+ skill's procedure: the architect writes the PR body and presents the push +
24
+ PR command. The command as shipped never produced a runnable block (#25) and
25
+ authored content the CLI has no context for (#24).
26
+
27
+ ### Fixed
28
+
29
+ - **`architect bug-report` — `~`-contracted paths in printed commands.**
30
+ The `gh issue create` invocation printed to stdout renders the
31
+ `--body-file` path as `~/…` instead of the user's expanded `$HOME`.
32
+ `Space::Core::Paths.contract` is the single home-contraction helper; `Terminal#path`
33
+ delegates to it.
34
+ - **Command wrapping for narrow terminals.** The `gh issue create` command is
35
+ now rendered with trailing ` \` continuations broken at `--flag` boundaries,
36
+ continuation lines indented two spaces. The wrapped output is valid shell
37
+ (`bash -n` clean). `Space::Core::Commands.wrap` is the single wrapping helper.
38
+
8
39
  ## [2.0.2] - 2026-07-01
9
40
 
10
41
  ### Added
data/README.md CHANGED
@@ -302,7 +302,7 @@ architect evidence <it> --lane <lane> # transcribe the builder's report ve
302
302
  architect gate <iteration> # run the frozen gate commands, stream raw output
303
303
  architect merge <it> <lane> # integrate ONE judged-passing lane (--no-ff)
304
304
  architect integrate <it> --lanes a,b # integrate a set of passing lanes, in order
305
- architect land # end-of-project PR command (no push, no gh)
305
+ # landing (PR body + push/PR command) is the architect's end-of-project procedure (see skill)
306
306
  architect status # project state (read-only)
307
307
  architect variant add|compare|promote … # competing (harness, model) lanes over one frozen spec
308
308
  architect research dispatch|status|wait … # parallel read-only research lanes (see below)
@@ -321,7 +321,7 @@ architect evidence my-feature --lane lane-a # transcribe raw evidence
321
321
  architect gate my-feature # run the frozen gates yourself
322
322
  # … read the diff against the spec, then write the Verdict …
323
323
  architect integrate my-feature --lanes lane-a # merge passing lanes → project/<slug>
324
- architect land # print gh pr create at project end
324
+ # landing (PR body + push/PR command) is the architect's end-of-project procedure (see skill)
325
325
  ```
326
326
 
327
327
  ### Streaming builder output 📡
@@ -415,43 +415,6 @@ module Space::Architect
415
415
  merged
416
416
  end
417
417
 
418
- # Generate the end-of-project PR command(s) for each integrated repo.
419
- # Writes a PR body to build/land/<repo>-pr-body.md and returns, per repo:
420
- # { repo:, integration_branch:, body_file:, command:, context: }.
421
- # Raises Space::Core::Error if nothing has been integrated yet.
422
- # Side-effect-free: no git write, no push, no gh.
423
- def land
424
- b = space.data["project"] || {}
425
- integration_branch = project_integration_branch
426
-
427
- integrated_lanes = (b["iterations"] || []).flat_map do |s|
428
- (s["lanes"] || []).filter_map { |l| { iteration: s, lane: l } if l["integration_branch"] }
429
- end
430
-
431
- raise Space::Core::Error, "nothing integrated yet — integrate a lane before landing" if integrated_lanes.empty?
432
-
433
- repos = integrated_lanes.map { |e| e[:lane]["repo"] }.uniq
434
-
435
- repos.map do |repo|
436
- body_dir = space.path.join("build", "land")
437
- FileUtils.mkdir_p(body_dir)
438
- body_path = body_dir.join("#{repo}-pr-body.md")
439
-
440
- iterations = b["iterations"] || []
441
- body = +"# #{space.title}\n\nMerges `#{integration_branch}` → `main`.\n\n## Iterations\n\n"
442
- iterations.each do |s|
443
- nn = format("%02d", s["ordinal"])
444
- verdict = s["verdict"] || "—"
445
- body << "- I#{nn} #{s["name"]} — #{verdict}\n"
446
- end
447
- body_path.write(body)
448
-
449
- cmd = %(gh pr create --base main --head #{integration_branch} --title "#{space.title}" --body-file #{body_path})
450
- context = "# Run from repos/#{repo} on branch #{integration_branch} (gh pushes it)"
451
- { repo: repo, integration_branch: integration_branch, body_file: body_path.to_s, command: cmd, context: context }
452
- end
453
- end
454
-
455
418
  # Run the iteration's frozen Acceptance Criteria gate commands. Each gate is
456
419
  # executed in the resolved cwd (per-gate `cwd` overrides the base dir), under
457
420
  # a hard timeout, and evaluated against its `expect` block. Returns an array
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "pathname"
5
+
6
+ module Space
7
+ module Architect
8
+ module BugReport
9
+ REPO = "jetpks/space-architect"
10
+
11
+ class << self
12
+ def generate(space: nil, env: ENV, cwd: Dir.pwd, now: Time.now)
13
+ body_path = resolve_body_path(space, cwd, now)
14
+ FileUtils.mkdir_p(body_path.dirname)
15
+ body = build_body(space)
16
+ body_path.write(body)
17
+ contracted = Space::Core::Paths.contract(body_path, env: env)
18
+ command = Space::Core::Commands.wrap(
19
+ %(gh issue create -R #{REPO} --title "<one-line summary>" --body-file #{contracted})
20
+ )
21
+ { body_path: body_path, command: command, body: body }
22
+ end
23
+
24
+ private
25
+
26
+ def resolve_body_path(space, cwd, now)
27
+ filename = "architect-bug-report-#{now.strftime('%Y%m%d-%H%M%S')}.md"
28
+ if space
29
+ space.path.join("build", "bug-report", filename)
30
+ else
31
+ Pathname.new(cwd).join(filename)
32
+ end
33
+ end
34
+
35
+ def build_body(space)
36
+ body = +template_header
37
+ body << diagnostics_section
38
+ body << space_section(space) if space
39
+ body
40
+ end
41
+
42
+ def template_header
43
+ <<~MD
44
+ <!-- Title: <one-line summary> -->
45
+
46
+ **Kind:** <!-- process / tooling / both -->
47
+
48
+ ## Summary
49
+
50
+ <!-- One sentence describing the bug. -->
51
+
52
+ ## What happened
53
+
54
+ <!-- Describe what you observed. -->
55
+
56
+ ## What was expected
57
+
58
+ <!-- Describe what you expected to happen. -->
59
+
60
+ ## Repro steps
61
+
62
+ <!-- Numbered steps to reproduce. -->
63
+
64
+ MD
65
+ end
66
+
67
+ def diagnostics_section
68
+ <<~MD
69
+ ## Diagnostics
70
+
71
+ - space-architect: #{Space::Core::VERSION}
72
+ - ruby: #{RUBY_VERSION} (#{RUBY_PLATFORM})
73
+ MD
74
+ end
75
+
76
+ def space_section(space)
77
+ iterations = Array(space.data.dig("project", "iterations"))
78
+ iter_lines = iterations.map do |s|
79
+ nn = format("%02d", s["ordinal"])
80
+ verdict = s["verdict"] || "—"
81
+ "- I#{nn} #{s["name"]} — #{verdict}"
82
+ end.join("\n")
83
+
84
+ +"\n## Space context\n\n" \
85
+ "- Space id: #{space.id}\n" \
86
+ "- Space title: #{space.title}\n" \
87
+ "\n### Iterations\n\n" \
88
+ "#{iter_lines.empty? ? "(none)" : iter_lines}\n"
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -389,27 +389,6 @@ module Space::Architect
389
389
  end
390
390
  end
391
391
 
392
- class Land < BaseCommand
393
- desc "Generate the end-of-project PR command (no push, no gh — prints gh pr create)"
394
- argument :space, required: false, desc: "Space identifier (default: $PWD)"
395
-
396
- def call(space: nil, **opts)
397
- setup_terminal(**opts.slice(:color, :colors))
398
- handle_errors do
399
- render(store.find(space)) do |sp|
400
- project = ArchitectProject.new(space: sp)
401
- results = project.land
402
- results.each do |r|
403
- terminal.say r[:context]
404
- terminal.say r[:command]
405
- terminal.say "Body: #{terminal.path(r[:body_file])}"
406
- end
407
- CLI.record_outcome(Outcome.new(exit_code: 0))
408
- end
409
- end
410
- end
411
- end
412
-
413
392
  class Gate < BaseCommand
414
393
  desc "Run the frozen Acceptance Criteria gate commands and report PASS/FAIL"
415
394
  argument :iteration, required: true, desc: "Iteration name"
@@ -439,6 +418,29 @@ module Space::Architect
439
418
  end
440
419
  end
441
420
 
421
+ class BugReport < BaseCommand
422
+ desc "Generate a prefilled GitHub issue template for filing bugs against space-architect"
423
+
424
+ def call(**opts)
425
+ setup_terminal(**opts.slice(:color, :colors))
426
+ handle_errors do
427
+ space = store.find.value_or(nil)
428
+ result = Space::Architect::BugReport.generate(
429
+ space: space,
430
+ env: project_config.env
431
+ )
432
+ terminal.say "Fill the placeholders in #{terminal.path(result[:body_path].to_s)}, then run:"
433
+ terminal.say result[:command]
434
+ terminal.say ""
435
+ terminal.say "Diagnostics:"
436
+ terminal.say " space-architect #{Space::Core::VERSION}"
437
+ terminal.say " ruby #{RUBY_VERSION} (#{RUBY_PLATFORM})"
438
+ terminal.say " space: #{space.id} — #{space.title}" if space
439
+ CLI.record_outcome(Outcome.new(exit_code: 0))
440
+ end
441
+ end
442
+ end
443
+
442
444
  class InstallSkills < BaseCommand
443
445
  desc "Install bundled skills (architect, architect-research, architect-vocabulary) for a harness"
444
446
  option :provider, default: "claude", desc: "Harness: claude, codex, opencode, pi"
@@ -652,9 +654,9 @@ Space::Architect::CLI::Registry.register "verdict", Space::Architect::CLI::Arc
652
654
  Space::Architect::CLI::Registry.register "evidence", Space::Architect::CLI::Architect::Evidence
653
655
  Space::Architect::CLI::Registry.register "merge", Space::Architect::CLI::Architect::Merge
654
656
  Space::Architect::CLI::Registry.register "integrate", Space::Architect::CLI::Architect::Integrate
655
- Space::Architect::CLI::Registry.register "land", Space::Architect::CLI::Architect::Land
656
657
  Space::Architect::CLI::Registry.register "gate", Space::Architect::CLI::Architect::Gate
657
658
  Space::Architect::CLI::Registry.register "install-skills", Space::Architect::CLI::Architect::InstallSkills
659
+ Space::Architect::CLI::Registry.register "bug-report", Space::Architect::CLI::Architect::BugReport
658
660
  Space::Architect::CLI::Registry.register "brief" do |b|
659
661
  b.register "new", Space::Architect::CLI::Architect::Brief::New
660
662
  end
@@ -10,5 +10,6 @@ require_relative "space_architect/gate_lint"
10
10
  require_relative "space_architect/gate_evaluator"
11
11
  require_relative "space_architect/architect_project"
12
12
  require_relative "space_architect/skill_installer"
13
+ require_relative "space_architect/bug_report"
13
14
  require_relative "space_architect/research"
14
15
  require_relative "space_architect/cli"
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Space::Core
4
+ module Commands
5
+ module_function
6
+
7
+ # Render a multi-flag command with trailing " \" continuations broken at
8
+ # "--flag" boundaries, continuation lines indented two spaces. Commands
9
+ # without "--" flags are returned unchanged.
10
+ def wrap(command)
11
+ parts = command.split(/(?= --)/)
12
+ return command if parts.size <= 1
13
+
14
+ parts.each_with_index.map do |part, i|
15
+ segment = i.zero? ? part.rstrip : " #{part.lstrip}"
16
+ i < parts.size - 1 ? "#{segment} \\" : segment
17
+ end.join("\n")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Space::Core
4
+ module Paths
5
+ module_function
6
+
7
+ def contract(path, env: ENV)
8
+ value = path.to_s
9
+ home = XDG.home(env: env)
10
+ [home, realpath_or_nil(home)].compact.uniq.each do |h|
11
+ return "~" if value == h
12
+ return "~#{value.delete_prefix(h)}" if value.start_with?("#{h}/")
13
+ end
14
+ value
15
+ end
16
+
17
+ def realpath_or_nil(path)
18
+ File.realpath(path)
19
+ rescue SystemCallError
20
+ nil
21
+ end
22
+ end
23
+ end
@@ -48,13 +48,7 @@ module Space::Core
48
48
  end
49
49
 
50
50
  def path(path)
51
- value = path.to_s
52
- homes.each do |home|
53
- return "~" if value == home
54
- return "~#{value.delete_prefix(home)}" if value.start_with?("#{home}/")
55
- end
56
-
57
- value
51
+ Paths.contract(path, env: config.env)
58
52
  end
59
53
 
60
54
  def with_spinner(message)
@@ -108,17 +102,6 @@ module Space::Core
108
102
  end
109
103
  end
110
104
 
111
- def homes
112
- home = XDG.home(env: config.env)
113
- [home, realpath_or_nil(home)].compact.uniq
114
- end
115
-
116
- def realpath_or_nil(path)
117
- File.realpath(path)
118
- rescue SystemCallError
119
- nil
120
- end
121
-
122
105
  def table_row(headers, row, column_widths, header: false)
123
106
  row.each_with_index.map do |cell, index|
124
107
  raw = cell.to_s
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Space
4
4
  module Core
5
- VERSION = "2.0.2"
5
+ VERSION = "3.0.0"
6
6
  end
7
7
  end
data/lib/space_core.rb CHANGED
@@ -10,6 +10,8 @@ require "space_core/warnings"
10
10
  Space::Core::Warnings.disable_experimental!
11
11
  require "space_core/atomic_write"
12
12
  require "space_core/xdg"
13
+ require "space_core/paths"
14
+ require "space_core/commands"
13
15
  require "space_core/config"
14
16
  require "space_core/state"
15
17
  require "space_core/slugger"
@@ -110,6 +110,7 @@ loop.
110
110
  8. **Stop conditions:** failing verification you can't root-cause, instructions
111
111
  conflicting with project docs, irreversible/destructive calls, or scope
112
112
  growth beyond the iteration → checkpoint to the handoff and ask the human.
113
+ For bugs in the architect tooling or process itself, run `architect bug-report` — it prints a prefilled issue template and the `gh` command to file it.
113
114
 
114
115
  ## Procedure
115
116
 
@@ -373,10 +374,15 @@ cited BRIEF §sections (per §2), then write the **Verdict** (`architect verdict
373
374
  <iteration> continue|kill --from <file>`): disagreement rulings, per-AC
374
375
  PASS/FAIL/INVALID, the KILL/CONTINUE call.
375
376
 
376
- At project end, `architect land` prints the single `gh pr create --base main
377
- --head project/<slug>` command per touched repo and writes a PR body to
378
- `build/land/`no push, no `gh` call; the human runs it from the repo when
379
- the project is ready to ship.
377
+ At project end, landing is yours, not the CLI's the PR body is judgment
378
+ output, the same class as a Verdict. Per touched repo: write the PR body
379
+ yourselffrom the iteration verdicts, the integrated diff, and the BRIEF
380
+ to `build/land/<repo>-pr-body.md`, then present the paste-and-run block to the
381
+ human: `cd` to the repo checkout, `git push -u origin project/<slug>`, and
382
+ `gh pr create --base main --head project/<slug> --title … --body-file …` —
383
+ paths `~`-contracted, the multi-flag command broken with trailing ` \` at flag
384
+ boundaries. No push, no `gh` call by you or the CLI; the human runs the block
385
+ when the project is ready to ship.
380
386
 
381
387
  ## Maintenance
382
388
 
@@ -112,7 +112,9 @@ lanes pass; the CLI does the git mechanics. Canonical path:
112
112
  architect integrate <iteration> --lanes <passing-set> # e.g. --lanes lane-a,lane-b
113
113
  architect gate <iteration> # integration smoke (raw output; verdict stays yours)
114
114
  architect integrate <iteration> --lanes <passing-set> --teardown # or remove worktrees + lane branches after
115
- architect land # end of project: prints gh pr create --base main --head project/<slug>
115
+ # end of project: landing is the architect's, not a CLI command — write the PR
116
+ # body to build/land/<repo>-pr-body.md yourself, then present the paste-and-run
117
+ # block (cd, git push -u origin project/<slug>, gh pr create) — see SKILL.md §6
116
118
  ```
117
119
 
118
120
  `architect integrate` commits each named lane on its branch and merges it
@@ -138,8 +140,8 @@ git -C repos/<repo> merge --no-ff lane/<iteration>-<lane>
138
140
  <run the gate commands> # integration smoke after every merge
139
141
  architect worktree remove <iteration> <lane>
140
142
  git -C repos/<repo> branch -d lane/<iteration>-<lane>
141
- # at project end:
142
- architect land # prints gh pr create --base main --head project/<slug>
143
+ # at project end there is no CLI step: the architect writes
144
+ # build/land/<repo>-pr-body.md and presents the push + gh pr create block
143
145
  ```
144
146
 
145
147
  ### Parallel + fast-follow
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: 2.0.2
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Jacobs
@@ -240,6 +240,7 @@ files:
240
240
  - exe/src
241
241
  - lib/space_architect.rb
242
242
  - lib/space_architect/architect_project.rb
243
+ - lib/space_architect/bug_report.rb
243
244
  - lib/space_architect/cli.rb
244
245
  - lib/space_architect/cli/architect.rb
245
246
  - lib/space_architect/cli/research.rb
@@ -281,6 +282,7 @@ files:
281
282
  - lib/space_core/cli/show.rb
282
283
  - lib/space_core/cli/status.rb
283
284
  - lib/space_core/cli/use.rb
285
+ - lib/space_core/commands.rb
284
286
  - lib/space_core/config.rb
285
287
  - lib/space_core/errors.rb
286
288
  - lib/space_core/git_client.rb
@@ -288,6 +290,7 @@ files:
288
290
  - lib/space_core/oci_builder.rb
289
291
  - lib/space_core/oci_packer.rb
290
292
  - lib/space_core/oci_runner.rb
293
+ - lib/space_core/paths.rb
291
294
  - lib/space_core/repo_reference.rb
292
295
  - lib/space_core/repo_resolver.rb
293
296
  - lib/space_core/shell_integration.rb