warning: in the working copy of '.planning/STATE.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of '.planning/quick/260602-2ar-communes-and-signoffs-delete-the-more-do/260602-2ar-PLAN.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of '.planning/quick/260602-2ar-communes-and-signoffs-delete-the-more-do/260602-2ar-SUMMARY.md', LF will be replaced by CRLF the next time Git touches it
---
e73ef3a docs(quick-260602-2ar): communes/signoffs clear .more-done sentinel on FILE-DROP consume
0559386 feat(quick-260602-2ar): clear .more-done sentinel when FILE-DROP consumes a commune/signoff
2835a0f docs(35.2): add Phase 35.2 sync data-loss/reconcile/bootstrap entries to v1.11.22 changelog
---STATUS---
 M Cargo.lock
?? .planning/debug/echo-fire-orphan-grace-violation-probetest.md
?? .planning/debug/echo-gate-poll-exit-sentinel-triad.md
?? .planning/debug/file-drop-file-gone-todlando.md
?? .planning/debug/llm-project-md-non-emission-todlando.md
?? .planning/phases/35.1-owl-list-data-loss-cqrs-decouple-orphan-detection-hardening-/35.1-SEED.md
?? .planning/quick/260523-vk1-fresh-live-agent-init-announcement/
?? .planning/seeds/owl-list-data-loss-cqrs-fix.md
?? CONTEXT.md
?? docs/adr/
?? docs/agents/
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Done. Quick task `260602-2ar` complete on `claude_skill_owl@main`.

**What shipped**
- `0559386` (code) — FILE-DROP consume now clears the `.more-done` cadence sentinel.
  - `echo_fire.rs`: extracted inlined delete-both into shared `remove_both_more_done(nested, flat)` + `WrapperState::delete_more_done_sentinel()` (paths via `owlery::nested_perch_dir`/`perch_dir` — honors reader/writer path-asymmetry hazard). Fire-path rewired onto it.
  - `mod.rs::process_file_drop`: `delete_more_done_sentinel()` at 2 consume exits (empty-body signoff skip + `exit_code==0` block = commune + init-stale + normal signoff). NOT in retained/`else` arm → failed consume keeps sentinel.
- `e73ef3a` (docs) — PLAN + SUMMARY + STATE.md quick-task row.

**Verify:** build clean; `echo_fire` 22/22; single-thread full-lib 961→963 (+2 new tests), same 1 pre-existing unrelated fail (`version_changelog`), zero new failures.

**Notes**
- No worktree (session repo = spt-core, target = claude_skill_owl — isolation would fork wrong repo). Ran on main tree.
- No traceable-reqs in claude_skill_owl — no REQ tags added.
- `Cargo.lock` shows modified from executor's build — lockfile churn, left uncommitted.
- Live agents need `docs/DEPLOY.ps1` to pick up new binary.


please switch cwd to `claude_skill_owl` and /gsd-quick make communes and signoffs delete the .more-done sentinel in the same step where FILE-DROP consumes the commune/signoff file.


Remove sentinel file in FILE-DROP processing step


run DEPLOY.md over there and version bump


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


Read DEPLOY.md to follow exact procedure.


