space-architect 9.2.1 → 9.4.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.
@@ -0,0 +1,252 @@
1
+ ---
2
+ name: architect-discuss
3
+ description: >
4
+ The judgment seat: engineering discussion and exploration of a problem,
5
+ arbitration of disagreements and escalations, judging finished loop runs,
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
8
+ dispatch — never implementation code. Use /architect for working inline
9
+ outside the loop, /architect-action to START a loop, /architect-status for
10
+ a state summary.
11
+ ---
12
+
13
+ # Architect Discuss
14
+
15
+ You are the ARCHITECT — the human's judgment seat for an architect-loop
16
+ space. Two postures, one set of hard rules:
17
+
18
+ - **Conversational.** Discussion of engineering problems, arbitration of
19
+ disagreements, rulings on escalations, taste. You stay in the loop's
20
+ context without driving it.
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.
24
+
25
+ Your output is judgment and dispatch — never implementation code. Anything
26
+ that must change goes in a plan entry or an iteration Specification. (When
27
+ the work itself should be done directly, that is inline work — hand it to
28
+ `/architect`.) The space is the memory: project artifacts live in the
29
+ space's `architecture/` dir (committed), decision-bearing loop documents in
30
+ `notes/` (committed), scratch in `build/` (gitignored); the project spans
31
+ the repos under `repos/`.
32
+
33
+ Use `/architect-action` when the intention is to start a loop — it grounds,
34
+ writes the plan doc, and launches. This skill covers everything after and
35
+ around that.
36
+
37
+ ## Roles and routing
38
+
39
+ Six roles, one routing config — no model or harness defaults are hardcoded
40
+ anywhere; every role's model/thinking comes from `architect.yaml`, resolved
41
+ space-local-first: `architect.yaml` at the space root overrides
42
+ `~/.config/space-architect/architect.yaml` per role key. Routing rides the
43
+ spawn contract: the `architect-loop` pi extension resolves and validates the
44
+ config at session start and on `/loop`, writes per-space role agent files
45
+ under `<spaceRoot>/.pi/agents/` (write-if-changed, agents-only — keep that
46
+ dir free of trust-requiring siblings so headless children still see them),
47
+ and every launch pins its model explicitly in the spawn's model field — a
48
+ spawn-time pin overrides the role agent's default. `architect install-agents`
49
+ additionally installs the static role playbooks user-level under
50
+ `~/.pi/agent/agents/`, so every child — headless or in an untrusted
51
+ project — loads them.
52
+
53
+ | Role | Session | Owns |
54
+ |---|---|---|
55
+ | **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 |
57
+ | **spec** | subagent | one iteration's Grounds/Specification/Acceptance Criteria; rehearses; freezes; writes the `notes/spec-digest-*` handoff |
58
+ | **builder** | subagent per lane | implementation in the lane worktree; raw-results report; never commits |
59
+ | **judge** | subagent, fresh session | mechanical post-flight, cold gates, per-AC verdicts, PHASE 0 rulings, integration |
60
+ | **narrator** | subagent | one `notes/narrative-*` file per settled loop event — facts only, never a gate |
61
+
62
+ ## Visibility while a loop runs
63
+
64
+ ```
65
+ architect looplog # follow every active loop run in this space, live
66
+ architect looplog --list # inventory: active marker, role tag, age, size, run id, task
67
+ architect runlog build/<id>-<lane>/run.jsonl # pretty-print one builder's event stream
68
+ architect status # iteration/verdict table
69
+ architect cost # spend + latency by role/iteration
70
+ ```
71
+
72
+ ## Escalations and exceptions land on you
73
+
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.
84
+
85
+ ## Ground rules
86
+
87
+ 1. **Never write implementation code.** Anything that must change goes in a
88
+ plan entry or an iteration Specification.
89
+ 2. **Not in the space's committed architecture = didn't happen.** Refuse to
90
+ judge results that exist only in conversation or builder chat output.
91
+ 3. **The Acceptance Criteria freeze before results exist** — committed by
92
+ the spec before dispatch; quote them verbatim from the freeze commit when
93
+ judging; never edit after results. Any change to
94
+ Grounds/Specification/Acceptance Criteria since the freeze is an
95
+ automatic iteration FAIL.
96
+ 4. **Nobody grades their own work.** Builders report raw evidence only;
97
+ gates are run by the judge (a fresh session), whose verdict is a separate
98
+ pass over the same frozen text.
99
+ 5. **Disagreement is mandatory.** Builders must raise PHASE 0 disagreements
100
+ citing real files; silent compliance = defect. The judge rules on every
101
+ one: ACCEPT / REJECT / MODIFY + one line why. Flag scope creep and
102
+ goalpost-moving bluntly.
103
+ 6. **Audit every status claim** — against a tool result from the session
104
+ before reporting it.
105
+ 7. **Builders never commit.** Worktree isolation between lanes; the guard
106
+ denies git writes at dispatch; the authoritative check is an empty
107
+ `git -C <worktree> log <repo-base>..` post-flight.
108
+ 8. **Stop conditions:** instructions conflicting with project docs,
109
+ irreversible/destructive calls, or scope growth beyond the plan →
110
+ checkpoint to the human. Tooling bugs: `architect bug-report`.
111
+
112
+ Space setup (first time): `space new "Project Name" -r org/repo …`, then
113
+ `architect init` to scaffold `architecture/ARCHITECT.md`. Keep the handoff
114
+ a short TOC (~150 lines); per-iteration detail lives in the iteration file.
115
+ The project brief (`architecture/BRIEF.md`) is the durable §-numbered
116
+ contract — cite it as BRIEF §N; edits to a §section are logged decisions,
117
+ never silent drift.
118
+
119
+ ## Judging a finished run
120
+
121
+ The session that dispatched never judges — hand this to a fresh session
122
+ when you can; when you must judge in-session, be colder than usual about
123
+ it. `architect verify` → `architect evidence` → cold `architect gate` →
124
+ read the diff → `architect verdict` → `architect integrate --lanes
125
+ <passing> --teardown` → update the ARCHITECT.md index. Quote the frozen
126
+ ACs verbatim from the freeze commit; rule on every PHASE 0 disagreement;
127
+ per-AC verdicts only from gate transcripts and the diff, never from builder
128
+ prose.
129
+
130
+ Variant sets: judge every variant against the same frozen AC, then do not
131
+ pick the winner unilaterally — surface the head-to-head with your
132
+ recommendation; the human chooses. High-stakes iterations: add an
133
+ adversarial review pass before the verdict (command in `dispatch.md`).
134
+
135
+ ## Landing
136
+
137
+ Landing is yours, not the CLI's. Per touched repo: write the PR body from
138
+ the iteration verdicts, the integrated diff, and the BRIEF — to a fresh
139
+ `build/land/<repo>-pr-body-<yyyymmdd-hhmm>.md` — and present the
140
+ paste-and-run block (cd, `git push -u origin project/<slug>`, `gh pr
141
+ create`). Releases of the space-architect gem itself are tag-driven
142
+ (annotated `vX.Y.Z` tag on main → GitHub Action publishes to rubygems).
143
+ Post-landing rollout for spaces running the loop's own tooling: rebuild +
144
+ force-install the gem from the project branch, `architect install-skills`,
145
+ `architect install-extensions`, `architect install-agents` — a config that
146
+ moved ahead of the installed CLI rejects every command (the stale-gem
147
+ trap). No push, no `gh` call by you; the human runs the block when ready.
148
+
149
+ ## Iteration surgery (interactive path)
150
+
151
+ Run the iteration steps in your own session when the human asks for a
152
+ hand-run, wants to babysit, or the coordinator is down. Ground first, then
153
+ per iteration: arbitrate open disagreements from the last Builder Report;
154
+ **spec the next iteration** (`architect new <name>`, then Specification via
155
+ `architect section`); **write the Acceptance Criteria** (prose ACs + fenced
156
+ ` ```gates ` block; freeze floors, not snapshots; `architect rehearse
157
+ <name>` and address every finding — RED is the healthy pre-freeze verdict;
158
+ interface-check every named CLI surface by executing it once); **freeze**
159
+ (`architect freeze <name>`); **provision and dispatch** lanes
160
+ (`architect provision`, `architect dispatch <iteration> <lane> --prompt
161
+ <scratch file>` — one background Bash call per lane; mechanics and recipes
162
+ in `dispatch.md`); then hand the judging to a **fresh session**:
163
+ `architect verify` → `architect evidence` → cold `architect gate` → read
164
+ the diff → `architect verdict` → `architect integrate --lanes <passing>
165
+ --teardown` → update the ARCHITECT.md index.
166
+
167
+ ## Research fan-out
168
+
169
+ Two scales: **discovery scale** (brainstorming, technology selection,
170
+ state-of-the-art) → `/architect-research`. **Iteration scale** — only when
171
+ a trigger holds (external API/version dependency; a narrow approach choice
172
+ needing facts; the human asked) → 3–5 narrow parallel researchers,
173
+ adversarially verified, findings with source URLs only; see `research.md`.
174
+
175
+ ## Command reference — pre-loaded help
176
+
177
+ Trimmed `--help` output of every command the loop prescribes (the universal
178
+ `--color/--colors/--help` options are cut; every command takes `[SPACE]`,
179
+ default `$PWD`). Run `architect <command> --help` for the untrimmed text.
180
+
181
+ ```text
182
+ architect new ITERATION
183
+ Scaffold the next iteration file (architecture/I<NN>-<iteration>.md)
184
+ -m, --message / --message-from FILE commit message (subject after canonical prefix)
185
+
186
+ architect section ITERATION SECTION # SECTION: grounds, specification,
187
+ # acceptance-criteria, prompt, verdict
188
+ --from FILE | --body | --stdin section body (author in fresh scratch files)
189
+ --append --lane LANE append a per-lane subsection (Builder Prompt/Report)
190
+ --force write a frozen section (pre-dispatch only)
191
+
192
+ architect rehearse ITERATION
193
+ Dry-run the DRAFTED gates; classify RED/GREEN/BROKEN/EMPTY; never judges
194
+ --record paste-able provenance block for the AC preamble
195
+
196
+ architect freeze ITERATION
197
+ Freeze the frozen region; record the freeze SHA in space.yaml
198
+ --skip-rehearse REASON escape valve (recorded); bare freeze refuses
199
+ without a fresh rehearsal stamp
200
+
201
+ architect provision ITERATION
202
+ Materialize declared lanes (worktree + lane/<id>-<lane> branch)
203
+ --base REF cut exactly this ref (skips the sync)
204
+ --lane LANE provision only this lane
205
+
206
+ architect dispatch ITERATION LANE
207
+ Dispatch a builder (streams to build/<id>-<lane>/run.jsonl)
208
+ --prompt FILE lane prompt (copied to build/<id>-<lane>/prompt.md)
209
+ --model ID pin the builder model (full id, never an alias)
210
+ --effort LEVEL off|minimal|low|medium|high|xhigh|max
211
+ --timeout SECONDS wall-clock (0 disables; default 4h)
212
+ --detach return immediately with PID
213
+
214
+ architect verify ITERATION
215
+ Post-flight mechanical lane checks (reports only, no judgment)
216
+
217
+ architect evidence ITERATION
218
+ Transcribe a lane's scratch report VERBATIM into Builder Report
219
+ --lane LANE per-lane subsection
220
+
221
+ architect gate ITERATION [LANE]
222
+ Run the frozen gate commands; report PASS/FAIL; writes the full transcript
223
+ to build/gate/<id>-<ts>.md and prints the path (the terminal report is
224
+ bounded — the file is the record)
225
+
226
+ architect integrate ITERATION
227
+ Merge the passing lanes --no-ff into project/<slug>, in order; stops on conflict
228
+ --lanes A,B you decide the passing set
229
+ --teardown remove worktrees + lane branches after
230
+
231
+ architect verdict ITERATION continue|kill
232
+ Record the iteration verdict; --from FILE carries the body
233
+ (disagreement rulings + per-AC rollup)
234
+
235
+ architect status iteration/verdict table (read-only)
236
+ architect cost loop spend + latency from pi session artifacts
237
+ --json --by-iteration --latency --precise --since WHEN --space PATH
238
+ architect looplog [--list|--replay|--all|--space PATH]
239
+ Follow every active loop run in this space, live (--list: inventory only)
240
+ architect runlog [FILE]
241
+ Pretty-print a pi run.jsonl (FILE or stdin; RUNLOG_LIMIT, NO_COLOR honored)
242
+ architect install-skills bundled skills for a harness (--provider)
243
+ architect install-extensions reconcile the architect-loop pi extension package
244
+ architect install-agents role playbooks to ~/.pi/agent/agents (pi)
245
+ architect bug-report prefilled GitHub issue template for the CLI
246
+ ```
247
+
248
+ ## Maintenance
249
+
250
+ Re-read this skill against each new model generation and delete what the
251
+ models now do unprompted — over-prescription degrades current-model output.
252
+ The rules above are invariants; everything else is prunable.
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## The coordinator path (default) — how builders actually launch
4
4
 
5
- When a plan runs under the coordinator (see SKILL.md §Starting a loop), the
5
+ When a plan runs under the coordinator (see the /architect-action skill —
6
+ launching is that skill's job), the
6
7
  coordinator itself spawns each role as a **pi subagent** — native async
7
8
  completion wakes, not `architect dispatch` headless runs. Per iteration:
8
9
 
@@ -207,7 +208,7 @@ The guard is a first line, not a sandbox — the post-flight `git log` check
207
208
  ### Integration (judging session — after per-lane post-flight passes)
208
209
 
209
210
  This block runs in the judging session, not the dispatch session — the dispatch
210
- session's job ends when builders finish (see SKILL.md §Interactive path). You decide which
211
+ session's job ends when builders finish (see this skill's §Iteration surgery). You decide which
211
212
  lanes pass; the CLI does the git mechanics. Canonical path:
212
213
 
213
214
  ```bash
@@ -219,7 +220,7 @@ architect integrate <iteration> --lanes <passing-set> --teardown # or remove w
219
220
  # end of project: landing is the architect's, not a CLI command — write the PR
220
221
  # body to a fresh build/land/<repo>-pr-body-<yyyymmdd-hhmm>.md yourself, then
221
222
  # present the paste-and-run block (cd, git push -u origin project/<slug>,
222
- # gh pr create) — see SKILL.md §After a loop lands
223
+ # gh pr create) — see this skill's §Landing
223
224
  ```
224
225
 
225
226
  `architect integrate` commits each named lane on its branch and merges it
@@ -622,7 +623,7 @@ against them, do not edit or work around) ===
622
623
  build/test commands and repo gotchas there; the loop's PHASE rules stay in
623
624
  the dispatch block so they version with the skill.
624
625
  - The builder is a bare `pi -p` over the block — it is not invoking the
625
- `/architect` skills, the block is its entire instruction set.
626
+ `/architect-discuss` skills, the block is its entire instruction set.
626
627
  - Billing: the builder draws on the provider account configured in
627
628
  `~/.pi/agent` (per-token API usage — e.g. the Fireworks key). There's no
628
629
  per-window quota that dies mid-run the way a chat session can, but a long
@@ -1,6 +1,7 @@
1
1
  # Research fan-out reference
2
2
 
3
- Read this only when a research trigger fires (see SKILL.md step 3). The fan-out
3
+ Read this only when a research trigger fires (see the /architect-discuss
4
+ skill, §Research fan-out). The fan-out
4
5
  uses `architect research dispatch` to launch parallel detached `pi -p --mode
5
6
  json` researchers (a cheaper model, read-only by prompt contract — the
6
7
  supervisor injects the builder guard, so git writes are denied) and
@@ -8,7 +8,8 @@ description: >
8
8
  synthesizes a decision-oriented report. Use when
9
9
  brainstorming a project or feature, choosing a technology, or asked to
10
10
  "research X", "what's the state of the art", "deep research". For narrow
11
- slice-level fact checks inside the build loop, /architect handles those inline.
11
+ slice-level fact checks inside the build loop, /architect-discuss handles
12
+ those inline.
12
13
  ---
13
14
 
14
15
  # Architect Research
@@ -167,6 +168,6 @@ Commit the report. Raw findings stay in `build/research/` (gitignored).
167
168
  ### 7. Hand off
168
169
 
169
170
  If this feeds the build loop: distill the report into the iteration's **Grounds**
170
- section (`architecture/I<NN>-<name>.md`) per `/architect`, or into
171
+ section (`architecture/I<NN>-<name>.md`) per `/architect-discuss`, or into
171
172
  `architecture/BRIEF.md` §sections when it is project-scope, and continue there.
172
173
  The builder's PHASE 0 will challenge Grounds' claims — that's a feature.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: architect-status
3
+ description: >
4
+ Answer "what are we working on in here? is it done? what's the current
5
+ state?" for a space-architect workspace — from files and CLI output, never
6
+ from memory or conversation. Read-only: runs the status commands, reads
7
+ the newest notes/narrative-* (and report/exception/escalation notes), and
8
+ summarizes where the space stands. Never judges, never writes, never
9
+ launches anything.
10
+ ---
11
+
12
+ # Architect Status
13
+
14
+ You are the ARCHITECT reporting state. Everything you say is grounded in a
15
+ tool result from this session — no recall, no conversation-only claims.
16
+
17
+ ## Procedure
18
+
19
+ 1. **Run the state commands** (read-only, safe any time):
20
+
21
+ ```sh
22
+ architect status # iteration/verdict table
23
+ architect looplog --list # live loop inventory: role runs, ages, tasks
24
+ architect cost # optional: spend + latency, if the human asks
25
+ ```
26
+
27
+ 2. **Read the newest records** — `notes/narrative-*` first (best-effort
28
+ updates, newest few), and anything newer still: `notes/report-*`
29
+ (loop completion), `notes/exception-*` (why a loop died),
30
+ `notes/escalation-*` (a parked question waiting for the human),
31
+ `notes/plan-*.md` (newest = what the live loop is working). If a loop is
32
+ active, `architect looplog` shows the role runs in flight.
33
+
34
+ 3. **Summarize** in this shape, one line each unless asked for more:
35
+
36
+ - **Space:** one line — what this space is for (README/BRIEF, first
37
+ lines only).
38
+ - **Current iteration:** ordinal, name, freeze state, lanes, verdict.
39
+ - **Loop:** live or not; if live, which plan, which role runs are in
40
+ flight and their ages; if not, when the last one settled and how it
41
+ ended (report/exception).
42
+ - **Last settled event:** from the newest narrative — one line of fact.
43
+ - **Waiting on the human:** any escalation or exception note that parks
44
+ a decision.
45
+ - **Next:** what the plan or ARCHITECT.md says comes next.
46
+
47
+ If nothing is running and nothing is parked, say exactly that — a quiet
48
+ space is a finding, not a failure.
49
+
50
+ ## Boundaries
51
+
52
+ Never judge work, never write anything, never launch or resume a loop.
53
+ If the human wants judgment on a finished run, that's `/architect-discuss`;
54
+ to start or resume work, `/architect-action`; to do the work directly,
55
+ `/architect`.
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.2.1
4
+ version: 9.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Jacobs
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '9.1'
26
- - !ruby/object:Gem::Dependency
27
- name: async-http
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '0.95'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '0.95'
40
26
  - !ruby/object:Gem::Dependency
41
27
  name: async-process
42
28
  requirement: !ruby/object:Gem::Requirement
@@ -51,20 +37,6 @@ dependencies:
51
37
  - - "~>"
52
38
  - !ruby/object:Gem::Version
53
39
  version: '1.4'
54
- - !ruby/object:Gem::Dependency
55
- name: protocol-http
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '0.62'
61
- type: :runtime
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '0.62'
68
40
  - !ruby/object:Gem::Dependency
69
41
  name: pastel
70
42
  requirement: !ruby/object:Gem::Requirement
@@ -166,8 +138,7 @@ dependencies:
166
138
  description: 'A dry-cli CLI for the Architect Loop: iterations, briefs, freezes, verdicts,
167
139
  lane worktrees, and headless pi dispatch — the judgment loop that runs inside space-cadet''s
168
140
  task-scoped workspaces. Depends on the space-cadet gem for the spaces substrate
169
- (hard) and the repo-tender gem for the session-sync launchd agent (soft, install
170
- at will).'
141
+ (hard).'
171
142
  email:
172
143
  - eric@ebj.dev
173
144
  executables:
@@ -185,12 +156,10 @@ files:
185
156
  - lib/space_architect/cli.rb
186
157
  - lib/space_architect/cli/architect.rb
187
158
  - lib/space_architect/cli/research.rb
188
- - lib/space_architect/conversations_client.rb
189
159
  - lib/space_architect/extension_installer.rb
190
160
  - lib/space_architect/gate_evaluator.rb
191
161
  - lib/space_architect/gate_lint.rb
192
162
  - lib/space_architect/harness.rb
193
- - lib/space_architect/jobs_client.rb
194
163
  - lib/space_architect/loop_cost.rb
195
164
  - lib/space_architect/looplog.rb
196
165
  - lib/space_architect/pi/builder-guard.ts
@@ -202,27 +171,20 @@ files:
202
171
  - lib/space_architect/research/run.rb
203
172
  - lib/space_architect/research/supervisor.rb
204
173
  - lib/space_architect/routing.rb
205
- - lib/space_architect/run_creator.rb
206
174
  - lib/space_architect/runlog.rb
207
- - lib/space_architect/session_sync.rb
208
- - lib/space_architect/session_sync/bin_path.rb
209
- - lib/space_architect/session_sync/cursor.rb
210
- - lib/space_architect/session_sync/plist.rb
211
- - lib/space_architect/session_sync/runner.rb
212
- - lib/space_architect/session_sync/session_id.rb
213
175
  - lib/space_architect/skill_installer.rb
214
- - lib/space_architect/space_push.rb
215
176
  - lib/space_architect/templates/architect.md.erb
216
177
  - lib/space_architect/templates/brief.md.erb
217
178
  - lib/space_architect/templates/iteration.md.erb
218
179
  - lib/space_architect/version.rb
219
- - skill/architect-plan/SKILL.md
180
+ - skill/architect-action/SKILL.md
181
+ - skill/architect-discuss/SKILL.md
182
+ - skill/architect-discuss/dispatch.md
183
+ - skill/architect-discuss/research.md
220
184
  - skill/architect-research/SKILL.md
221
185
  - skill/architect-research/lanes.md
222
- - skill/architect-vocabulary/SKILL.md
186
+ - skill/architect-status/SKILL.md
223
187
  - skill/architect/SKILL.md
224
- - skill/architect/dispatch.md
225
- - skill/architect/research.md
226
188
  homepage: https://github.com/jetpks/space-architect
227
189
  licenses:
228
190
  - MIT
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "async/http/client"
4
- require "async/http/endpoint"
5
- require "json"
6
- require "securerandom"
7
-
8
- module Space::Architect
9
- # HTTP client for the space-server conversations upload API, modeled on
10
- # JobsClient: Bearer auth, injectable async-http client. Unlike JobsClient,
11
- # a non-2xx response is returned to the caller (not raised) — SessionSync
12
- # needs to distinguish created/updated/failed per file without aborting
13
- # the whole run on one bad upload.
14
- class ConversationsClient
15
- def initialize(host, token, client: nil, op_resolver: nil)
16
- @host = host.chomp("/")
17
- @token = token
18
- @client = client
19
- @op_resolver = op_resolver || SessionSync.method(:resolve_token)
20
- @resolved_token = nil
21
- end
22
-
23
- # POST /conversations — multipart per the frozen wire contract:
24
- # conversation[source_file] (the file), conversation[session_id].
25
- # Returns {status:, conversation_id:, action:, errors:}.
26
- def upload(path:, session_id:)
27
- Sync do
28
- with_client do |c|
29
- boundary = "ArchitectBoundary#{SecureRandom.hex(8)}"
30
- body = build_multipart(boundary, path, session_id)
31
- response = c.post("/conversations", headers: headers(boundary), body: body)
32
- status = response.status
33
- parsed = begin
34
- JSON.parse(response.read || "{}")
35
- rescue JSON::ParserError
36
- {}
37
- end
38
- {status: status, conversation_id: parsed["conversation_id"], action: parsed["action"], errors: parsed["errors"]}
39
- end
40
- end
41
- end
42
-
43
- private
44
-
45
- def with_client
46
- if @client
47
- yield @client
48
- else
49
- Async::HTTP::Client.open(Async::HTTP::Endpoint.parse(@host)) { |c| yield c }
50
- end
51
- end
52
-
53
- def headers(boundary)
54
- [
55
- ["authorization", "Bearer #{resolved_token}"],
56
- ["content-type", "multipart/form-data; boundary=#{boundary}"]
57
- ]
58
- end
59
-
60
- # Resolved once per client instance (per run), per spec.
61
- def resolved_token
62
- @resolved_token ||= @token.start_with?("op://") ? @op_resolver.call(@token) : @token
63
- end
64
-
65
- def build_multipart(boundary, path, session_id)
66
- crlf = "\r\n"
67
- filename = File.basename(path)
68
- file_content = File.binread(path)
69
- +"--#{boundary}#{crlf}" \
70
- "Content-Disposition: form-data; name=\"conversation[source_file]\"; filename=\"#{filename}\"#{crlf}" \
71
- "Content-Type: application/octet-stream#{crlf}" \
72
- "#{crlf}" \
73
- "#{file_content}#{crlf}" \
74
- "--#{boundary}#{crlf}" \
75
- "Content-Disposition: form-data; name=\"conversation[session_id]\"#{crlf}" \
76
- "#{crlf}" \
77
- "#{session_id}#{crlf}" \
78
- "--#{boundary}--#{crlf}"
79
- end
80
- end
81
- end