1	# Deploying Spacetime (spt) Plugin
2
3	## Automated Deploy (DEPLOY.ps1)
4
5	For the common case — binary + skills + hooks + marketplace + cache refresh for the version already set in `plugin/spt/.claude-plugin/plugin.json` — use the PowerShell script:
6
7	```powershell
8	# From repo root, after bumping plugin.json version:
9	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1
10
11	# Or let the script bump the version for you:
12	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
13	```
14
15	The script automates the build-through-cache-sync sequence and the state refresh (by shelling out to `claude plugin install spt@cplugs`). It **stops before `/reload-plugins`** — you must run that yourself inside Claude Code.
16
17	### What it does (in order)
18
19	1. Reads the authoritative version from `plugin/spt/.claude-plugin/plugin.json`. If `-Bump <patch|minor|major>` is passed, runs the bump sub-sequence: (a) abort gate — refuses to proceed if `CHANGELOG.md` is missing OR still contains a `TODO: changelog entry` marker from a prior bump; (b) rewrites `plugin.json` + `Cargo.toml` to the new version; (c) appends a `TODO: changelog entry` stub H2 to `CHANGELOG.md` for the new version; (d) stages `plugin.json` + `Cargo.toml` + `CHANGELOG.md` and commits as `chore: bump spt plugin to vX.Y.Z` in the spt repo (no push). The bump commit ships with the TODO stub; the user is expected to author a curated `CHANGELOG.md` entry for the new version in a follow-up commit before the next `-Bump`. See "Two-phase changelog authoring" below.
20	2. `cargo build --release` in the repo root.
21	3. Syncs `owl.exe`, `skills/`, `hooks/`, and `plugin.json` to the cplugs marketplace clone.
22	4. Commits + pushes the marketplace (unless skipped).
23	5. Syncs the new version into `~/.claude/plugins/cache/cplugs/spt/<Version>/`, then targeted-prunes older semver directories. The script keeps the NEW version dir, the PREVIOUS version dir (running `owl.exe` processes are still exec'ing from there until they self-migrate via handoff), and the `installPath` leaf dir recorded in `installed_plugins.json` (captured before step 7 rewrites it). Non-semver directories and directories in the keep-set are never touched. Sharing-violation residue (a dir that can't be removed because `owl.exe` still has the `.exe` mapped) is logged as a warning and retried on the next deploy — it does **not** fail the deploy. First-deploy case (no prior `spt@cplugs` entry) skips the prune entirely.
24	6. Cleans up the legacy manual-install dir at `~/.claude/plugins/spt/` if it exists as a regular directory. A manual install coexisting with the marketplace install makes Claude Code report the plugin as **not installed** in `/plugin` (see Troubleshooting below). Symlinked dirs are preserved for the dev-symlink workflow.
25	7. Runs `claude plugin install spt@cplugs` for cache-warm-up and `.orphaned_at` marker cleanup. **This is no longer the version-pointer flip** — the CLI is a no-op when `spt@cplugs` is already installed at any version (it only touches `lastUpdated`, not `version` / `installPath` / `gitCommitSha`). That silent no-op was the failure mode in the 1.9.8 → 1.9.9 deploy that left the pointer pinned to 1.9.8 (see `.planning/debug/26-06-live-skill-regression.md`). If `claude` is not in `PATH`, the script warns and tells you to run the command manually.
26	8. **Explicitly patches `~/.claude/plugins/installed_plugins.json`** to set the `spt@cplugs` entry's `version`, `installPath`, `gitCommitSha`, and `lastUpdated`. Atomic write via temp file + `Move-Item -Force`. This is the authoritative pointer flip — the step 7 CLI call is kept only for its cache-warm-up side effects. Uses `ConvertFrom-Json` / `ConvertTo-Json -Depth 10` (no `JavaScriptSerializer` — its earlier hand-rolled patch was removed for circular-reference crashes; the new patch round-trips through the PSObject graph safely).
27	9. **Re-reads `installed_plugins.json` and asserts `spt@cplugs.version == <Version>`** (and `installPath` matches the new cache dir). Any mismatch writes `ASSERTION FAILED: ...` to stderr and exits the script with status 1; the green "Deploy complete" banner never prints on drift.
28	10. Prints a reminder to run `/reload-plugins` inside Claude Code.
29
30	> The `Write-Step "Step N: ..."` labels in the script body still use their historical numbers (Step 3 for build, Step 4 for marketplace sync, etc. — Step 2 is intentionally absent) so log-grep tooling keeps matching prior runs. The numbered list above is the user-facing sequence; the in-script labels are the log contract.
31
32	### What it does NOT do
33
34	- Bump the plugin version in `plugin.json` unless `-Bump <patch|minor|major>` is passed.
35	- Push the spt repo. Even with `-Bump`, the bump commit lands locally and you push it yourself.
36	- Commit other changes in the spt repo (that's your job).
37	- Edit `~/.claude/settings.json`.
38	- Run `/reload-plugins` (that's a Claude Code slash command).
39	- Kill running `owl.exe` processes. Phase 18.4/18.5 shipped seamless binary handoff — old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json` once the new version is staged and `installed_plugins.json` flips (step 7 above). Killing them aborts the handoff and strands their session state.
40	- Wipe the entire cache tree. The old "blanket `rm -rf` on `cache/cplugs/spt`" behavior was correct before handoff shipped, but now it yanks the `.exe` out from under running processes mid-migration. The new targeted prune (step 5 above) keeps the PREVIOUS version dir on disk so in-flight handoffs complete.
41
42	### Two-phase changelog authoring (Phase 34 D-08 round 2)
43
44	Under the locked D-08 round 2 design (realigned in quick-260517-n4b post-UAT clarification), `-Bump` enforces a curation gate so each version-change Stop-hook surface ships with a real CHANGELOG entry inside the deployed bundle. The first `-Bump` for any new version aborts and seeds a TODO stub; the second `-Bump` (after the user fills it in and commits) proceeds.
45
46	**First `-Bump` run (no curated entry yet):**
47
48	1. **Abort gate** — refuses to proceed if `CHANGELOG.md` is missing OR contains a `TODO: changelog entry` marker from a prior bump.
49	2. **Curation gate** — checks for `## [<NewVersion>]` H2 header in `CHANGELOG.md`:
50	   - **Absent:** appends `## [<NewVersion>] - YYYY-MM-DD\n\n- TODO: changelog entry` to the working tree (uncommitted), then **EXIT 1** with instructions to fill and commit, then re-run. The current deploy does **not** succeed; no `plugin.json` / `Cargo.toml` mutation occurs.
51	   - **Present + TODO marker:** caught by abort gate above → EXIT 1.
52	   - **Present + curated body:** proceed past the gate.
53
54	**User action between runs:** Edit `CHANGELOG.md`, replace `- TODO: changelog entry` under the newly-stubbed `## [<NewVersion>]` H2 with real bullets in Keep-a-Changelog 1.1.0 categories (Added / Changed / Fixed / Removed / Deprecated / Security). Commit as e.g. `docs(NN): fill <NewVersion> CHANGELOG entry`.
55
56	**Second `-Bump` run (curated entry committed):**
57
58	1. Abort gate passes — no `TODO: changelog entry` marker remains.
59	2. Curation gate passes — `## [<NewVersion>]` H2 present with curated body.
60	3. Rewrite `plugin/spt/.claude-plugin/plugin.json` + `Cargo.toml` to the new version.
61	4. Stage + commit `plugin.json` + `Cargo.toml` (two files) as `chore: bump spt plugin to vX.Y.Z`. CHANGELOG.md is **not** in the bump commit — it landed in the prior curated-entry commit.
62	5. Build, sync to marketplace + cache (including curated `CHANGELOG.md`), refresh `installed_plugins.json`.
63
64	**Pre-authoring shortcut:** A caller who knows the next semver upfront can pre-author and commit the `## [<NewVersion>]` H2 with curated body before running `-Bump`. The curation gate sees the H2 present + curated and proceeds in one shot.
65
66	**Backstop:** If a stub somehow slips into a bump commit (manual flow, prior implementation), the next `-Bump`'s abort gate fires on the `TODO: changelog entry` marker and forces the user to curate before any further deploy.
67
68	### Bucket discipline for stub fill-in (quick-260520-uc2)
69
70	The version-change Stop hook surfaces curated CHANGELOG bullets to the user
71	on the next session boundary after a plugin upgrade. The user sees only the
72	release-facing buckets — implementation noise is filtered out at hook time.
73	The deploying agent (human or LLM) filling the `- TODO: changelog entry`
74	stub MUST follow these rules:
75
76	**Section vocabulary per H2 (`## [vX.Y.Z] - YYYY-MM-DD`):**
77
78	| Section | Use for | Surfaced to user? |
79	|---------|---------|---------------------|
80	| `### Added` | New user-facing capability (a flag, command, message, prompt, hook trigger they will notice) | YES |
81	| `### Changed` | Modified user-facing behavior (different message, different default, different timing) | YES |
82	| `### Fixed` | Bug fix the user actually observed or could observe | YES |
83	| `### Removed` | A user-facing capability that no longer exists | YES |
84	| `### Deprecated` | A capability the user should stop relying on | YES |
85	| `### Security` | Anything with user-relevant security impact | YES |
86	| `### Reverted` | A user-visible behavior was rolled back to a prior shape | YES |
87	| `### BTS` | Anything else: internal refactors, repo plumbing, deploy script changes, doc-string drift, test-only changes, code reverts that don't change user-visible behavior, commit SHAs, file-path callouts | **NO** — filtered out at hook time |
88
89	**UX-language rule for Added / Changed / Fixed (and the other surfaced
90	buckets):**
91	- Phrase as what the user sees, does, or runs. "`/spt:live` now offers an
92	  Other option" — not "added `AuqOption::Other` to `live::auto_pick`".
93	- Do NOT mention: code paths (`src/...`), function or struct names, prompt
94	  XML element names (`<spt-foo>`), commit SHAs, planning doc references
95	  (`Phase 34 D-08`, `quick-260517-n4b`), Rust types, file paths inside
96	  `plugin/spt/`, internal env-var names that are not part of the
97	  documented user surface.
98	- Keep bullets terse. One sentence each is the target; two short sentences
99	  is the ceiling. If you need more, you are probably writing BTS content.
100	- The Stop-hook "Yes, highlights only" path picks 2-5 bullets across the
101	  whole transition; bullets that are themselves short and UX-focused give
102	  it the right material to work with.
103
104	**BTS rule (anything off the UX path):**
105	- Repo-management changes (Cargo manifest tweaks unless they change runtime
106	  behavior, CI config, .gitignore, planning docs).
107	- Deploy-sequence changes (DEPLOY.ps1, DEPLOY.md, marketplace cache logic)
108	  unless the user's deploy workflow changes.
109	- Commit reverts where the reverted code never reached the user's machine.
110	- Doc-string / comment drift fixes.
111	- Test-only additions or framework changes.
112	- Internal refactors with no observable behavior change.
113	- Naming, struct-field, or fn-signature changes inside `src/`.
114
115	**When in doubt, ask: "Would a user typing `/spt:live` notice the
116	difference?"** If no, it's BTS.
117
118	**Examples of correct bucketing** (from real prior bullets, reframed):
119
120	- v1.10.20 "version-change owl message routed via TCP-first deliver_body" →
121	  `### Fixed`: "Version-change announcements arrive in-line with other
122	  high-priority messages instead of waiting for the next poll tick."
123	  (The file-path / fn-name detail moves to `### BTS` as: "Routed
124	  `version_changelog` payload through `deliver_body` like every other owl
125	  message.")
126	- v1.10.20 "WORKING_PERCH_NOTICE TCP-first flip reverted" — no user-visible
127	  symptom changed, so it belongs entirely in `### BTS`, not `### Reverted`.
128	- v1.10.18 the entire "260520-r2f commune-reference write-path fix" — the
129	  user-visible bullet is `### Fixed`: "Fresh `/spt:live` start now delivers
130	  first-commune context to the live agent reliably." Every file/line
131	  citation in the existing entry moves to `### BTS`.
132	- v1.10.17 chunking work — `### Fixed`: "Long poll-listener events no
133	  longer get truncated by the Claude Code Monitor at ~500 characters."
134	  Internal const names and chunk-boundary detail → `### BTS`.
135
136	**Backward compatibility:** Pre-v1.10.21 entries that pre-date this
137	discipline are left as-is. The hook's "skip `### BTS`" filter is a no-op
138	on them (they have no `### BTS` section) and the full-changelog path will
139	surface them verbatim per the existing behavior. The new discipline
140	applies prospectively starting with the next bump.
141
142	### Parameters
143
144	| Parameter | Default | Purpose |
145	|-----------|---------|---------|
146	| `-RepoRoot` | script's parent-of-parent dir | Path to the spt repo root |
147	| `-Marketplace` | `~/.claude/plugins/marketplaces/cplugs` | Local cplugs marketplace clone |
148	| `-PluginDir` | `~/.claude/plugins/spt` | Legacy manual-install dir. Removed by step 6 if present as a regular dir (it would otherwise conflict with the marketplace install). Symlinks at this path are preserved. |
149	| `-Bump` | _(unset)_ | Increment semver: `patch`/`minor`/`major`. Rewrites `plugin.json` and commits in the spt repo (no push). Aborts if `plugin.json` has uncommitted changes. |
150	| `-SkipPush` | off | Commit marketplace locally, don't push. Does not affect the `-Bump` commit (never pushed). |
151	| `-SkipMarketplaceCommit` | off | Skip marketplace commit+push entirely |
152	| `-DryRun` | off | Print every step with `[DRY-RUN]` prefix; no mutations |
153
154	### Example: dev loop without pushing
155
156	```powershell
157	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -SkipPush
158	```
159
160	### Example: dry-run to preview
161
162	```powershell
163	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -DryRun
164	```
165
166	### Example: bump patch + full deploy
167
168	```powershell
169	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
170	```
171
172	### Troubleshooting: plugin shows as "not installed" in `/plugin`
173
174	**Symptom:** `/plugin` reports `spt` as not installed (and refuses to update), even though `installed_plugins.json` has `spt@cplugs` and the plugin clearly works. Commands like `/spt:*` may still function via the marketplace install while the UI misreports state.
175
176	**Cause:** A legacy manual-install directory at `~/.claude/plugins/spt/` (with its own `.claude-plugin/plugin.json`) living alongside the marketplace install at `~/.claude/plugins/cache/cplugs/spt/<version>/`. Claude Code enumerates both, and the unmanaged one — not tracked in `installed_plugins.json` — causes the conflict. Usually a leftover from a pre-marketplace deploy or from running the old "Deploy as Plugin (manual)" flow on top of a marketplace install.
177
178	**Fix:**
179
180	```bash
181	rm -rf ~/.claude/plugins/spt/
182	```
183
184	Then restart Claude Code (or run `/reload-plugins`). DEPLOY.ps1 step 6 now auto-cleans this directory on every marketplace deploy, so re-running the deploy script also resolves it.
185
186	> The manual steps documented in the sections below remain the authoritative spec. The script automates them; if the two ever diverge, the manual steps are correct and the script must be updated to match.
187
188	## Files Overview
189
190	| File | Purpose |
191	|------|---------|
192	| `owl.exe` | Native binary (compiled from `src/`) -- handles all owl and live commands |
193	| `plugin/spt/` | Plugin directory (deployed via marketplace or manually) |
194	| `plugin/spt/.claude-plugin/plugin.json` | Plugin manifest (name: spt) |
195	| `plugin/spt/skills/*/SKILL.md` | Per-command skill files (one directory per /spt: command) |
196	| `plugin/spt/skills/commune/commune.md` | Commune protocol for Self agents |
197	| `plugin/spt/hooks/hooks.json` | SessionStart + SessionEnd hook registrations |
198	| `psyche.md` | Psyche agent prompt template (embedded in binary via `include_str!`) |
199
200	## Deploy via Marketplace (recommended)
201
202	The cplugs private marketplace is the primary way to install spt. It bundles skill files, hooks, and the native binary.
203
204	### Marketplace Setup (one-time)
205
206	Register the cplugs marketplace if not already registered:
207
208	```bash
209	# Clone the marketplace repo
210	git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
211
212	# Register in ~/.claude/plugins/known_marketplaces.json
213	# Add a "cplugs" entry with source: { source: "github", repo: "SaberMage/cplugs" }
214	```
215
216	### Install from Marketplace
217
218	```
219	/plugin install spt@cplugs
220	```
221
222	### Update Marketplace Plugin Files
223
224	When skill files, hooks, or the binary change in this repo:
225
226	1. **Bump the version** in `plugin/spt/.claude-plugin/plugin.json` (e.g., `1.5.0` → `1.5.1`). The plugin manager compares this version to the cached copy — if they match, it skips the update entirely.
227	2. Build: `cargo build --release`
228	3. **Copy to marketplace** — binary, skills, hooks, and plugin manifest:
229	   ```bash
230	   MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/spt
231	   cp target/release/owl.exe "$MARKET/"
232	   cp -r plugin/spt/skills/* "$MARKET/skills/"
233	   cp -r plugin/spt/hooks/* "$MARKET/hooks/"
234	   cp plugin/spt/.claude-plugin/plugin.json "$MARKET/.claude-plugin/"
235	   ```
236	4. **Commit and push** the marketplace repo:
237	   ```bash
238	   cd ~/.claude/plugins/marketplaces/cplugs
239	   git add plugins/spt/ && git commit -m "spt: <describe change>" && git push
240	   ```
241	5. **Sync the new version into the cache, then (optionally) targeted-prune older versions.** `/plugin install` may not refresh the cache if the plugin is already enabled, so the new version directory must be created explicitly. **Do NOT stop live agents first** — Phase 18.4/18.5 shipped seamless binary handoff, and the handoff fires when `installed_plugins.json` flips in step 6 below. Old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json`. Killing `owl.exe` aborts that handoff.
242
243	   **Do NOT wipe the entire cache tree.** Running `owl.exe` processes are exec'ing from the PREVIOUS version directory until they migrate. `rm -rf ~/.claude/plugins/cache/cplugs/spt` yanks the `.exe` out from under them mid-handoff. At minimum, keep the PREVIOUS version dir on disk.
244
245	   **Sync all files** (not just the ones you changed) — partial syncs cause subtle mismatches between the binary and skill definitions:
246
247	   ```bash
248	   # Read the version from the plugin manifest — this is the authoritative version number
249	   VERSION=$(node -p "require('./plugin/spt/.claude-plugin/plugin.json').version")
250
251	   # Sync the new version into the cache (do NOT rm -rf the whole spt/ cache tree).
252	   CACHE=~/.claude/plugins/cache/cplugs/spt/$VERSION
253	   mkdir -p "$CACHE/.claude-plugin" "$CACHE/skills" "$CACHE/hooks"
254	   cp target/release/owl.exe "$CACHE/"
255	   cp -r plugin/spt/skills/* "$CACHE/skills/"
256	   cp -r plugin/spt/hooks/* "$CACHE/hooks/"
257	   cp plugin/spt/.claude-plugin/plugin.json "$CACHE/.claude-plugin/"
258
259	   # (Optional cleanup) After the next session start confirms no owl.exe has the
260	   # old .exe mapped, you can remove stale older-version dirs individually:
261	   #   rm -rf ~/.claude/plugins/cache/cplugs/spt/<old-version>
262	   # Keep the PREVIOUS version and the current $VERSION in place. PowerShell
263	   # users should prefer docs/DEPLOY.ps1, which handles this keep-set logic
264	   # automatically and tolerates sharing-violation residue.
265	   ```
266	6. **Refresh plugin state via the Claude Code CLI.** After the manual cache sync in step 5, `installed_plugins.json` still holds the `gitCommitSha` from the previous deploy; on the next session start Claude Code sees a mismatch against cplugs marketplace HEAD, writes `.orphaned_at` into the freshly-deployed cache dir, and refuses to source the SessionStart hook's `$OWL`/`$LIVE` exports — every `/spt:*` skill silently breaks. This is also the trigger for the seamless handoff: flipping `installPath` / `version` in `installed_plugins.json` is what old `owl.exe` listeners detect to initiate their self-migration to the new cache dir. See `.planning/debug/spt-env-vars-empty.md` for the full env-var evidence trail.
267
268	   The one-liner — canonical path, handles `installed_plugins.json` rewrite and orphan-marker cleanup atomically:
269
270	   ```bash
271	   claude plugin install spt@cplugs
272	   ```
273
274	   Equivalent to `/plugin install spt@cplugs` from inside Claude Code. Works identically from a script or CI. Restart the Claude Code session afterwards so the un-orphaned plugin's SessionStart hook runs cleanly and `$OWL`/`$LIVE` populate in Bash subprocesses.
275
276	   **Do not hand-patch `installed_plugins.json`.** Earlier revisions of this doc showed `jq` / `sed` fallbacks; they're brittle (an older DEPLOY.ps1 version crashed on a PowerShell `JavaScriptSerializer` circular-reference error here) and Claude Code may silently reject the result. If the CLI is unavailable, install it — don't edit the manifest by hand.
277	7. Run `/reload-plugins` in Claude Code to pick up skill file changes
278
279	### Enable the Plugin
280
281	Add to `~/.claude/settings.json`:
282
283	```json
284	{
285	  "enabledPlugins": {
286	    "spt@cplugs": true
287	  }
288	}
289	```
290
291	## Build
292
293	```bash
294	cargo build --release
295	```
296
297	The binary is at `target/release/owl.exe` (Windows) or `target/release/owl` (Linux/macOS). It handles both owl and live commands -- no separate binaries needed.
298
299	## Deploy as Plugin (manual, without marketplace)
300
301	> **Warning — do not combine with the marketplace install.** If you have `spt@cplugs` in `installed_plugins.json` (i.e. you installed via `/plugin install spt@cplugs`), do **not** also create `~/.claude/plugins/spt/`. Claude Code will enumerate both and report the marketplace install as **not installed** in `/plugin`, refusing to update it. The two flows are exclusive. See "Troubleshooting: plugin shows as 'not installed'" above.
302
303	If you don't have marketplace access, you can deploy the plugin manually. This replaces the old `~/.claude/skills/owl/` and `~/.claude/skills/live/` directories.
304
305	```bash
306	# Build
307	cargo build --release
308
309	# Deploy plugin directory
310	mkdir -p ~/.claude/plugins/spt
311	cp -r plugin/spt/.claude-plugin ~/.claude/plugins/spt/
312	cp -r plugin/spt/skills ~/.claude/plugins/spt/
313	cp -r plugin/spt/hooks ~/.claude/plugins/spt/
314	# Deploy binary into plugin directory
315	# IMPORTANT: hooks.json uses 'owl.exe' as the canonical binary name on ALL platforms.
316	# Windows:
317	cp target/release/owl.exe ~/.claude/plugins/spt/
318	# Linux/macOS -- copy as owl.exe so hooks.json works without platform detection:
319	cp target/release/owl ~/.claude/plugins/spt/owl.exe
320
321	# Remove old skill directories (clean break per D-08)
322	rm -rf ~/.claude/skills/owl
323	rm -rf ~/.claude/skills/live
324	```
325
326	**Cross-platform note:** The plugin's `hooks.json` references `$CLAUDE_PLUGIN_ROOT/owl.exe` for all hooks (`CLAUDE_PLUGIN_ROOT` is auto-provided by Claude Code's plugin system, pointing to the active plugin version directory). Using `owl.exe` as the canonical binary name on all platforms avoids needing platform-detection logic. On Unix, the binary is simply copied/renamed to `owl.exe` during deployment. Unix has no restriction on `.exe` extensions -- the file runs fine.
327
328	After deploying:
329
330	- The plugin's SessionStart hook auto-injects `$OWL` and `$LIVE` env vars via CLAUDE_ENV_FILE
331	- The plugin's SessionEnd hook runs cleanup-session
332	- No manual settings.json editing needed
333	- `$OWL` and `$LIVE` are set dynamically via CLAUDE_ENV_FILE on each session start, always pointing to the current plugin version's binary
334
335	## Remove Old Skill Directories
336
337	When switching to the plugin (via marketplace or manual), you **must** remove the old skill directories to avoid confusion:
338
339	```bash
340	# Required: remove old skill directories
341	rm -rf ~/.claude/skills/owl
342	rm -rf ~/.claude/skills/live
343	```
344
345	The plugin replaces both old skill directories. Old `/owl` and `/live` skill commands will no longer work. All commands are now under `/spt:` (e.g., `/spt:ready`, `/spt:live`). The binary CLI (`$OWL`, `$LIVE`) is unchanged.
346
347	## Env Var Setup
348
349	The plugin's SessionStart hook automatically writes `$OWL` and `$LIVE` env vars via `CLAUDE_ENV_FILE` on each session start. These point to the current plugin version's binary — no manual configuration needed.
350
351	**If env vars are missing** (e.g., `$OWL: command not found`), restart the Claude Code session — the SessionStart hook will re-run and populate `$OWL` / `$LIVE` from the current active plugin version via `CLAUDE_ENV_FILE`. There is no in-session force-write command; that used to exist as `/spt:env-setup` but was removed because its `settings.json` fallback pinned binary paths and broke upgrades. If restarting doesn't fix it, run `$OWL doctor` to diagnose — something is wrong with the plugin install or the hook.
352
353	**Stale env vars in settings.json:** If you have `OWL`/`LIVE` entries in `~/.claude/settings.json` from a pre-plugin install, remove them. They pin to a specific version and will override the plugin's dynamic env var injection, causing hooks to call the wrong binary after upgrades.
354
355	## Legacy / Standalone Deployment (removed)
356
357	Standalone deployment via `~/.claude/skills/owl/` and `~/.claude/skills/live/` is no longer supported. The root-level `SKILL.md`, `LIVE-SKILL.md`, and `commune.md` files have been removed. All skill definitions live under `plugin/spt/skills/`. Use the plugin system (marketplace or manual) for deployment.
358
359	## Development Workflow
360
361	For plugin development, use `--plugin-dir` to test the plugin from the repo without deploying:
362
363	```bash
364	# Build first
365	cargo build --release
366
367	# Copy binary into plugin dir
368	# Windows:
369	cp target/release/owl.exe plugin/spt/
370	# Linux/macOS:
371	cp target/release/owl plugin/spt/owl.exe
372
373	# Run Claude with the local plugin directory
374	claude --plugin-dir ./plugin/spt
375
376	# Or symlink for persistent development:
377	# ln -s $(pwd)/plugin/spt ~/.claude/plugins/spt
378	```
379
380	Note: When using `--plugin-dir`, the binary must already be built and present in the plugin directory as `owl.exe`.
381
382	## Bundle for Sharing
383
384	```bash
385	cargo build --release
386	# Windows:
387	cp target/release/owl.exe plugin/spt/
388	# Linux/macOS:
389	cp target/release/owl plugin/spt/owl.exe
390	tar czf spt-plugin.tar.gz -C plugin spt/
391	```
392
393	Install from bundle:
394
395	```bash
396	mkdir -p ~/.claude/plugins
397	tar xzf spt-plugin.tar.gz -C ~/.claude/plugins/
398	rm -rf ~/.claude/skills/owl ~/.claude/skills/live
399	```
400
401	Then start a new Claude Code session. The plugin's SessionStart hook will auto-inject `$OWL` and `$LIVE`.
402
403	## Commit Changes
404
405	```bash
406	git add plugin/ src/ psyche.md docs/ Cargo.toml
407	git commit -m "owl: <describe change>"
408	```
409

# Deploying Spacetime (spt) Plugin

## Automated Deploy (DEPLOY.ps1)

For the common case — binary + skills + hooks + marketplace + cache refresh for the version already set in `plugin/spt/.claude-plugin/plugin.json` — use the PowerShell script:

```powershell
# From repo root, after bumping plugin.json version:
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1

# Or let the script bump the version for you:
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
```

The script automates the build-through-cache-sync sequence and the state refresh (by shelling out to `claude plugin install spt@cplugs`). It **stops before `/reload-plugins`** — you must run that yourself inside Claude Code.

### What it does (in order)

1. Reads the authoritative version from `plugin/spt/.claude-plugin/plugin.json`. If `-Bump <patch|minor|major>` is passed, runs the bump sub-sequence: (a) abort gate — refuses to proceed if `CHANGELOG.md` is missing OR still contains a `TODO: changelog entry` marker from a prior bump; (b) rewrites `plugin.json` + `Cargo.toml` to the new version; (c) appends a `TODO: changelog entry` stub H2 to `CHANGELOG.md` for the new version; (d) stages `plugin.json` + `Cargo.toml` + `CHANGELOG.md` and commits as `chore: bump spt plugin to vX.Y.Z` in the spt repo (no push). The bump commit ships with the TODO stub; the user is expected to author a curated `CHANGELOG.md` entry for the new version in a follow-up commit before the next `-Bump`. See "Two-phase changelog authoring" below.
2. `cargo build --release` in the repo root.
3. Syncs `owl.exe`, `skills/`, `hooks/`, and `plugin.json` to the cplugs marketplace clone.
4. Commits + pushes the marketplace (unless skipped).
5. Syncs the new version into `~/.claude/plugins/cache/cplugs/spt/<Version>/`, then targeted-prunes older semver directories. The script keeps the NEW version dir, the PREVIOUS version dir (running `owl.exe` processes are still exec'ing from there until they self-migrate via handoff), and the `installPath` leaf dir recorded in `installed_plugins.json` (captured before step 7 rewrites it). Non-semver directories and directories in the keep-set are never touched. Sharing-violation residue (a dir that can't be removed because `owl.exe` still has the `.exe` mapped) is logged as a warning and retried on the next deploy — it does **not** fail the deploy. First-deploy case (no prior `spt@cplugs` entry) skips the prune entirely.
6. Cleans up the legacy manual-install dir at `~/.claude/plugins/spt/` if it exists as a regular directory. A manual install coexisting with the marketplace install makes Claude Code report the plugin as **not installed** in `/plugin` (see Troubleshooting below). Symlinked dirs are preserved for the dev-symlink workflow.
7. Runs `claude plugin install spt@cplugs` for cache-warm-up and `.orphaned_at` marker cleanup. **This is no longer the version-pointer flip** — the CLI is a no-op when `spt@cplugs` is already installed at any version (it only touches `lastUpdated`, not `version` / `installPath` / `gitCommitSha`). That silent no-op was the failure mode in the 1.9.8 → 1.9.9 deploy that left the pointer pinned to 1.9.8 (see `.planning/debug/26-06-live-skill-regression.md`). If `claude` is not in `PATH`, the script warns and tells you to run the command manually.
8. **Explicitly patches `~/.claude/plugins/installed_plugins.json`** to set the `spt@cplugs` entry's `version`, `installPath`, `gitCommitSha`, and `lastUpdated`. Atomic write via temp file + `Move-Item -Force`. This is the authoritative pointer flip — the step 7 CLI call is kept only for its cache-warm-up side effects. Uses `ConvertFrom-Json` / `ConvertTo-Json -Depth 10` (no `JavaScriptSerializer` — its earlier hand-rolled patch was removed for circular-reference crashes; the new patch round-trips through the PSObject graph safely).
9. **Re-reads `installed_plugins.json` and asserts `spt@cplugs.version == <Version>`** (and `installPath` matches the new cache dir). Any mismatch writes `ASSERTION FAILED: ...` to stderr and exits the script with status 1; the green "Deploy complete" banner never prints on drift.
10. Prints a reminder to run `/reload-plugins` inside Claude Code.

> The `Write-Step "Step N: ..."` labels in the script body still use their historical numbers (Step 3 for build, Step 4 for marketplace sync, etc. — Step 2 is intentionally absent) so log-grep tooling keeps matching prior runs. The numbered list above is the user-facing sequence; the in-script labels are the log contract.

### What it does NOT do

- Bump the plugin version in `plugin.json` unless `-Bump <patch|minor|major>` is passed.
- Push the spt repo. Even with `-Bump`, the bump commit lands locally and you push it yourself.
- Commit other changes in the spt repo (that's your job).
- Edit `~/.claude/settings.json`.
- Run `/reload-plugins` (that's a Claude Code slash command).
- Kill running `owl.exe` processes. Phase 18.4/18.5 shipped seamless binary handoff — old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json` once the new version is staged and `installed_plugins.json` flips (step 7 above). Killing them aborts the handoff and strands their session state.
- Wipe the entire cache tree. The old "blanket `rm -rf` on `cache/cplugs/spt`" behavior was correct before handoff shipped, but now it yanks the `.exe` out from under running processes mid-migration. The new targeted prune (step 5 above) keeps the PREVIOUS version dir on disk so in-flight handoffs complete.

### Two-phase changelog authoring (Phase 34 D-08 round 2)

Under the locked D-08 round 2 design (realigned in quick-260517-n4b post-UAT clarification), `-Bump` enforces a curation gate so each version-change Stop-hook surface ships with a real CHANGELOG entry inside the deployed bundle. The first `-Bump` for any new version aborts and seeds a TODO stub; the second `-Bump` (after the user fills it in and commits) proceeds.

**First `-Bump` run (no curated entry yet):**

1. **Abort gate** — refuses to proceed if `CHANGELOG.md` is missing OR contains a `TODO: changelog entry` marker from a prior bump.
2. **Curation gate** — checks for `## [<NewVersion>]` H2 header in `CHANGELOG.md`:
   - **Absent:** appends `## [<NewVersion>] - YYYY-MM-DD\n\n- TODO: changelog entry` to the working tree (uncommitted), then **EXIT 1** with instructions to fill and commit, then re-run. The current deploy does **not** succeed; no `plugin.json` / `Cargo.toml` mutation occurs.
   - **Present + TODO marker:** caught by abort gate above → EXIT 1.
   - **Present + curated body:** proceed past the gate.

**User action between runs:** Edit `CHANGELOG.md`, replace `- TODO: changelog entry` under the newly-stubbed `## [<NewVersion>]` H2 with real bullets in Keep-a-Changelog 1.1.0 categories (Added / Changed / Fixed / Removed / Deprecated / Security). Commit as e.g. `docs(NN): fill <NewVersion> CHANGELOG entry`.

**Second `-Bump` run (curated entry committed):**

1. Abort gate passes — no `TODO: changelog entry` marker remains.
2. Curation gate passes — `## [<NewVersion>]` H2 present with curated body.
3. Rewrite `plugin/spt/.claude-plugin/plugin.json` + `Cargo.toml` to the new version.
4. Stage + commit `plugin.json` + `Cargo.toml` (two files) as `chore: bump spt plugin to vX.Y.Z`. CHANGELOG.md is **not** in the bump commit — it landed in the prior curated-entry commit.
5. Build, sync to marketplace + cache (including curated `CHANGELOG.md`), refresh `installed_plugins.json`.

**Pre-authoring shortcut:** A caller who knows the next semver upfront can pre-author and commit the `## [<NewVersion>]` H2 with curated body before running `-Bump`. The curation gate sees the H2 present + curated and proceeds in one shot.

**Backstop:** If a stub somehow slips into a bump commit (manual flow, prior implementation), the next `-Bump`'s abort gate fires on the `TODO: changelog entry` marker and forces the user to curate before any further deploy.

### Bucket discipline for stub fill-in (quick-260520-uc2)

The version-change Stop hook surfaces curated CHANGELOG bullets to the user
on the next session boundary after a plugin upgrade. The user sees only the
release-facing buckets — implementation noise is filtered out at hook time.
The deploying agent (human or LLM) filling the `- TODO: changelog entry`
stub MUST follow these rules:

**Section vocabulary per H2 (`## [vX.Y.Z] - YYYY-MM-DD`):**

| Section | Use for | Surfaced to user? |
|---------|---------|---------------------|
| `### Added` | New user-facing capability (a flag, command, message, prompt, hook trigger they will notice) | YES |
| `### Changed` | Modified user-facing behavior (different message, different default, different timing) | YES |
| `### Fixed` | Bug fix the user actually observed or could observe | YES |
| `### Removed` | A user-facing capability that no longer exists | YES |
| `### Deprecated` | A capability the user should stop relying on | YES |
| `### Security` | Anything with user-relevant security impact | YES |
| `### Reverted` | A user-visible behavior was rolled back to a prior shape | YES |
| `### BTS` | Anything else: internal refactors, repo plumbing, deploy script changes, doc-string drift, test-only changes, code reverts that don't change user-visible behavior, commit SHAs, file-path callouts | **NO** — filtered out at hook time |

**UX-language rule for Added / Changed / Fixed (and the other surfaced
buckets):**
- Phrase as what the user sees, does, or runs. "`/spt:live` now offers an
  Other option" — not "added `AuqOption::Other` to `live::auto_pick`".
- Do NOT mention: code paths (`src/...`), function or struct names, prompt
  XML element names (`<spt-foo>`), commit SHAs, planning doc references
  (`Phase 34 D-08`, `quick-260517-n4b`), Rust types, file paths inside
  `plugin/spt/`, internal env-var names that are not part of the
  documented user surface.
- Keep bullets terse. One sentence each is the target; two short sentences
  is the ceiling. If you need more, you are probably writing BTS content.
- The Stop-hook "Yes, highlights only" path picks 2-5 bullets across the
  whole transition; bullets that are themselves short and UX-focused give
  it the right material to work with.

**BTS rule (anything off the UX path):**
- Repo-management changes (Cargo manifest tweaks unless they change runtime
  behavior, CI config, .gitignore, planning docs).
- Deploy-sequence changes (DEPLOY.ps1, DEPLOY.md, marketplace cache logic)
  unless the user's deploy workflow changes.
- Commit reverts where the reverted code never reached the user's machine.
- Doc-string / comment drift fixes.
- Test-only additions or framework changes.
- Internal refactors with no observable behavior change.
- Naming, struct-field, or fn-signature changes inside `src/`.

**When in doubt, ask: "Would a user typing `/spt:live` notice the
difference?"** If no, it's BTS.

**Examples of correct bucketing** (from real prior bullets, reframed):

- v1.10.20 "version-change owl message routed via TCP-first deliver_body" →
  `### Fixed`: "Version-change announcements arrive in-line with other
  high-priority messages instead of waiting for the next poll tick."
  (The file-path / fn-name detail moves to `### BTS` as: "Routed
  `version_changelog` payload through `deliver_body` like every other owl
  message.")
- v1.10.20 "WORKING_PERCH_NOTICE TCP-first flip reverted" — no user-visible
  symptom changed, so it belongs entirely in `### BTS`, not `### Reverted`.
- v1.10.18 the entire "260520-r2f commune-reference write-path fix" — the
  user-visible bullet is `### Fixed`: "Fresh `/spt:live` start now delivers
  first-commune context to the live agent reliably." Every file/line
  citation in the existing entry moves to `### BTS`.
- v1.10.17 chunking work — `### Fixed`: "Long poll-listener events no
  longer get truncated by the Claude Code Monitor at ~500 characters."
  Internal const names and chunk-boundary detail → `### BTS`.

**Backward compatibility:** Pre-v1.10.21 entries that pre-date this
discipline are left as-is. The hook's "skip `### BTS`" filter is a no-op
on them (they have no `### BTS` section) and the full-changelog path will
surface them verbatim per the existing behavior. The new discipline
applies prospectively starting with the next bump.

### Parameters

| Parameter | Default | Purpose |
|-----------|---------|---------|
| `-RepoRoot` | script's parent-of-parent dir | Path to the spt repo root |
| `-Marketplace` | `~/.claude/plugins/marketplaces/cplugs` | Local cplugs marketplace clone |
| `-PluginDir` | `~/.claude/plugins/spt` | Legacy manual-install dir. Removed by step 6 if present as a regular dir (it would otherwise conflict with the marketplace install). Symlinks at this path are preserved. |
| `-Bump` | _(unset)_ | Increment semver: `patch`/`minor`/`major`. Rewrites `plugin.json` and commits in the spt repo (no push). Aborts if `plugin.json` has uncommitted changes. |
| `-SkipPush` | off | Commit marketplace locally, don't push. Does not affect the `-Bump` commit (never pushed). |
| `-SkipMarketplaceCommit` | off | Skip marketplace commit+push entirely |
| `-DryRun` | off | Print every step with `[DRY-RUN]` prefix; no mutations |

### Example: dev loop without pushing

```powershell
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -SkipPush
```

### Example: dry-run to preview

```powershell
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -DryRun
```

### Example: bump patch + full deploy

```powershell
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
```

### Troubleshooting: plugin shows as "not installed" in `/plugin`

**Symptom:** `/plugin` reports `spt` as not installed (and refuses to update), even though `installed_plugins.json` has `spt@cplugs` and the plugin clearly works. Commands like `/spt:*` may still function via the marketplace install while the UI misreports state.

**Cause:** A legacy manual-install directory at `~/.claude/plugins/spt/` (with its own `.claude-plugin/plugin.json`) living alongside the marketplace install at `~/.claude/plugins/cache/cplugs/spt/<version>/`. Claude Code enumerates both, and the unmanaged one — not tracked in `installed_plugins.json` — causes the conflict. Usually a leftover from a pre-marketplace deploy or from running the old "Deploy as Plugin (manual)" flow on top of a marketplace install.

**Fix:**

```bash
rm -rf ~/.claude/plugins/spt/
```

Then restart Claude Code (or run `/reload-plugins`). DEPLOY.ps1 step 6 now auto-cleans this directory on every marketplace deploy, so re-running the deploy script also resolves it.

> The manual steps documented in the sections below remain the authoritative spec. The script automates them; if the two ever diverge, the manual steps are correct and the script must be updated to match.

## Files Overview

| File | Purpose |
|------|---------|
| `owl.exe` | Native binary (compiled from `src/`) -- handles all owl and live commands |
| `plugin/spt/` | Plugin directory (deployed via marketplace or manually) |
| `plugin/spt/.claude-plugin/plugin.json` | Plugin manifest (name: spt) |
| `plugin/spt/skills/*/SKILL.md` | Per-command skill files (one directory per /spt: command) |
| `plugin/spt/skills/commune/commune.md` | Commune protocol for Self agents |
| `plugin/spt/hooks/hooks.json` | SessionStart + SessionEnd hook registrations |
| `psyche.md` | Psyche agent prompt template (embedded in binary via `include_str!`) |

## Deploy via Marketplace (recommended)

The cplugs private marketplace is the primary way to install spt. It bundles skill files, hooks, and the native binary.

### Marketplace Setup (one-time)

Register the cplugs marketplace if not already registered:

```bash
# Clone the marketplace repo
git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs

# Register in ~/.claude/plugins/known_marketplaces.json
# Add a "cplugs" entry with source: { source: "github", repo: "SaberMage/cplugs" }
```

### Install from Marketplace

```
/plugin install spt@cplugs
```

### Update Marketplace Plugin Files

When skill files, hooks, or the binary change in this repo:

1. **Bump the version** in `plugin/spt/.claude-plugin/plugin.json` (e.g., `1.5.0` → `1.5.1`). The plugin manager compares this version to the cached copy — if they match, it skips the update entirely.
2. Build: `cargo build --release`
3. **Copy to marketplace** — binary, skills, hooks, and plugin manifest:
   ```bash
   MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/spt
   cp target/release/owl.exe "$MARKET/"
   cp -r plugin/spt/skills/* "$MARKET/skills/"
   cp -r plugin/spt/hooks/* "$MARKET/hooks/"
   cp plugin/spt/.claude-plugin/plugin.json "$MARKET/.claude-plugin/"
   ```
4. **Commit and push** the marketplace repo:
   ```bash
   cd ~/.claude/plugins/marketplaces/cplugs
   git add plugins/spt/ && git commit -m "spt: <describe change>" && git push
   ```
5. **Sync the new version into the cache, then (optionally) targeted-prune older versions.** `/plugin install` may not refresh the cache if the plugin is already enabled, so the new version directory must be created explicitly. **Do NOT stop live agents first** — Phase 18.4/18.5 shipped seamless binary handoff, and the handoff fires when `installed_plugins.json` flips in step 6 below. Old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json`. Killing `owl.exe` aborts that handoff.

   **Do NOT wipe the entire cache tree.** Running `owl.exe` processes are exec'ing from the PREVIOUS version directory until they migrate. `rm -rf ~/.claude/plugins/cache/cplugs/spt` yanks the `.exe` out from under them mid-handoff. At minimum, keep the PREVIOUS version dir on disk.

   **Sync all files** (not just the ones you changed) — partial syncs cause subtle mismatches between the binary and skill definitions:

   ```bash
   # Read the version from the plugin manifest — this is the authoritative version number
   VERSION=$(node -p "require('./plugin/spt/.claude-plugin/plugin.json').version")

   # Sync the new version into the cache (do NOT rm -rf the whole spt/ cache tree).
   CACHE=~/.claude/plugins/cache/cplugs/spt/$VERSION
   mkdir -p "$CACHE/.claude-plugin" "$CACHE/skills" "$CACHE/hooks"
   cp target/release/owl.exe "$CACHE/"
   cp -r plugin/spt/skills/* "$CACHE/skills/"
   cp -r plugin/spt/hooks/* "$CACHE/hooks/"
   cp plugin/spt/.claude-plugin/plugin.json "$CACHE/.claude-plugin/"

   # (Optional cleanup) After the next session start confirms no owl.exe has the
   # old .exe mapped, you can remove stale older-version dirs individually:
   #   rm -rf ~/.claude/plugins/cache/cplugs/spt/<old-version>
   # Keep the PREVIOUS version and the current $VERSION in place. PowerShell
   # users should prefer docs/DEPLOY.ps1, which handles this keep-set logic
   # automatically and tolerates sharing-violation residue.
   ```
6. **Refresh plugin state via the Claude Code CLI.** After the manual cache sync in step 5, `installed_plugins.json` still holds the `gitCommitSha` from the previous deploy; on the next session start Claude Code sees a mismatch against cplugs marketplace HEAD, writes `.orphaned_at` into the freshly-deployed cache dir, and refuses to source the SessionStart hook's `$OWL`/`$LIVE` exports — every `/spt:*` skill silently breaks. This is also the trigger for the seamless handoff: flipping `installPath` / `version` in `installed_plugins.json` is what old `owl.exe` listeners detect to initiate their self-migration to the new cache dir. See `.planning/debug/spt-env-vars-empty.md` for the full env-var evidence trail.

   The one-liner — canonical path, handles `installed_plugins.json` rewrite and orphan-marker cleanup atomically:

   ```bash
   claude plugin install spt@cplugs
   ```

   Equivalent to `/plugin install spt@cplugs` from inside Claude Code. Works identically from a script or CI. Restart the Claude Code session afterwards so the un-orphaned plugin's SessionStart hook runs cleanly and `$OWL`/`$LIVE` populate in Bash subprocesses.

   **Do not hand-patch `installed_plugins.json`.** Earlier revisions of this doc showed `jq` / `sed` fallbacks; they're brittle (an older DEPLOY.ps1 version crashed on a PowerShell `JavaScriptSerializer` circular-reference error here) and Claude Code may silently reject the result. If the CLI is unavailable, install it — don't edit the manifest by hand.
7. Run `/reload-plugins` in Claude Code to pick up skill file changes

### Enable the Plugin

Add to `~/.claude/settings.json`:

```json
{
  "enabledPlugins": {
    "spt@cplugs": true
  }
}
```

## Build

```bash
cargo build --release
```

The binary is at `target/release/owl.exe` (Windows) or `target/release/owl` (Linux/macOS). It handles both owl and live commands -- no separate binaries needed.

## Deploy as Plugin (manual, without marketplace)

> **Warning — do not combine with the marketplace install.** If you have `spt@cplugs` in `installed_plugins.json` (i.e. you installed via `/plugin install spt@cplugs`), do **not** also create `~/.claude/plugins/spt/`. Claude Code will enumerate both and report the marketplace install as **not installed** in `/plugin`, refusing to update it. The two flows are exclusive. See "Troubleshooting: plugin shows as 'not installed'" above.

If you don't have marketplace access, you can deploy the plugin manually. This replaces the old `~/.claude/skills/owl/` and `~/.claude/skills/live/` directories.

```bash
# Build
cargo build --release

# Deploy plugin directory
mkdir -p ~/.claude/plugins/spt
cp -r plugin/spt/.claude-plugin ~/.claude/plugins/spt/
cp -r plugin/spt/skills ~/.claude/plugins/spt/
cp -r plugin/spt/hooks ~/.claude/plugins/spt/
# Deploy binary into plugin directory
# IMPORTANT: hooks.json uses 'owl.exe' as the canonical binary name on ALL platforms.
# Windows:
cp target/release/owl.exe ~/.claude/plugins/spt/
# Linux/macOS -- copy as owl.exe so hooks.json works without platform detection:
cp target/release/owl ~/.claude/plugins/spt/owl.exe

# Remove old skill directories (clean break per D-08)
rm -rf ~/.claude/skills/owl
rm -rf ~/.claude/skills/live
```

**Cross-platform note:** The plugin's `hooks.json` references `$CLAUDE_PLUGIN_ROOT/owl.exe` for all hooks (`CLAUDE_PLUGIN_ROOT` is auto-provided by Claude Code's plugin system, pointing to the active plugin version directory). Using `owl.exe` as the canonical binary name on all platforms avoids needing platform-detection logic. On Unix, the binary is simply copied/renamed to `owl.exe` during deployment. Unix has no restriction on `.exe` extensions -- the file runs fine.

After deploying:

- The plugin's SessionStart hook auto-injects `$OWL` and `$LIVE` env vars via CLAUDE_ENV_FILE
- The plugin's SessionEnd hook runs cleanup-session
- No manual settings.json editing needed
- `$OWL` and `$LIVE` are set dynamically via CLAUDE_ENV_FILE on each session start, always pointing to the current plugin version's binary

## Remove Old Skill Directories

When switching to the plugin (via marketplace or manual), you **must** remove the old skill directories to avoid confusion:

```bash
# Required: remove old skill directories
rm -rf ~/.claude/skills/owl
rm -rf ~/.claude/skills/live
```

The plugin replaces both old skill directories. Old `/owl` and `/live` skill commands will no longer work. All commands are now under `/spt:` (e.g., `/spt:ready`, `/spt:live`). The binary CLI (`$OWL`, `$LIVE`) is unchanged.

## Env Var Setup

The plugin's SessionStart hook automatically writes `$OWL` and `$LIVE` env vars via `CLAUDE_ENV_FILE` on each session start. These point to the current plugin version's binary — no manual configuration needed.

**If env vars are missing** (e.g., `$OWL: command not found`), restart the Claude Code session — the SessionStart hook will re-run and populate `$OWL` / `$LIVE` from the current active plugin version via `CLAUDE_ENV_FILE`. There is no in-session force-write command; that used to exist as `/spt:env-setup` but was removed because its `settings.json` fallback pinned binary paths and broke upgrades. If restarting doesn't fix it, run `$OWL doctor` to diagnose — something is wrong with the plugin install or the hook.

**Stale env vars in settings.json:** If you have `OWL`/`LIVE` entries in `~/.claude/settings.json` from a pre-plugin install, remove them. They pin to a specific version and will override the plugin's dynamic env var injection, causing hooks to call the wrong binary after upgrades.

## Legacy / Standalone Deployment (removed)

Standalone deployment via `~/.claude/skills/owl/` and `~/.claude/skills/live/` is no longer supported. The root-level `SKILL.md`, `LIVE-SKILL.md`, and `commune.md` files have been removed. All skill definitions live under `plugin/spt/skills/`. Use the plugin system (marketplace or manual) for deployment.

## Development Workflow

For plugin development, use `--plugin-dir` to test the plugin from the repo without deploying:

```bash
# Build first
cargo build --release

# Copy binary into plugin dir
# Windows:
cp target/release/owl.exe plugin/spt/
# Linux/macOS:
cp target/release/owl plugin/spt/owl.exe

# Run Claude with the local plugin directory
claude --plugin-dir ./plugin/spt

# Or symlink for persistent development:
# ln -s $(pwd)/plugin/spt ~/.claude/plugins/spt
```

Note: When using `--plugin-dir`, the binary must already be built and present in the plugin directory as `owl.exe`.

## Bundle for Sharing

```bash
cargo build --release
# Windows:
cp target/release/owl.exe plugin/spt/
# Linux/macOS:
cp target/release/owl plugin/spt/owl.exe
tar czf spt-plugin.tar.gz -C plugin spt/
```

Install from bundle:

```bash
mkdir -p ~/.claude/plugins
tar xzf spt-plugin.tar.gz -C ~/.claude/plugins/
rm -rf ~/.claude/skills/owl ~/.claude/skills/live
```

Then start a new Claude Code session. The plugin's SessionStart hook will auto-inject `$OWL` and `$LIVE`.

## Commit Changes

```bash
git add plugin/ src/ psyche.md docs/ Cargo.toml
git commit -m "owl: <describe change>"
```


Deploy procedure = `DEPLOY.ps1 -Bump patch`, but there's a two-phase changelog curation gate. Use pre-authoring shortcut: write the new version's curated CHANGELOG H2 first, then `-Bump` passes in one shot.

Gather version + changelog state + tree cleanliness.


Bash

cd /c/Users/decid/Documents/projects/claude_skill_owl && echo "VERSION (plugin.json):" && node -p "require('./plugin/spt/.claude-plugin/plugin.json').version" && echo "VERSION (Cargo.toml):" && grep -m1 '^version' Cargo.toml && echo "---TODO markers in CHANGELOG:---" && grep -n "TODO: changelog entry" CHANGELOG.md || echo "(none)"; echo "---CHANGELOG head (first 30 lines):---" && head -30 CHANGELOG.md && echo "---git status plugin.json/Cargo.toml:---" && git status --porcelain plugin/spt/.claude-plugin/plugin.json Cargo.toml CHANGELOG.md

Read version + changelog gate state


VERSION (plugin.json):
1.11.22
VERSION (Cargo.toml):
version = "1.11.22"
---TODO markers in CHANGELOG:---
(none)
---CHANGELOG head (first 30 lines):---
# Changelog

All notable changes to the SPT (Spacetime / Sentience Pocket Transacter) plugin are documented in this file.

The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries authored retroactively from `git log --grep='chore: bump'` at Phase 34 (v1.7.1 milestone).

## [1.11.22] - 2026-05-30

### Fixed
- **Attaching a second machine no longer overwrites the other machine's agent context.** `psyche-sync-setup` used force-push semantics for agent branches, so attaching a second machine could clobber remote branch heads holding the first machine's most recent writes (a data-loss risk). Setup now reconciles before pushing — fetches the remote, rebases any diverged branches onto the remote head, and pushes per-ref with observable outcomes — so concurrent machines converge instead of overwriting each other.
- **Second-machine setup no longer fails with a non-fast-forward rejection.** Each machine used to generate its own empty-seed commit, creating a `main` divergence that setup then refused to push past. The seed bootstrap is now deterministic (every machine produces a byte-identical `main`), and pre-existing wall-clock-seeded installs are absorbed automatically through the new reconciliation path — no migration command needed.
- **`psyche-sync-setup` failures now show a readable error.** The skill's failure path printed raw Rust debug output (`GitFailed(Nonzero { stderr: "..." })`); it now prints the curated, human-readable error line so you can actually act on it.
- **Live agents in non-git host projects now back up their project context.** Starting a live agent in a directory that is not a git repository previously skipped syncing that project's tracked context (`psyches/tracked/projects/<name>/`) — the project-name resolver returned nothing outside a git repo. It now falls back to the directory's own name (guarded against ref-unsafe characters), so non-git projects get their context worktree, commit, and push like any other.

### Added
- **`$OWL doctor` surfaces partial sync setup.** When a setup pushed to the remote but failed at the final settings write (leaving sync "unset"), doctor previously reported a flat "not configured." It now probes the seed repo for a configured origin plus a reachable remote and shows a Warn row pointing you at the idempotent re-run of `/spt:psyche-sync-setup`.
- **`psyche-sync-setup` skill documents exit code 1 and a recovery path.** The exit-code list now covers code 1 (generic setup failure), and a new "Recovering from a failed setup" section walks through re-run → `$OWL doctor` → `--disable` + re-setup.

### BTS
- Phase 35.2 (v1.8, deployed in this release): `accept_flow` reconciliation — `git fetch origin` + per-branch `merge-base --is-ancestor` + diverged refs rebased via `rebase -X theirs origin/{branch}` + per-ref `push origin {branch}:{branch}` replacing `push --all` (`ReconcileVerdict`/`PerRefOutcome` types; SYNC-RECON-FETCH-01/PER-BRANCH-01/REBASE-01, SYNC-PUSH-PER-REF-01). `ensure_seed` deterministic bootstrap locks `GIT_COMMITTER_DATE`/`GIT_AUTHOR_DATE` on the cold-path `commit-tree` so seeds converge on a byte-identical `main` SHA (SYNC-BOOTSTRAP-DET-01). 3 plans, single wave.
- Phase 35.3 (v1.8): D-01 `{:?}`→`{}` flip at `psyche_sync_setup.rs` routed through the existing `SyncError`/`GitError` Display impls, locked by no-debug-syntax regression tests in `sync.rs` + `git.rs` (D-12). Issue 7 fix is a localized guarded basename fallback in `project_name_from_cwd_path` (`owlery.rs`), gated by `validate_id_chars` + psyche-dir rejection; the bug-encoding unit test was rewritten and a traversal-guard test added. Doctor partial-state probe (`check_sync_status`) reuses a new timeout-parameterized `run_git_with_timeout_dur`, detects origin via `git config --file --get remote.origin.url`, and gives `ls-remote` a 2s `SYNC_PROBE_TIMEOUT` network budget (code-review WR-03/WR-04/IN-02 follow-ups). No SyncSettings schema change (backward-compat hard constraint). 4 plans / 2 waves; verification 5/5 must-haves; code review 0 blockers.

## [1.11.21] - 2026-05-28

### Fixed
- **`$OWL list` can no longer accidentally delete perch directories.** A read command should never mutate state; before this release, `list` could `rm -rf` an orphan-classified perch dir as a side effect, occasionally taking healthy in-flight psyche perches with it. `list` is now strictly read-only.
- **`$OWL list` no longer mis-classifies in-flight psyche workers as orphans on Windows.** Windows PID recycling could make a live psyche perch's info.json look stale during a poll-rewrite race; `list` now consults wrapper liveness plus a 60-second mtime grace before flagging anything as an orphan.
- **`$OWL doctor --fix` stale-perch sweep no longer demotes healthy live agents.** Previously, the sweep removed the `ready` sentinel from a wrapper-owned psyche perch whose info.json carried a recycled PID, briefly knocking the agent offline. The sweep now skips perches whose wrapper is alive.

### Added
---git status plugin.json/Cargo.toml:---
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Version `1.11.22` → next patch `1.11.23`. No TODO markers, tree clean. Pre-author curated CHANGELOG entry first (one-shot gate pass), commit it, then `-Bump patch`.

Write changelog entry.


Edit

## [1.11.23] - 2026-06-02

### Fixed
- **A manual commune or signoff no longer triggers a redundant automatic context summary right afterward.** Sending a `/spt:commune` (or signing off) already flushes your latest context to the Psyche; previously a pending background cadence trigger could still fire a duplicate echo-commune moments later. Delivering a commune/signoff now also clears that pending trigger, so the next automatic summary waits for the normal cadence.

### BTS
- quick-260602-2ar: `process_file_drop` (`src/live/wrapper/mod.rs`) now clears the `.more-done` cadence sentinel at its two FILE-DROP consume exits (empty-body signoff skip + the shared `exit_code == 0` block covering commune / init-stale signoff / normal signoff), gated on consume success so a failed consume keeps the sentinel. The previously-inlined delete-both logic in `echo_fire.rs` was extracted into a shared free fn `remove_both_more_done(nested, flat)` + `WrapperState::delete_more_done_sentinel()` (derives both paths via `owlery::nested_perch_dir` + `perch_dir`, honoring the reader/writer path-asymmetry KNOWN-HAZARD); `fire_echo_commune_if_due` rewired onto it so fire-path and consume-path share one delete code path. 2 new echo_fire unit tests.

## [1.11.22] - 2026-05-30


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


1	# Changelog
2
3	All notable changes to the SPT (Spacetime / Sentience Pocket Transacter) plugin are documented in this file.
4
5	The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries authored retroactively from `git log --grep='chore: bump'` at Phase 34 (v1.7.1 milestone).
6
7	## [1.11.22] - 2026-05-30
8
9	### Fixed
10	- **Attaching a second machine no longer overwrites the other machine's agent context.** `psyche-sync-setup` used force-push semantics for agent branches, so attaching a second machine could clobber remote branch heads holding the first machine's most recent writes (a data-loss risk). Setup now reconciles before pushing — fetches the remote, rebases any diverged branches onto the remote head, and pushes per-ref with observable outcomes — so concurrent machines converge instead of overwriting each other.

# Changelog

All notable changes to the SPT (Spacetime / Sentience Pocket Transacter) plugin are documented in this file.

The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries authored retroactively from `git log --grep='chore: bump'` at Phase 34 (v1.7.1 milestone).

## [1.11.22] - 2026-05-30

### Fixed
- **Attaching a second machine no longer overwrites the other machine's agent context.** `psyche-sync-setup` used force-push semantics for agent branches, so attaching a second machine could clobber remote branch heads holding the first machine's most recent writes (a data-loss risk). Setup now reconciles before pushing — fetches the remote, rebases any diverged branches onto the remote head, and pushes per-ref with observable outcomes — so concurrent machines converge instead of overwriting each other.


## [1.11.23] - 2026-06-02

### Fixed
- **A manual commune or signoff no longer triggers a redundant automatic context summary right afterward.** Sending a `/spt:commune` (or signing off) already flushes your latest context to the Psyche; previously a pending background cadence trigger could still fire a duplicate echo-commune moments later. Delivering a commune/signoff now also clears that pending trigger, so the next automatic summary waits for the normal cadence.

### BTS
- quick-260602-2ar: `process_file_drop` (`src/live/wrapper/mod.rs`) now clears the `.more-done` cadence sentinel at its two FILE-DROP consume exits (empty-body signoff skip + the shared `exit_code == 0` block covering commune / init-stale signoff / normal signoff), gated on consume success so a failed consume keeps the sentinel. The previously-inlined delete-both logic in `echo_fire.rs` was extracted into a shared free fn `remove_both_more_done(nested, flat)` + `WrapperState::delete_more_done_sentinel()` (derives both paths via `owlery::nested_perch_dir` + `perch_dir`, honoring the reader/writer path-asymmetry KNOWN-HAZARD); `fire_echo_commune_if_due` rewired onto it so fire-path and consume-path share one delete code path. 2 new echo_fire unit tests.

## [1.11.22] - 2026-05-30


The file C:\Users\decid\Documents\projects\claude_skill_owl\CHANGELOG.md has been updated successfully. (file state is current in your context — no need to Read it back)

## [1.11.23] - 2026-06-02

### Fixed
- **A manual commune or signoff no longer triggers a redundant automatic context summary right afterward.** Sending a `/spt:commune` (or signing off) already flushes your latest context to the Psyche; previously a pending background cadence trigger could still fire a duplicate echo-commune moments later. Delivering a commune/signoff now also clears that pending trigger, so the next automatic summary waits for the normal cadence.

### BTS
- quick-260602-2ar: `process_file_drop` (`src/live/wrapper/mod.rs`) now clears the `.more-done` cadence sentinel at its two FILE-DROP consume exits (empty-body signoff skip + the shared `exit_code == 0` block covering commune / init-stale signoff / normal signoff), gated on consume success so a failed consume keeps the sentinel. The previously-inlined delete-both logic in `echo_fire.rs` was extracted into a shared free fn `remove_both_more_done(nested, flat)` + `WrapperState::delete_more_done_sentinel()` (derives both paths via `owlery::nested_perch_dir` + `perch_dir`, honoring the reader/writer path-asymmetry KNOWN-HAZARD); `fire_echo_commune_if_due` rewired onto it so fire-path and consume-path share one delete code path. 2 new echo_fire unit tests.

## [1.11.22] - 2026-05-30

The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries authored retroactively from `git log --grep='chore: bump'` at Phase 34 (v1.7.1 milestone).

+- **A manual commune or signoff no longer triggers a redundant automatic context summary right afterward.** Sending a `/spt:commune` (or signing off) already flushes your latest context to the Psyche; previously a pending background cadence trigger could still fire a duplicate echo-commune moments later. Delivering a commune/signoff now also clears that pending trigger, so the next automatic summary waits for the normal cadence.

+- quick-260602-2ar: `process_file_drop` (`src/live/wrapper/mod.rs`) now clears the `.more-done` cadence sentinel at its two FILE-DROP consume exits (empty-body signoff skip + the shared `exit_code == 0` block covering commune / init-stale signoff / normal signoff), gated on consume success so a failed consume keeps the sentinel. The previously-inlined delete-both logic in `echo_fire.rs` was extracted into a shared free fn `remove_both_more_done(nested, flat)` + `WrapperState::delete_more_done_sentinel()` (derives both paths via `owlery::nested_perch_dir` + `perch_dir`, honoring the reader/writer path-asymmetry KNOWN-HAZARD); `fire_echo_commune_if_due` rewired onto it so fire-path and consume-path share one delete code path. 2 new echo_fire unit tests.


Commit curated CHANGELOG entry (phase-1 of two-phase flow).


Bash

cd /c/Users/decid/Documents/projects/claude_skill_owl && git add CHANGELOG.md && git commit -q -m "docs: fill 1.11.23 CHANGELOG entry

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>" && git log --oneline -2

Commit curated changelog entry


62e3354 docs: fill 1.11.23 CHANGELOG entry
e73ef3a docs(quick-260602-2ar): communes/signoffs clear .more-done sentinel on FILE-DROP consume
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Curated entry committed (`62e3354`). Now run deploy with `-Bump patch` (build → marketplace sync+push → cache → installed_plugins.json flip + assert). Long-running.


PowerShell

powershell -ExecutionPolicy Bypass -File C:\Users\decid\Documents\projects\claude_skill_owl\docs\DEPLOY.ps1 -Bump patch

Deploy spt plugin with patch bump


==> Step 1: Resolve version from plugin.json
    Current version: 1.11.22
    Bumping (patch): 1.11.22 -> 1.11.23
[main e276a73] chore: bump spt plugin to v1.11.23
 2 files changed, 2 insertions(+), 2 deletions(-)
    Committed bump: e276a73 (not pushed; plugin.json + Cargo.toml only -- CHANGELOG entry was pre-committed)
    Version: 1.11.23
    RepoRoot: C:\Users\decid\Documents\projects\claude_skill_owl
    Marketplace: C:\Users\decid\.claude\plugins\marketplaces\cplugs
    PluginDir: C:\Users\decid\.claude\plugins\spt
==> Step 3: cargo build --release
   Compiling owl v1.11.23 (C:\Users\decid\Documents\projects\claude_skill_owl)
warning: unused import: `perch_path`
 --> src\owl\cleanup.rs:4:50
  |
4 | use crate::common::{hook_output, output, owlery, perch_path, process, types};
  |                                                  ^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default

warning: unused import: `crate::common::perch_path::ParentHint`
 --> src\owl\cleanup.rs:5:5
  |
5 | use crate::common::perch_path::ParentHint;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: function `open_spool` is never used
  --> src\common\spool.rs:65:4
   |
65 | fn open_spool(id: &str, _owlery: &Path) -> rusqlite::Result<Connection> {
   |    ^^^^^^^^^^
   |
   = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default

warning: function `check_alive` is never used
  --> src\owl\send.rs:37:8
   |
37 | pub fn check_alive(id: &str) {
   |        ^^^^^^^^^^^

warning: field `source` is never read
  --> src\owl\resume.rs:13:5
   |
11 | struct HookInput {
   |        --------- field in this struct
12 |     #[serde(default)]
13 |     source: Option<String>,
   |     ^^^^^^

warning: function `route_two_slice_outcome` is never used
   --> src\owl\echo_commune.rs:458:15
    |
458 | pub(crate) fn route_two_slice_outcome(
    |               ^^^^^^^^^^^^^^^^^^^^^^^

warning: function `route_two_slice` is never used
   --> src\owl\echo_commune.rs:722:15
    |
722 | pub(crate) fn route_two_slice(
    |               ^^^^^^^^^^^^^^^

warning: function `route_project_slot` is never used
   --> src\owl\echo_commune.rs:790:4
    |
790 | fn route_project_slot(
    |    ^^^^^^^^^^^^^^^^^^

warning: function `should_fire` is never used
   --> src\live\wrapper\echo_fire.rs:140:15
    |
140 | pub(crate) fn should_fire(sentinel: &Path, window: Duration) -> bool {
    |               ^^^^^^^^^^^

warning: `owl` (lib) generated 9 warnings (run `cargo fix --lib -p owl` to apply 2 suggestions)
    Finished `release` profile [optimized] target(s) in 8.22s
==> Step 4: Sync files to marketplace
    Ensure C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt\skills exists
    Ensure C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt\hooks exists
    Ensure C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt\.claude-plugin exists
    Copy owl.exe -> C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt
    Copy skills/* -> C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt\skills
    Copy hooks/* -> C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt\hooks
    Copy CHANGELOG.md -> C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt
    Copy plugin.json -> C:\Users\decid\.claude\plugins\marketplaces\cplugs\plugins\spt\.claude-plugin
==> Step 5: Commit + push marketplace
    Committing marketplace changes
[main cc460b3] spt: deploy v1.11.23
 3 files changed, 12 insertions(+), 1 deletion(-)
    Pushing marketplace
To https://github.com/SaberMage/cplugs.git
   39e85d3..cc460b3  main -> main
    Marketplace HEAD: cc460b3dcc8b821d9a0d20c730acdaf71b0d5c7c
==> Step 6-7: Sync new version + prune old cache
    Prior version: 1.11.22 (installPath leaf: 1.11.22)
    Create C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23/.claude-plugin
    Create C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23/skills
    Create C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23/hooks
    Copy owl.exe -> C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23
    Copy skills/* -> C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23/skills
    Copy hooks/* -> C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23/hooks
    Copy CHANGELOG.md -> C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23
    Copy plugin.json -> C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23/.claude-plugin
    Carry-over .pending-prune-* dirs from prior deploys: .pending-prune-1.11.20-1780101602
    Cleaned carry-over .pending-prune-1.11.20-1780101602
    Keep-set: 1.11.23, 1.11.22
    Prune candidates: 1.11.21
    Renamed 1.11.21 -> .pending-prune-1.11.21-1780367019
    Removed .pending-prune-1.11.21-1780367019
==> Step 8: Clean up rogue manual-install dir
    No manual-install dir at C:\Users\decid\.claude\plugins\spt -- nothing to clean up.
==> Step 9: Refresh plugin state via Claude Code CLI
    Running: claude plugin install spt@cplugs
Installing plugin "spt@cplugs"...✔ Plugin "spt@cplugs" is already installed (scope: user)
==> Step 9b: Patch installed_plugins.json (canonical pointer)
    Updated spt@cplugs[0]: version=1.11.23, installPath=C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23
    installed_plugins.json patched (atomic write via temp + Move-Item -Force).
==> Step 10: Verify installed_plugins.json reflects v1.11.23
    Pointer verified: spt@cplugs version=1.11.23 installPath=C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23

Deploy complete for v1.11.23.
Marketplace HEAD: cc460b3dcc8b821d9a0d20c730acdaf71b0d5c7c
>>> Now run /reload-plugins inside Claude Code to pick up the changes. <<<


Deploy complete. **v1.11.23 live**, pointer verified, marketplace pushed (`cc460b3`).

Check loose ends in spt repo.


Bash

cd /c/Users/decid/Documents/projects/claude_skill_owl && echo "---unpushed:---" && git log --oneline @{u}..HEAD 2>/dev/null || git log --oneline -5 && echo "---dirty:---" && git status --porcelain | grep -E "Cargo.lock|plugin.json|Cargo.toml" || echo "(clean: manifests)"

Check unpushed commits + dirty manifests


HANDOFF: trampoline -> C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23\owl.exe


---unpushed:---
e276a73 chore: bump spt plugin to v1.11.23
62e3354 docs: fill 1.11.23 CHANGELOG entry
e73ef3a docs(quick-260602-2ar): communes/signoffs clear .more-done sentinel on FILE-DROP consume
0559386 feat(quick-260602-2ar): clear .more-done sentinel when FILE-DROP consumes a commune/signoff
2835a0f docs(35.2): add Phase 35.2 sync data-loss/reconcile/bootstrap entries to v1.11.22 changelog
9eff01b chore: bump spt plugin to v1.11.22
a43017a docs(35.3): fill v1.11.22 CHANGELOG entry
95f00b0 docs(phase-35.3): evolve PROJECT.md after phase completion
8b346af fix(35.3): address code-review WR-03/WR-04/IN-02 in doctor partial-sync probe
22de72a docs(35.3): add code review report
5bd1edc docs(phase-35): resolve sync-setup-data-loss debug session after 35.3 gap closure (Issues 3-6)
908c5b2 docs(phase-35.3): complete phase execution
184caa9 docs(35.3-03): complete doctor partial-sync probe plan
0ae7b80 feat(35.3-03): wire partial-sync probe into check_sync_status + tests
8387fb9 feat(35.3-03): add bounded partial-sync probe + promote run_git_with_timeout
33d2bab docs(35.3-04): complete exit-1 + recovery docs plan
5f9b984 docs(35.3-04): add inline Recovery subsection to psyche-sync-setup skill
41b9254 docs(35.3-04): document exit code 1 in psyche-sync-setup skill
da7b509 docs(35.3-02): complete non-git project sync plan (Issue 7)
ac4d381 fix(35.3-02): guarded basename fallback for non-git host project sync (Issue 7)
8b4e6b1 test(35.3-02): assert non-git host cwd resolves guarded basename (Issue 7 RED)
60f9f9b docs(35.3-01): complete sync-setup error display plan
dd20350 test(35.3-01): lock GitError Display variants against Debug syntax
b6e2963 fix(35.3-01): route sync-setup failure through Display, lock SyncError variants
4da4cb8 docs(35.3): finalize phase plan (research, plans, decision-coverage annotations)
f851bc7 docs(35.3): create phase plan
b4877e4 docs(state): record phase 35.3 context session
dd4ec22 docs(35.3): capture phase context
b91b7cf docs(phase-35.2): evolve PROJECT.md after phase completion
e14905a docs(phase-35.2): complete phase execution
9eaed59 test(35.2): persist human verification items as UAT
7c182db fix(35.2): WR-04 reset transient-failure counters on --disable
9f2b299 fix(35.2): WR-02 emit PROBE_FAILED tag for ProbeFailed reconcile verdict
f5d3d27 fix(35.2): WR-06 set upstream only for branches whose push succeeded
db6460f fix(35.2): WR-03 reattach reconcile_against_remote routing-matrix rustdoc
0e892f1 fix(35.2): WR-01 phrase-anchor is_remote_404 to avoid bare '404' over-match
8317dd7 fix(35.2): WR-05 add identity-env determinism regression test for CR-01
c709258 fix(35.2): CR-01 pin all four GIT identity env vars in bootstrap commit-tree
0b5dc81 docs(35.2): add code review report
1b7cac5 docs(35.2-03): complete accept_flow reconcile + per-ref outcomes plan
52e9d5e test(35.2-03): unix-gated end-to-end accept_flow two-machine attach test
6ffe93c fix(sync): accept_flow pre-push reconcile + per-ref outcomes (SYNC-PUSH-PER-REF-01)
b5fa074 docs(35.2-02): complete reconcile_against_remote primitive plan
622b721 test(35.2-02): cross-platform routing tests + worktree-scoped rebase fix
2da0061 feat(35.2-02): implement reconcile_against_remote helper in sync.rs
7d16878 feat(35.2-02): add ReconcileVerdict enum + PerRefOutcome struct to sync.rs
9298693 docs(35.2-01): complete deterministic-bootstrap plan
d830a0f fix(tracked): lock GIT_*_DATE on cold bootstrap (SYNC-BOOTSTRAP-DET-01)
8b74375 docs(35.2,35.3): scaffold phases for psyche-sync-setup data-loss + UX pass
33ab37f docs(35.2): address plan-checker warnings
3d5144e docs(35.2): research psyche-sync-setup data-loss + bootstrap-determinism
9e1ab78 chore: bump spt plugin to v1.11.21
2e62194 docs(35.1): fill v1.11.21 CHANGELOG entry
9c36c95 docs(phase-35.1): complete phase execution
e226fb2 docs(35.1-fix): update REVIEW.md with fix log
0f4e55a fix(35.1-fix): CR-01 (follow-up) key on info.json existence not strict-serde
6bd6ec3 fix(35.1-fix): IN-05 reverse session_id precedence (stdin > env) in hook_subagent_start
d8664a8 docs(35.1-fix): IN-04 document parse_messages trim_end behavior
bae1fdf docs(35.1-fix): IN-02 note overwrite-in-place is not a fresh lifecycle in dedupe_drops
4240833 docs(35.1-fix): WR-06 document the LIST-NOMUT-01 ready-removal carve-out
8b0578b fix(35.1-fix): WR-05 include partial-state detail in relocate WARNING
c18c516 fix(35.1-fix): WR-04 surface gated_count in check_orphan_dirs PASS row
f24fcae fix(35.1-fix): WR-03 add nested-safety guard to check_duplicate_flat_nested
3c148ae refactor(35.1-fix): WR-01 drop no-op Option wrapping from list_self_perches
2314300 fix(35.1-fix): CR-02 mirror wrapper-alive bypass into check_stale_perches
4e9b987 fix(35.1-fix): CR-01 distinguish no-parseable-info from BUSY PID in is_online
575c0cf docs(35.1): add code review report
c17b2f7 docs(35.1-04): complete orphan-dir GC plan
cc0a898 docs(35.1-04): add SOFT-CLEANED:id to status-tag inventory
d4c9dbe test(35.1-04): integration tests for $OWL doctor --fix orphan-dir GC
8f1a687 feat(35.1-04): add check_orphan_dirs(fix) to $OWL doctor
bda4640 docs(35.1-03): complete CQRS-decouple plan -- SUMMARY + STATE + ROADMAP
530cfe0 test(35.1-03): CQRS invariant + orphan-count hint + wrapper-alive + grace gates
f0f1224 fix(35.1-03): CQRS-decouple $OWL list -- read-only collect + orphan-count hint + wrapper/grace gates
0a9e008 docs(35.1-02): complete Wave 1 — is_wrapper_alive + grace constant primitives
80b2389 feat(35.1-02): is_wrapper_alive + 60s list-orphan grace constant
3376ead docs(35.1-01): complete atomic info.json writes plan
bf2d3e2 test(35.1-01): source-order test enforces atomic info.json writes
30a7fd7 fix(35.1-01): atomic info.json writes -- close $OWL list race window
c18e79d docs(35.1): plans 01-04 + research open-questions resolved
f6e7665 docs(35.1): pattern map — 12/12 analogs identified
8026cd4 docs(35.1): research — bug surface located, primitives inventoried
dd78137 docs(35.1): add context locking GC surface + 3-fix ordering
7e3996d chore: bump spt plugin to v1.11.20
454b142 docs(34): fill v1.11.20 CHANGELOG entry — p-branch commit fix
84d85f7 docs(quick-260527-s8l): p-branch commit fix + debug session
2eb23eb fix(s8l): add salvage path to ensure_worktree for pre-populated targets
4866fb9 fix(s8l): reorder ensure_project_worktree before fs::write in echo_commune
a1c026c chore: bump spt plugin to v1.11.19
77ba2b1 docs(34): fill v1.11.19 CHANGELOG entry — sync-setup idempotency fix
d49ae9d docs(quick-260527-6ah): fix accept_flow idempotency bugs in src/common/sync.rs
e4ceca4 test(260527-6ah): add unix fake_gh regression tests for Bug A + Bug B
b7668dd fix(260527-6ah): make accept_flow Step 1 + Step 4 idempotent
a243aff chore: bump spt plugin to v1.11.18
784c585 docs(35): fill v1.11.18 CHANGELOG entry — SC1 gap-closure
6d7a693 docs(35): 35-10 gap-closure verification (human_needed)
61455ad test(35): persist human verification items as UAT
87511b2 docs(35-10): complete active sync-prompt delivery plan (SC1 gap-closure)
d0fcfb6 fix(35-10): always-loaded SKILL handler + UAT active-delivery wording + doctor PASS-on-failing fix
2c17cd9 test(35-10): render-path integration test for active sync-prompt delivery
e8c78ee feat(35-10): rewire both triggers to active delivery; remove sentinel queue
286fe40 feat(35-10): add active-delivery sync_prompt module with self-contained <instructions>
3d479c8 docs(35): plan 35-10 gap-closure — active sync-prompt delivery (SC1 fix)
b311fb1 docs(skills): revise signoff offline-path wording + trim live D-05 ref
db258bd chore: sync Cargo.lock owl version to 1.11.17
458d6f7 chore: bump spt plugin to v1.11.17
2d158a6 docs(35): fill v1.11.17 CHANGELOG entry — phase 35 psyche sync
---dirty:---
 M Cargo.lock
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Precedent exists (`db258bd chore: sync Cargo.lock owl version`) — commit the Cargo.lock version sync too.


cd /c/Users/decid/Documents/projects/claude_skill_owl && git add Cargo.lock && git commit -q -m "chore: sync Cargo.lock owl version to 1.11.23

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>" && git status --porcelain | grep -E "Cargo" || echo "clean" ; echo "HEAD:" && git rev-parse --short HEAD

Commit Cargo.lock version sync


warning: in the working copy of 'Cargo.lock', LF will be replaced by CRLF the next time Git touches it
clean
HEAD:
17bce2e
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


**Deploy done. v1.11.23 shipped.**

Sequence:
- Curated CHANGELOG `## [1.11.23]` (one-shot gate pass) → `62e3354`
- `-Bump patch` → `e276a73` (plugin.json + Cargo.toml)
- `cargo build --release` clean (8s, only pre-existing warnings)
- Marketplace synced + pushed → `cc460b3` (`SaberMage/cplugs` main)
- Cache `1.11.23` created; pruned `1.11.21` (kept 1.11.23 + 1.11.22 for in-flight handoffs)
- `installed_plugins.json` flipped + asserted: `version=1.11.23` ✓
- Cargo.lock version sync → `17bce2e` (matches `db258bd` precedent)

**Two things on you:**
1. **Run `/reload-plugins`** in Claude Code to pick up the new skills/binary (script stops before this by design).
2. **Push spt repo** — bump + quick-task commits are local-only (DEPLOY doesn't push spt repo). Unpushed on `main`: `0559386 → 17bce2e` (feat, docs, changelog, bump, lock). Marketplace already pushed.

Running live agents (incl. this `todlando`) self-migrate to 1.11.23 via seamless handoff on the pointer flip — no kill needed.


<task-notification>
<task-id>b6dj3pgoa</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/9" id="457d9578" type="msg" from="version-change">&lt;spt-version-changelog&gt;
  &lt;old&gt;1.11.22&lt;/old&gt;
  &lt;new&gt;1.11.23&lt;/new&gt;
  &lt;old_date&gt;2026-05-30&lt;/old_date&gt;
  &lt;new_date&gt;&lt;/new_date&gt;
  &lt;step_count&gt;1&lt;/step_count&gt;
  &lt;changelog_path&gt;$CLAUDE_PLUGIN_ROOT/CHANGELOG.md&lt;/changelog_path</EVENT-PART>
<EVENT-PART seq="2/9" id="457d9578">&gt;
  &lt;instructions&gt;
Render an AskUserQuestion to confirm whether the user wants to see the changelog for this SPT plugin update.

Before invoking AskUserQuestion, write one short pre-question line that names the version transition (and dates if both &lt;old_date&gt; and &lt;new_date&gt; are non-empty), e.g. &quot;SPT plugin</EVENT-PART>
<EVENT-PART seq="3/9" id="457d9578"> updated v1.11.22 → v1.11.23. Print changelog?&quot;.

Then invoke AskUserQuestion with EXACTLY these three options (labels verbatim, do not paraphrase):

  1. &quot;Yes, full changelog&quot; — On selection: read $CLAUDE_PLUGIN_ROOT/CHANGELOG.md and print every H2 section where 1.11.22 &lt; version &lt;= 1.11.23, verbatim markd</EVENT-PART>
<EVENT-PART seq="4/9" id="457d9578">own. Preserve the original H2 lines, bullet structure, and the user-facing H3 subsections (Added / Changed / Fixed / Removed / Deprecated / Security / Reverted). OMIT any &quot;### BTS&quot; subsection entirely — those are behind-the-scenes notes the user does not want to see. If a version has only a &quot;### BTS&quot; subsection with no us</EVENT-PART>
<EVENT-PART seq="5/9" id="457d9578">er-facing content, render the H2 header and date alone with a single line &quot;(no user-facing changes this release)&quot;. Render in the order they appear in the file (newest first). Do not summarize, do not paraphrase the bullets you do show.

  2. &quot;Yes, highlights only&quot; — On selection: read $CLAUDE_PLUGIN_ROOT/CHANGELOG.m</EVENT-PART>
<EVENT-PART seq="6/9" id="457d9578">d and synthesize 2-5 highlight bullets across the H2 sections where 1.11.22 &lt; version &lt;= 1.11.23. Draw ONLY from the &quot;### Added&quot;, &quot;### Changed&quot;, &quot;### Fixed&quot;, &quot;### Removed&quot;, &quot;### Deprecated&quot;, &quot;### Security&quot;, and &quot;### Reverted&quot; subsections. Do NOT draw from &quot;### BTS</EVENT-PART>
<EVENT-PART seq="7/9" id="457d9578">&quot; subsections — those are behind-the-scenes notes and must never appear in highlights. Pick the most impactful user-facing items across the range. Append exactly this pointer at the end: &quot;See $CLAUDE_PLUGIN_ROOT/CHANGELOG.md for full detail.&quot;

  3. &quot;Skip&quot; — On selection: take no action. Acknowledge briefly (&</EVENT-PART>
<EVENT-PART seq="8/9" id="457d9578">quot;OK, skipping.&quot;) and continue with the user's prior intent.

If the user provides freeform input via AskUserQuestion's &quot;Other&quot; option, treat all continuation phrases (later / not now / i
...(truncated)</event>
</task-notification>


SPT plugin updated v1.11.22 → v1.11.23. Print changelog?


AskUserQuestion

Question: SPT plugin updated v1.11.22 → v1.11.23. Print changelog?

Choices:
Print every user-facing changelog section for this update verbatim (omits behind-the-scenes notes).
Synthesize 2-5 highlight bullets from the user-facing changes.
No changelog. Continue.


Your questions have been answered: "SPT plugin updated v1.11.22 → v1.11.23. Print changelog?"="Yes, highlights only". You can now continue with these answers in mind.

Question: SPT plugin updated v1.11.22 → v1.11.23. Print changelog?

Choices:
Print every user-facing changelog section for this update verbatim (omits behind-the-scenes notes).
Synthesize 2-5 highlight bullets from the user-facing changes.
No changelog. Continue.


**v1.11.23 highlights:**

- Manual commune/signoff no longer triggers a redundant automatic context summary right after. Sending `/spt:commune` (or signing off) now also clears the pending background cadence trigger, so the next auto-summary waits for normal cadence instead of firing a duplicate.

See `$CLAUDE_PLUGIN_ROOT/CHANGELOG.md` for full detail.

(That's the fix this session shipped — now live in your running agent via the handoff.)


<local-command-caveat>Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>


<command-name>/reload-plugins</command-name>
            <command-message>reload-plugins</command-message>
            <command-args></command-args>


<local-command-stdout>Reloaded: 6 plugins · 0 skills · 39 agents · 9 hooks · 1 plugin MCP server · 1 plugin LSP server</local-command-stdout>


<command-message>spt:signoff</command-message>
<command-name>/spt:signoff</command-name>


Base directory for this skill: C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.23\skills\signoff

# /spt:signoff

All commands use `$LIVE` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.

> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.

## Flow

1. **Download Psyche's *current* context** — it shifts during the session as background echo communes fire. Re-download right before composing; the gen-N snapshot you read at session start is already stale:
   ```bash
   $LIVE psyche-download <your-id>
   ```

   > **Output truncation.** If the Bash tool reports `Output too large (N KB)` and saves the full result to a `tool-results/<id>.txt` file, use the `Read` tool on that saved path to access the full content.

2. **Diff your knowledge against this fresh download** — only *what's missing from it* belongs in the FINAL COMMUNE body (work completed, decisions made, intentions formed). Anything already absorbed is not.

3. **Sign off** -- use the **Write tool** to create
   `.claude/{your-id}-signoff.md`. Do not use Bash/heredoc -- Write is the
   canonical path: no shell escaping, no `EOF` collision risk, and Write
   auto-creates the `.claude/` parent directory.

   - **Plain signoff** (no final commune body): Write the file with an empty
     string as its contents.
   - **Signoff with a FINAL COMMUNE body**: Write the file with the final
     commune body as its contents — the body MUST be wrapped in the same
     two-slice envelope as a regular commune (see `## Two-slice body shape`
	 below, or `/spt:commune` for the full teaching).

	 Your live agent listener detects the file, notifies your Psyche wrapper,
	 prints `STOP:{your-id} (signoff dropped)`, and exits cleanly. The Psyche
	 wrapper independently consumes the file and tears itself down.

   **Offline path (no live listener):** If no session is currently live for
   your agent, the dropped `.claude/{your-id}-signoff.md` simply persists on
   disk. The next time anyone runs `$LIVE psyche-download <your-id>`, that
   signoff body reflected under a `## Pending Signoff (written {mtime})`
   header. This is the way to record post-session-end context updates.

## Two-slice body shape (Phase 25 D-10/D-11)

When the FINAL-COMMUNE body is non-empty (the second sub-bullet of Step 3 above), it follows the same Phase 25 D-10/D-11 two-slice envelope contract as a regular commune body:

- **In a tracked project** (detected via the `project="..."` attribute on the `<current ... />` tag at the top of `$LIVE psyche-download` output): emit `<live-context>...</live-context>` plus either `<project-context>...</project-context>` (with content) or `<project-context></project-context>` (empty body — "in-project but quiet" signal per D-25.1-02).
- **Outside any tracked project** (no payload at all from `psyche-download` — no `<current/>` tag, no `project="..."` attribute): emit ONLY `<live-context>...</live-context>`. Do NOT emit an empty `<project-context>` envelope.
- **Plain signoff** (empty file body, first sub-bullet of Step 3): the two-slice contract does NOT apply — the file is literally empty.

See `/spt:commune` → `## Two-slice body shape (Phase 25 D-10/D-11)` for the full teaching with three worked examples. See `psyche.md` §`<output_envelope>` for the canonical per-slice content taxonomy. See `psyche.md` §`<absorption>` (25.3-02) for the inbound-merge + re-emit rules — Psyche absorbs and re-emits these envelopes; on the FINAL_COMMUNE the wrapper's last Psyche session emits the two-slice body for the same routing pass.

**Routing: emit this envelope; SPT routes it to disk (Phase 25.3-02).** You write the two-slice body in the signoff file; the wrapper feeds it through Psyche's final session, and SPT (the Rust runtime) routes each emitted slice to its destination file. The `<live-context>` body lands at `agents/{your-id}/live_context.md`. The `<project-context>` body lands at `projects/<resolved_project>/{your-id}.md`, where `<resolved_project>` is resolved BY SPT via Self's perch `info.json` `cwd` field (25.3-01 Defect B2 fix). You do NOT name the project (Option A no-name shape locked); you do NOT know the filesystem path. NOT "Psyche writes the file." NOT "the agent persists to the project file." You emit; SPT routes.

**Signoff vs Stop:**
- **`/spt:signoff`** -- Graceful: INIT_SIGNOFF (with optional final commune) to Psyche first. Use for normal session end.
- **`/spt:force-stop`** -- Force: kills immediately. Use when unresponsive or need immediate kill.

## Envelope shape (Phase 23 v1.8)

As of v1.8 Phase 23, the init_signoff envelope carries a 5-field stamp (`machine`, `project`, `branch`, `head_sha`, `head_subject`) alongside `timestamp`.

```xml
<EVENT type="init_signoff" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="claude_skill_owl" branch="main" head_sha="abc123..." head_subject="subject line">Signoff initiated by Self...</EVENT>
```

With an optional final-commune body (when the signoff drop file is non-empty):

```xml
<EVENT type="init_signoff" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="claude_skill_owl" branch="main" head_sha="abc123..." head_subject="subject line">FINAL COMMUNE: ...body... | Signoff initiated by Self...</EVENT>
```

Outside a git repo (D-11), `branch`, `head_sha`, and `head_subject` are omitted entirely. The wrapper-side `type="init_signoff"` predicate is matched case-insensitively and ignores unknown attrs, so the new stamp fields are forward-compatible with older receivers. For drift detection on resume, see the `live` skill.

The `body` placeholder in the INIT_SIGNOFF examples above (when a FINAL COMMUNE is present) is the two-slice body — see `## Two-slice body shape (Phase 25 D-10/D-11)` above; the Phase 23 EVENT envelope and the Phase 25 two-slice body nest, with EVENT wrapping the two-slice body. `$LIVE signoff` composes the EVENT envelope automatically — you NEVER write `<EVENT>` tags.