title = "The adapter ships the [message-idle-translation-binary] (path=\"cc-spt-idle-translate\") — a lifecycle-managed stdin→stdout JSON-lines filter that turns each idle inbound <EVENT> envelope into the keystroke choreography spt-core applies ATOMICALLY to the broker PTY: ctrl+s (stash any draft) · 50ms · <envelope> (type the text, no trailing CR) · 50ms · {key:enter} (submit the PTY line) · {commit:true} (terminate the inject sequence) — so an inbound message never clobbers a half-typed operator draft (CC auto-restores the stashed draft after submit; no trailing restore keystroke). The submit is a DISCRETE {key:enter}, NOT a trailing \\r in the text: a \\r byte does NOT submit a Claude Code message (corrected 2026-06-23). The trailing {commit} is MANDATORY: spt-core's run_inject_worker (broker.rs:1075-1090) ends a sequence only on {commit}; without it the broker FAULTs at the 5s INJECT_COMMIT_DEADLINE on every delivery (the enter key submits the line, {commit} releases the InjectFloor — two distinct signals). Idle delivery ONLY (the spt-hosted complement to the busy/mid-turn [inject] hook path); coexists with a live `spt rc` controller."

-required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→4-step choreography [stash · 50ms · <envelope>\r submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so only the trailing submit \r fires, forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, text+\r submit-not-enter-key, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the trailing \r in the text command. GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).

+required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, text+\r submit-not-enter-key, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the trailing \r in the text command. GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).


Edit

unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, text+\r submit-not-enter-key, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape)

unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape)


The file C:\Users\decid\Documents\projects\spt-claude-code\traceable-reqs.toml has been updated successfully. (file state is current in your context — no need to Read it back)

-required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, text+\r submit-not-enter-key, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the trailing \r in the text command. GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).

+required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the trailing \r in the text command. GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).


just discovered: `\r` is not sufficient for a claude code message submission. so i need you to revamp the translation binary output to remove `\r` at the end of the message, and insert an enter keypre…


Fix message submission in translation binary


spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the trailing \r in the text command.

spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the discrete {key:enter} submit (and that the text carries NO trailing \r).


-required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the trailing \r in the text command. GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).

+required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the discrete {key:enter} submit (and that the text carries NO trailing \r). GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).


SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).

SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY and could split the injection). CORRECTION (2026-06-23): the submit is now a DISCRETE {key:enter} after the text, NOT a trailing \r — empirically a \r byte in injected text does NOT trigger Claude Code's message submission (CC needs the discrete Enter key event; the earlier assumption that a verbatim \r == {key:enter} held at the byte level but not for CC's input handling). The published contract example always showed {text}{key:enter}; this adapter now follows it literally.


-required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the discrete {key:enter} submit (and that the text carries NO trailing \r). GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the trailing \r submits (== {key:enter}, key_to_bytes("enter")->b"\r") AND the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY) — doyle is republishing the manifest to state text-is-verbatim authoritatively (a doc gap, the contract example showed {text}{key:enter} but never stated verbatim).

+required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the discrete {key:enter} submit (and that the text carries NO trailing \r). GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY and could split the injection). CORRECTION (2026-06-23): the submit is now a DISCRETE {key:enter} after the text, NOT a trailing \r — empirically a \r byte in injected text does NOT trigger Claude Code's message submission (CC needs the discrete Enter key event; the earlier assumption that a verbatim \r == {key:enter} held at the byte level but not for CC's input handling). The published contract example always showed {text}{key:enter}; this adapter now follows it literally.


<task-notification>
<task-id>baery84m8</task-id>
<tool-use-id>toolu_018fRbn8xp2fqjYP1uGj3LcG</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\baery84m8.output</output-file>
<status>completed</status>
<summary>Background command "Run cargo tests for translate binary" completed (exit code 0)</summary>
</task-notification>


Code updated. Build + test:


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code/tools/cc-spt-idle-translate && cargo test 2>&1 | tail -30

Run cargo tests for translate binary


Command running in background with ID: baery84m8. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\baery84m8.output. You will be notified when it completes. To check interim output, use Read on that file path.


Monitor

test result line appears (ok/FAILED) or compile error


<tool_use_error>InputValidationError: Monitor failed due to the following issues:
The required parameter `description` is missing
The required parameter `command` is missing
An unexpected parameter `taskId` was provided
An unexpected parameter `until` was provided

This tool's schema was not sent to the API — it was not in the discovered-tool set derived from message history. Without the schema in your prompt, typed parameters (arrays, numbers, booleans) get emitted as strings and the client-side parser rejects them. Load the tool first: call ToolSearch with query "select:Monitor", then retry this call. For reference, this tool's input schema is: {"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"description":{"description":"Short human-readable description of what you are monitoring (shown in notifications).","type":"string"},"timeout_ms":{"description":"Kill the monitor after this deadline. Default 300000ms, max 3600000ms. Ignored when persistent is true.","default":300000,"type":"number","minimum":1000},"persistent":{"description":"Run for the lifetime of the session (no timeout). Use for session-length watches like PR monitoring or log tails. Stop with TaskStop.","default":false,"type":"boolean"},"command":{"description":"Shell command or script. Each stdout line is an event; exit ends the watch.","type":"string"}},"required":["description","timeout_ms","persistent","command"],"additionalProperties":false}</tool_use_error>

InputValidationError: [
  {
    "expected": "string",
    "code": "invalid_type",
    "path": [
      "description"
    ],
    "message": "Invalid input: expected string, received undefined"
  },
  {
    "expected": "string",
    "code": "invalid_type",
    "path": [
      "command"
    ],
    "message": "Invalid input: expected string, received undefined"
  },
  {
    "code": "unrecognized_keys",
    "keys": [
      "taskId",
      "until"
    ],
    "path": [],
    "message": "Unrecognized keys: \"taskId\", \"until\""
  }
]


Prompt:
<task-notification>
<task-id>baery84m8</task-id>
<tool-use-id>toolu_018fRbn8xp2fqjYP1uGj3LcG</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\baery84m8.output</output-file>
<status>completed</status>
<summary>Background command "Run cargo tests for translate binary" completed (exit code 0)</summary>
</task-notification>


Tests passed (exit 0). Confirm output:


Bash

Show test result line


test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


<task-notification>
<task-id>b5pqztury</task-id>
<tool-use-id>toolu_01BqpcPjF7LiVJy9b95oB6W6</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b5pqztury.output</output-file>
<status>completed</status>
<summary>Background command "Build release binary + gate" completed (exit code 0)</summary>
</task-notification>


11 passed. Now build release binary + traceable-reqs check:


sh ci/idle-translate/build.sh 2>&1 | tail -15

Build release binary + gate


Command running in background with ID: b5pqztury. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b5pqztury.output. You will be notified when it completes. To check interim output, use Read on that file path.


Run traceable-reqs check


  [OK] REQ-HAZARD-PERCH-COLLISION  required: [unit]  stages: +doc +impl +unit +int
       The acceptance harness spawns every nested claude SUT under a DISPOSABLE identity (SPT_AGENT_ID=sptc-ci-<n>), never a live agent's perch id — a colliding id tears down the live agent's perch + poll stream (name-keyed, last-establish-wins)
  [OK] REQ-HAZARD-PSYCHE-PERMS-DEADLOCK  required: [doc, unit]  stages: +doc -impl +unit -int
       Every CC process spt-core spawns NON-INTERACTIVELY carries --dangerously-skip-permissions: each claude-spt-psyche turn (seed + every pulse, detached/null-stdin), both [session.self] bringup commands (base claude + ccs profile, broker PTY), AND [session.resume] (native-resume into the broker PTY). An interactive permission prompt with no operator deadlocks the turn (silently, for the detached Psyche). The Psyche additionally runs inside a Read/Edit/Write tool sandbox so auto-approve is bounded.
  [OK] REQ-PARITY-AUDIT  required: [doc]  stages: +doc -impl -unit -int
       The adapter's operative skill/profile surface is reconciled against the SCOPE.md LOCKED parity-trim (KEEP/ADD/DROP/TRANSFORM): every divergence is either resolved or logged as an accepted gap in docs/PARITY.md — the 'proven parity' record the sptc->spt succession flip gates on
  [OK] REQ-SETUP-ACTIVATE  required: [doc, int]  stages: +doc -impl -unit +int
       /sptc:setup, after confirming the spt binary is present, ACTIVATES the claude-spt adapter (`spt adapter add` — the manifest file in local dev; `--github <published-adapter-repo>` for end-users) so profiles/strings/hints/[digest] go live, then verifies (adapter no longer `deregistered`). Binary-present is NOT a no-op — this is the F-005 post-install activation bridge.
  [OK] REQ-SETUP-CCS  required: [doc]  stages: +doc -impl -unit -int
       /sptc:setup wires the ccs integration (SCOPE LOCKED setup #7): if `~/.ccs` is present, point the user at the shipped `claude-spt:ccs` profile (routes live/ready sessions through `ccs`, a drop-in for `claude`, via `--adapter claude-spt:ccs`) + sanity-check `ccs` on PATH; if absent, offer to install ccs with a one-sentence value prop (optional — base claude-spt is unaffected).
  [OK] REQ-SETUP-SUBNET  required: [doc]  stages: +doc -impl -unit -int
       /sptc:setup offers subnet onboarding (SCOPE LOCKED setup #3/#4): detect membership (`spt subnet status`); not-in-a-subnet -> offer create (`spt subnet create`, prints code/URI/QR) or join (`spt subnet join`); in-a-subnet -> show-code to invite / add-this-machine. Delegates the verb mechanics to /sptc:subnet and surfaces the OS-elevation requirement (create/join/show-code are seed-reveal/enroll-gated) with the per-context elevation paths; automated context-aware elevation is a deeper wave.
  [OK] REQ-SKILL-LIVE  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       /sptc:live upgrades THIS CC session to a LiveAgent: base claude-spt is live-capable ([session.psyche_init] in the BASE manifest, Option A) so ready-vs-live is the COMMAND not a profile — bare `spt api listen <id>` stamps the perch state=live_agent and the daemon hosts the Psyche (poll stays ready, livehost.rs:282 gate); the claude-spt-psyche runner keeps the Psyche claude alive (one --continue turn per pulse, NOT one-shot `claude -p`); a resident Monitor relay is the single delivery pipe; zero --adapter (host_binaries resolution, spt-core v0.9.0+)
  [OK] REQ-SKILL-SUBNET  required: [doc, int]  stages: +doc -impl -unit +int
       The adapter ships subnet membership skill(s) (create/join/show-code) wrapping the published `spt subnet <verb>` surface — the LOCKED-ADD cross-subnet entrypoint (v1 topology is mandatory, SCOPE)
  [OK] REQ-SKILL-VERSION  required: []  stages: -doc -impl -unit -int
       /spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)
  [OK] REQ-UPS-INJECTION  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands


Prompt:
<task-notification>
<task-id>b5pqztury</task-id>
<tool-use-id>toolu_01BqpcPjF7LiVJy9b95oB6W6</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b5pqztury.output</output-file>
<status>completed</status>
<summary>Background command "Build release binary + gate" completed (exit code 0)</summary>
</task-notification>


traceable-reqs all OK. Confirm build gate + check exit:


tail -8 "C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b5pqztury.output"; echo "---reqs---"; traceable-reqs check >/dev/null 2>&1 && echo "REQS EXIT 0" || echo "REQS NONZERO"

Show build tail + reqs exit


running 11 tests
...........
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  cc-spt-idle-translate: cargo test + release build
---reqs---
REQS EXIT 0


Green. Run proof-int against live spt to validate the new enter-key form:


sh ci/idle-translate/translate-proof-int.sh 2>&1 | tail -15

Run translate proof int


ok  translate-proof: TRANSLATE_PROOF_OK
ok  emitted the mandatory {commit} terminator (commit: yes)
ok  emitted the discrete {key:enter} submit
ok  text command carries no trailing \r
TRANSLATE-PROOF-INT OK


Proof-int green on live spt. Now release. Read the runbook:


docs\RELEASE-RUNBOOK.md


1	# Release runbook
2
3	> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
4	> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
5	> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
6	> thin adapter; the heavy signed-release machinery lives in spt-core, not here.
7
8	<!-- [<doc>->REQ-EXAMPLE-REL] (illustration; escaped per docs/TRACEABILITY.md) -->
9
10	## Publish targets
11
12	An spt-claude-code release lands in **two places**, by volatility:
13
14	1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced
15	   `/spt:*` skill skeletons, `hooks.json`, the SessionStart bootstrap, `plugin.json`). This
16	   is low-churn: it only changes on **structural** plugin changes, not on logic/instruction
17	   updates.
18	2. **The spt-core adapter registry** — the volatile bulk: the CC adapter **manifest**
19	   (`[digest]` extractor, profiles, strings, hints) **+ binary**. This publish is
20	   **spt-core-conducted** (the same framework that conducts adapter updates), not a plugin
21	   file push.
22
23	## One-time setup
24
25	No signing-key ceremony in this project. **Signing is delegated to the `spt` binary**, which
26	handles adapter manifest/binary integrity through spt-core's own signed-update framework. Keep
27	no release keys here. The only setup is marketplace/registry publish credentials for the two
28	targets above (a `cplugs` push token + whatever the spt-core adapter-registry publish step
29	requires).
30
31	## Per release
32
33	1. **Bump** the version (the plugin `plugin.json` version and/or the adapter manifest version —
34	   note these are distinct numbers; see Notes) if needed; **regenerate generated docs**
35	   (the mdBook `docs-site/` build + `llms.txt` / schema / CLI-help exports); land everything
36	   green.
37	2. **Write the user-facing changelog** — add a `## [<version>] - <date>` section at the top of
38	   `CHANGELOG.md` with **Added / Changed / Fixed** subsections. This section becomes the public
39	   release body verbatim, so it is the changelog every user reads. Rules:
40	   - **User-facing UX only.** What a person using the product notices or does differently. Name
41	     the actual commands / flags / surfaces they touch (`/spt:*` skills, `cc`, `/spt:setup`).
42	   - **No internal lingo** — no requirement ids, internal module names, commit hashes, or
43	     milestone / hazard codes. A reader who has never seen the source must understand every line.
44	   - **Flag breaking changes** prominently under Changed.
45	   - The release **fails loudly** if the tagged version has no `## [<version>]` section — the
46	     changelog is not optional.
47	3. **Tag**: `git tag <vX.Y.Z> && git push origin <vX.Y.Z>`. This triggers:
48	   - the **docs-publish workflow** — build `docs-site/` and publish to GitHub Pages (drift-gated;
49	     see `docs/DOCS-STRATEGY.md`).
50	4. **Publish** (light — signing delegated to `spt`):
51	   - **Skeleton plugin** → push to the `SaberMage/cplugs` marketplace **only when the plugin
52	     skeleton structurally changed** (new/removed skill skeleton, hook wiring, bootstrap, or
53	     `plugin.json`). Most releases do not bump the marketplace.
54	   - **Manifest + binary** → publish through the **spt-core adapter registry**; the `spt`
55	     binary performs signing/integrity as part of spt-core's adapter-update framework. You do
56	     **not** run a separate signing step.
57
58	## cplugs skeleton publish — concrete mechanics
59
60	<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
61	The concrete cplugs marketplace steps, captured from the sister project's
62	`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs
63	target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart
64	bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest
65	ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
66	none of owl's binary-lifecycle machinery applies here.
67
68	> **Now scripted (`ci/publish/`).** The validate + stage mechanics below are codified:
69	> `ci/publish/validate-skeleton.sh` is a binary installability gate (valid `plugin.json` with
70	> `name=sptc`; valid `hooks.json` whose referenced wrappers all exist; every skill has a
71	> `SKILL.md`; **no runtime-state / binary / manifest leak in the published surface**) — it runs in
72	> `ci/run-gates.sh` (the `skeleton-validate` gate) and is unit-tested by `tests/skeleton-validate.sh`.
73	> `ci/publish/package-skeleton.sh` codifies the per-bump copy below — validates first, **dry-run by
74	> default**, stages only the skeleton subset, never pushes (the marketplace commit/push stays the
75	> operator's step).
76
77	**One-time marketplace setup:**
78
79	```bash
80	# Clone the marketplace repo
81	git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
82	# Register a "cplugs" entry in ~/.claude/plugins/known_marketplaces.json
83	#   source: { source: "github", repo: "SaberMage/cplugs" }
84	```
85
86	**Per skeleton bump** (only when the skeleton *structurally* changes — see "Per release"):
87
88	```bash
89	# 1. Bump plugin.json version. The plugin manager compares this to the cached copy and
90	#    SKIPS the update entirely if they match — a stale version = a silent no-update.
91	# 2. Copy skeleton files (NO binary) into the marketplace clone:
92	MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/sptc
93	cp -r skills/*  "$MARKET/skills/"
94	cp -r hooks/*   "$MARKET/hooks/"
95	cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
96	# 3. Commit + push the cplugs repo:
97	cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/sptc/ \
98	  && git commit -m "sptc: <change>" && git push
99	```
100
101	**Pointer flip** (the authoritative install-state update):
102
103	```bash
104	claude plugin install sptc@cplugs   # un-orphans + rewrites installed_plugins.json atomically
105	# then, inside Claude Code:
106	/reload-plugins
107	```
108
109	**Gotchas (these DO apply to the skeleton):**
110	- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).
111	  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.
112	- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`
113	  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin
114	  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.
115	- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates
116	  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.
117
118	**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so
119	skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·
120	the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.
121
122	> **RULED (operator, 2026-06-14) — cplugs plugin name = `sptc`.** CC ties the skill prefix to the
123	> `plugin.json` name (no override), so skills surface as `/sptc:*`. **Succession:** once parity is
124	> proven, flip `plugin.json` name `sptc`→`spt` (skills → `/spt:*`) and retire/rename legacy owl's
125	> cplugs `spt` plugin in the same coordinated move — two plugins cannot share the `spt` name. The
126	> flip is a single substitution `s/sptc/spt/` (plugin dir, `plugin.json` name, marketplace path).
127
128	## adapter manifest publish — the `adapter.spt` release asset
129
130	<!-- [doc->REQ-DIST-ADAPTER-RELEASE] -->
131	The CC adapter manifest (publish target #2) ships to end users as an **`adapter.spt`** GitHub
132	**release asset** on this monorepo, acquired with **`spt adapter add --release SaberMage/spt-claude-code`**
133	(doyle's `--release` source; needs **spt v0.7.3+ / counter 15** — not in 0.7.2). No dedicated
134	adapter repo: the asset is packed straight from `adapter/`.
135
136	Since v0.6.1 the asset is **ONE multi-platform fat `adapter.spt`** (ADR-0024 W1, spt-core ≥ 0.13.2):
137	a single archive bundles every supported platform's binaries, and install auto-resolves the host's.
138	This RETIRED the F-014 per-OS stopgap — there is no longer a default-vs-per-OS-asset split.
139
140	- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes the single
141	  `dist/adapter.spt`. It validates the manifest, refuses a `min_spt_core_version < 0.13.2` (a fat
142	  archive needs it), requires **both** platforms' built tool binaries, and tars the archive **ROOT** =
143	  `manifest.toml` (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name) +
144	  `strings/` **shared at root**, plus each recognized target-triple's binaries under a `<triple>/` dir.
145	  Never uploads (operator's step).
146	<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
147	- **Fat layout (REQ-DIST-ADAPTER-PEROS).** Recognized triples = **`x86_64-pc-windows-msvc`** (win
148	  `.exe`) + **`x86_64-unknown-linux-gnu`** (linux ELF) — the ONLY two spt-core classifies in a fat
149	  archive. On install it places the shared root + **flattens this node's `<triple>/*` into the install
150	  dir**, so the bare-name command tokens (`claude-spt-digest`, …) still resolve at `<install_dir>/`
151	  (REQ-INSTALL-11). **Footgun:** an unrecognized top-level dir is silently treated as a shared-root
152	  entry and lands flat — the packer guards this (refuses any stray top-level dir); for a platform
153	  beyond the two triples, ship a *separate* single-triple asset via `--asset`, never a third dir here.
154	- **Build both platforms first** (the packer needs both binary sets present):
155	  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
156	    ci/idle-translate/build.sh` → `tools/*/target/release/*.exe`.
157	  - **Linux cross-built FROM Windows (proven 2026-06-16):** bare `cargo build --target
158	    x86_64-unknown-linux-gnu` fails (`error: linker 'cc' not found` — the crate compiles, only the
159	    link needs a Linux linker), so use **`cargo-zigbuild`** (zig supplies the cross-linker):
160	    ```sh
161	    # one-time: install zig (a self-contained binary) + cargo-zigbuild
162	    cargo install cargo-zigbuild
163	    # download zig (e.g. 0.14.1) to a dir OUTSIDE the repo (do NOT keep it under tools/ — it's a
164	    # traceable-reqs scan root and zig's bundled libc C-headers trip the scanner), put zig.exe on PATH:
165	    #   e.g. ~/.sptc-zig/zig-x86_64-windows-0.14.1/zig.exe  →  export PATH="$HOME/.sptc-zig/...:$PATH"
166	    rustup target add x86_64-unknown-linux-gnu
167	    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-digest/Cargo.toml
168	    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-psyche/Cargo.toml
169	    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/cc-spt-idle-translate/Cargo.toml
170	    ```
171	    → `tools/*/target/x86_64-unknown-linux-gnu/release/*` carrying real `ELF x86-64 GNU/Linux`
172	    binaries (verified). Then `sh ci/publish/package-adapter.sh --apply` packs BOTH into one
173	    `dist/adapter.spt`.
174	- **Publish:** attach the single `dist/adapter.spt` to the GitHub release on `SaberMage/spt-claude-code`.
175	  End users `spt adapter add --release SaberMage/spt-claude-code` (default asset `adapter.spt`, or
176	  `--tag <ver>` to pin) — the fat archive auto-resolves the host's binaries, no `--asset` needed. The
177	  `[update] avenue = "gh_release"` self-update fetches the same default `adapter.spt`, host-agnostic.
178	  - **F-015 note (Windows):** a binary-changing update still can't overwrite a binary a running live
179	    agent locks — but spt-core ≥ 0.13.2's W3 fix runs the live psyche from a `<perch>/.live-bin`
180	    own-copy, so the install-dir binary stays lock-free (validated 2026-06-22). Strings/hook-only
181	    releases always land fine.
182	  - **Local dogfood before upload (recommended):** extract `dist/adapter.spt`, flatten this node's
183	    triple into a dir (`cp <triple>/* .`), then `spt adapter {translate,digest}-proof claude-spt --dir
184	    <dir> --manifest <dir>/manifest.toml` — proves the flattened binaries resolve + run without any
185	    upload or registry mutation.
186	  - **`/sptc:setup` coupling.** The fat default is host-agnostic, so setup needs no os-detection /
187	    `--asset` — a bare `adapter add --release` suffices. (The current setup body still detects os/arch
188	    + passes `--asset`; harmless, simplifying it to the bare default is a follow-on.)
189	- **Binaries (install-dir resolution — REQ-INSTALL-11).** A `--release`/`--github` acquisition extracts
190	  the `.spt` archive's `manifest.toml` + `strings/` **+ both tool binaries** into the adapter install
191	  dir (`…/adapters/_github/<safe>/`). The command templates use **bare names** (`claude-spt-digest`,
192	  `claude-spt-psyche`), which spt-core resolves **from that install dir** (v0.8.0 Feature B /
193	  REQ-INSTALL-11) — **no PATH placement needed** (dogfood-proven on v0.8.1 for both binaries; F-006
194	  RESOLVED, interim retired). The pack therefore MUST carry both binaries (the resolution source). Note:
195	  a plain local-dev `spt adapter add <manifest.toml>` copies manifest + strings only (no binaries in the
196	  install dir), so local-dev runtime still relies on the built binary being on PATH (e.g. the CI ints
197	  prepend `target/release`); the install-dir path is the end-user `--release` story.
198	- **Acquisition only:** `--release` does **not** establish an `[update]` self-update route (our
199	  manifest declares no `[update]` → COPY-mode registration). Re-acquire a newer version by re-running
200	  `--release --tag <newer>`. The signed `file_pull` self-update channel is a separate, later concern.
201
202	## Update path (dual, kept in sync)
203
204	Users receive updates two ways, and both must stay consistent:
205	- **File-pull (the real channel)** — spt-core conducts manifest + binary updates seamlessly;
206	  this is how logic and instruction churn reaches users. Version-of-truth = the
207	  manifest/binary version spt-core tracks (`spt adapter list` / `/spt:version`).
208	- **`claude plugin update` (cautionary, skeleton sync)** — only resyncs the low-churn
209	  marketplace skeleton on the rare structural bump. It is NOT the version-of-truth.
210
211	## Notes
212
213	- **Distinct numbers — never conflate them:** the **plugin (marketplace) version** (in
214	  `plugin.json`, bumps rarely) and the **adapter manifest/binary version** (spt-core-tracked,
215	  the user-visible version-of-truth). They move on independent schedules — the plugin can sit
216	  static across many manifest updates.
217	- **Trust model:** adapter manifest/binary integrity is the `spt` binary's responsibility via
218	  spt-core's signed-update framework — spt-claude-code defines no separate trust ceremony.
219	  (Claude Code has no plugin-file integrity check, which is why the skeleton plugin is safe to
220	  in-place-edit; only old version dirs orphan on update and are GC'd after 7 days.)
221

# Release runbook

> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
> thin adapter; the heavy signed-release machinery lives in spt-core, not here.

<!-- [<doc>->REQ-EXAMPLE-REL] (illustration; escaped per docs/TRACEABILITY.md) -->

## Publish targets

An spt-claude-code release lands in **two places**, by volatility:

1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced
   `/spt:*` skill skeletons, `hooks.json`, the SessionStart bootstrap, `plugin.json`). This
   is low-churn: it only changes on **structural** plugin changes, not on logic/instruction
   updates.
2. **The spt-core adapter registry** — the volatile bulk: the CC adapter **manifest**
   (`[digest]` extractor, profiles, strings, hints) **+ binary**. This publish is
   **spt-core-conducted** (the same framework that conducts adapter updates), not a plugin
   file push.

## One-time setup

No signing-key ceremony in this project. **Signing is delegated to the `spt` binary**, which
handles adapter manifest/binary integrity through spt-core's own signed-update framework. Keep
no release keys here. The only setup is marketplace/registry publish credentials for the two
targets above (a `cplugs` push token + whatever the spt-core adapter-registry publish step
requires).

## Per release

1. **Bump** the version (the plugin `plugin.json` version and/or the adapter manifest version —
   note these are distinct numbers; see Notes) if needed; **regenerate generated docs**
   (the mdBook `docs-site/` build + `llms.txt` / schema / CLI-help exports); land everything
   green.
2. **Write the user-facing changelog** — add a `## [<version>] - <date>` section at the top of
   `CHANGELOG.md` with **Added / Changed / Fixed** subsections. This section becomes the public
   release body verbatim, so it is the changelog every user reads. Rules:
   - **User-facing UX only.** What a person using the product notices or does differently. Name
     the actual commands / flags / surfaces they touch (`/spt:*` skills, `cc`, `/spt:setup`).
   - **No internal lingo** — no requirement ids, internal module names, commit hashes, or
     milestone / hazard codes. A reader who has never seen the source must understand every line.
   - **Flag breaking changes** prominently under Changed.
   - The release **fails loudly** if the tagged version has no `## [<version>]` section — the
     changelog is not optional.
3. **Tag**: `git tag <vX.Y.Z> && git push origin <vX.Y.Z>`. This triggers:
   - the **docs-publish workflow** — build `docs-site/` and publish to GitHub Pages (drift-gated;
     see `docs/DOCS-STRATEGY.md`).
4. **Publish** (light — signing delegated to `spt`):
   - **Skeleton plugin** → push to the `SaberMage/cplugs` marketplace **only when the plugin
     skeleton structurally changed** (new/removed skill skeleton, hook wiring, bootstrap, or
     `plugin.json`). Most releases do not bump the marketplace.
   - **Manifest + binary** → publish through the **spt-core adapter registry**; the `spt`
     binary performs signing/integrity as part of spt-core's adapter-update framework. You do
     **not** run a separate signing step.

## cplugs skeleton publish — concrete mechanics

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
The concrete cplugs marketplace steps, captured from the sister project's
`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs
target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart
bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest
ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
none of owl's binary-lifecycle machinery applies here.

> **Now scripted (`ci/publish/`).** The validate + stage mechanics below are codified:
> `ci/publish/validate-skeleton.sh` is a binary installability gate (valid `plugin.json` with
> `name=sptc`; valid `hooks.json` whose referenced wrappers all exist; every skill has a
> `SKILL.md`; **no runtime-state / binary / manifest leak in the published surface**) — it runs in
> `ci/run-gates.sh` (the `skeleton-validate` gate) and is unit-tested by `tests/skeleton-validate.sh`.
> `ci/publish/package-skeleton.sh` codifies the per-bump copy below — validates first, **dry-run by
> default**, stages only the skeleton subset, never pushes (the marketplace commit/push stays the
> operator's step).

**One-time marketplace setup:**

```bash
# Clone the marketplace repo
git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
# Register a "cplugs" entry in ~/.claude/plugins/known_marketplaces.json
#   source: { source: "github", repo: "SaberMage/cplugs" }
```

**Per skeleton bump** (only when the skeleton *structurally* changes — see "Per release"):

```bash
# 1. Bump plugin.json version. The plugin manager compares this to the cached copy and
#    SKIPS the update entirely if they match — a stale version = a silent no-update.
# 2. Copy skeleton files (NO binary) into the marketplace clone:
MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/sptc
cp -r skills/*  "$MARKET/skills/"
cp -r hooks/*   "$MARKET/hooks/"
cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
# 3. Commit + push the cplugs repo:
cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/sptc/ \
  && git commit -m "sptc: <change>" && git push
```

**Pointer flip** (the authoritative install-state update):

```bash
claude plugin install sptc@cplugs   # un-orphans + rewrites installed_plugins.json atomically
# then, inside Claude Code:
/reload-plugins
```

**Gotchas (these DO apply to the skeleton):**
- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).
  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.
- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`
  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin
  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.
- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates
  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.

**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so
skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·
the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.

> **RULED (operator, 2026-06-14) — cplugs plugin name = `sptc`.** CC ties the skill prefix to the
> `plugin.json` name (no override), so skills surface as `/sptc:*`. **Succession:** once parity is
> proven, flip `plugin.json` name `sptc`→`spt` (skills → `/spt:*`) and retire/rename legacy owl's
> cplugs `spt` plugin in the same coordinated move — two plugins cannot share the `spt` name. The
> flip is a single substitution `s/sptc/spt/` (plugin dir, `plugin.json` name, marketplace path).

## adapter manifest publish — the `adapter.spt` release asset

<!-- [doc->REQ-DIST-ADAPTER-RELEASE] -->
The CC adapter manifest (publish target #2) ships to end users as an **`adapter.spt`** GitHub
**release asset** on this monorepo, acquired with **`spt adapter add --release SaberMage/spt-claude-code`**
(doyle's `--release` source; needs **spt v0.7.3+ / counter 15** — not in 0.7.2). No dedicated
adapter repo: the asset is packed straight from `adapter/`.

Since v0.6.1 the asset is **ONE multi-platform fat `adapter.spt`** (ADR-0024 W1, spt-core ≥ 0.13.2):
a single archive bundles every supported platform's binaries, and install auto-resolves the host's.
This RETIRED the F-014 per-OS stopgap — there is no longer a default-vs-per-OS-asset split.

- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes the single
  `dist/adapter.spt`. It validates the manifest, refuses a `min_spt_core_version < 0.13.2` (a fat
  archive needs it), requires **both** platforms' built tool binaries, and tars the archive **ROOT** =
  `manifest.toml` (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name) +
  `strings/` **shared at root**, plus each recognized target-triple's binaries under a `<triple>/` dir.
  Never uploads (operator's step).
<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Fat layout (REQ-DIST-ADAPTER-PEROS).** Recognized triples = **`x86_64-pc-windows-msvc`** (win
  `.exe`) + **`x86_64-unknown-linux-gnu`** (linux ELF) — the ONLY two spt-core classifies in a fat
  archive. On install it places the shared root + **flattens this node's `<triple>/*` into the install
  dir**, so the bare-name command tokens (`claude-spt-digest`, …) still resolve at `<install_dir>/`
  (REQ-INSTALL-11). **Footgun:** an unrecognized top-level dir is silently treated as a shared-root
  entry and lands flat — the packer guards this (refuses any stray top-level dir); for a platform
  beyond the two triples, ship a *separate* single-triple asset via `--asset`, never a third dir here.
- **Build both platforms first** (the packer needs both binary sets present):
  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
    ci/idle-translate/build.sh` → `tools/*/target/release/*.exe`.
  - **Linux cross-built FROM Windows (proven 2026-06-16):** bare `cargo build --target
    x86_64-unknown-linux-gnu` fails (`error: linker 'cc' not found` — the crate compiles, only the
    link needs a Linux linker), so use **`cargo-zigbuild`** (zig supplies the cross-linker):
    ```sh
    # one-time: install zig (a self-contained binary) + cargo-zigbuild
    cargo install cargo-zigbuild
    # download zig (e.g. 0.14.1) to a dir OUTSIDE the repo (do NOT keep it under tools/ — it's a
    # traceable-reqs scan root and zig's bundled libc C-headers trip the scanner), put zig.exe on PATH:
    #   e.g. ~/.sptc-zig/zig-x86_64-windows-0.14.1/zig.exe  →  export PATH="$HOME/.sptc-zig/...:$PATH"
    rustup target add x86_64-unknown-linux-gnu
    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-digest/Cargo.toml
    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-psyche/Cargo.toml
    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/cc-spt-idle-translate/Cargo.toml
    ```
    → `tools/*/target/x86_64-unknown-linux-gnu/release/*` carrying real `ELF x86-64 GNU/Linux`
    binaries (verified). Then `sh ci/publish/package-adapter.sh --apply` packs BOTH into one
    `dist/adapter.spt`.
- **Publish:** attach the single `dist/adapter.spt` to the GitHub release on `SaberMage/spt-claude-code`.
  End users `spt adapter add --release SaberMage/spt-claude-code` (default asset `adapter.spt`, or
  `--tag <ver>` to pin) — the fat archive auto-resolves the host's binaries, no `--asset` needed. The
  `[update] avenue = "gh_release"` self-update fetches the same default `adapter.spt`, host-agnostic.
  - **F-015 note (Windows):** a binary-changing update still can't overwrite a binary a running live
    agent locks — but spt-core ≥ 0.13.2's W3 fix runs the live psyche from a `<perch>/.live-bin`
    own-copy, so the install-dir binary stays lock-free (validated 2026-06-22). Strings/hook-only
    releases always land fine.
  - **Local dogfood before upload (recommended):** extract `dist/adapter.spt`, flatten this node's
    triple into a dir (`cp <triple>/* .`), then `spt adapter {translate,digest}-proof claude-spt --dir
    <dir> --manifest <dir>/manifest.toml` — proves the flattened binaries resolve + run without any
    upload or registry mutation.
  - **`/sptc:setup` coupling.** The fat default is host-agnostic, so setup needs no os-detection /
    `--asset` — a bare `adapter add --release` suffices. (The current setup body still detects os/arch
    + passes `--asset`; harmless, simplifying it to the bare default is a follow-on.)
- **Binaries (install-dir resolution — REQ-INSTALL-11).** A `--release`/`--github` acquisition extracts
  the `.spt` archive's `manifest.toml` + `strings/` **+ both tool binaries** into the adapter install
  dir (`…/adapters/_github/<safe>/`). The command templates use **bare names** (`claude-spt-digest`,
  `claude-spt-psyche`), which spt-core resolves **from that install dir** (v0.8.0 Feature B /
  REQ-INSTALL-11) — **no PATH placement needed** (dogfood-proven on v0.8.1 for both binaries; F-006
  RESOLVED, interim retired). The pack therefore MUST carry both binaries (the resolution source). Note:
  a plain local-dev `spt adapter add <manifest.toml>` copies manifest + strings only (no binaries in the
  install dir), so local-dev runtime still relies on the built binary being on PATH (e.g. the CI ints
  prepend `target/release`); the install-dir path is the end-user `--release` story.
- **Acquisition only:** `--release` does **not** establish an `[update]` self-update route (our
  manifest declares no `[update]` → COPY-mode registration). Re-acquire a newer version by re-running
  `--release --tag <newer>`. The signed `file_pull` self-update channel is a separate, later concern.

## Update path (dual, kept in sync)

Users receive updates two ways, and both must stay consistent:
- **File-pull (the real channel)** — spt-core conducts manifest + binary updates seamlessly;
  this is how logic and instruction churn reaches users. Version-of-truth = the
  manifest/binary version spt-core tracks (`spt adapter list` / `/spt:version`).
- **`claude plugin update` (cautionary, skeleton sync)** — only resyncs the low-churn
  marketplace skeleton on the rare structural bump. It is NOT the version-of-truth.

## Notes

- **Distinct numbers — never conflate them:** the **plugin (marketplace) version** (in
  `plugin.json`, bumps rarely) and the **adapter manifest/binary version** (spt-core-tracked,
  the user-visible version-of-truth). They move on independent schedules — the plugin can sit
  static across many manifest updates.
- **Trust model:** adapter manifest/binary integrity is the `spt` binary's responsibility via
  spt-core's signed-update framework — spt-claude-code defines no separate trust ceremony.
  (Claude Code has no plugin-file integrity check, which is why the skeleton plugin is safe to
  in-place-edit; only old version dirs orphan on update and are GC'd after 7 days.)


Binary-logic change → adapter manifest+binary publish (no skeleton structural change, so no cplugs bump). Check manifest version + changelog:


grep -n "version" adapter/claude-spt.toml | head; echo "---"; grep -n "min_spt_core" adapter/claude-spt.toml; echo "--- changelog top ---"; head -20 CHANGELOG.md

Check manifest version + changelog top


5:# Registered with `spt adapter add <path>`; the version-of-truth is `spt adapter list`, not the
6:# marketplace `plugin.json` version.
37:version = "0.6.1"
45:min_spt_core_version = "0.13.2"
338:version = { file = "skills/version.md" }
---
45:min_spt_core_version = "0.13.2"
212:# min_spt_core stays 0.9.0 because the ready/live flows work on 0.9.0, and a blanket floor would
--- changelog top ---
# Changelog

All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
public release body verbatim.

> Each release below is keyed to the **adapter version of truth** (the version you see in
> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
> moves on its own slower schedule and may differ.

## [0.6.1] - 2026-06-22

> Requires spt-core **v0.13.2 or newer** (the version that can install a multi-platform package).
> Packaging-only release — no change to what the adapter does, only how it ships.

### Packaging
- **One download now covers every platform.** The adapter ships as a single multi-platform
  `adapter.spt` that bundles both the Windows and Linux builds beside one shared manifest; installing
  picks your platform automatically. This replaces the previous per-OS files and the Windows-only
  stopgap `adapter.spt` (which broke self-update on Linux), so `spt adapter add --release …` and


Bump manifest to 0.6.2. Update version + changelog. Then build. First check zig toolchain + existing linux binaries:


echo "--- zig ---"; command -v zig && zig version; command -v cargo-zigbuild && echo "zigbuild present"; echo "--- linux binaries present? ---"; ls tools/*/target/x86_64-unknown-linux-gnu/release/ 2>/dev/null; echo "--- win release binaries ---"; ls tools/*/target/release/*.exe 2>/dev/null

Check zig + existing built binaries


--- zig ---
/c/Users/decid/.cargo/bin/cargo-zigbuild
zigbuild present
--- linux binaries present? ---
tools/cc-spt-idle-translate/target/x86_64-unknown-linux-gnu/release/:
build
cc-spt-idle-translate
cc-spt-idle-translate.d
deps
examples
incremental

tools/claude-spt-digest/target/x86_64-unknown-linux-gnu/release/:
build
claude-spt-digest
claude-spt-digest.d
deps
examples
incremental

tools/claude-spt-psyche/target/x86_64-unknown-linux-gnu/release/:
build
claude-spt-psyche
claude-spt-psyche.d
deps
examples
incremental
--- win release binaries ---
tools/cc-spt-idle-translate/target/release/cc-spt-idle-translate.exe
tools/claude-spt-digest/target/release/claude-spt-digest.exe
tools/claude-spt-psyche/target/release/claude-spt-psyche.exe


All binaries present; need to rebuild the changed linux idle-translate. Check zig on PATH:


command -v zig || ls ~/.sptc-zig/*/zig.exe 2>/dev/null || echo "zig not found on PATH"

Locate zig binary


export PATH="$HOME/.sptc-zig/zig-x86_64-windows-0.14.1:$PATH" && cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/cc-spt-idle-translate/Cargo.toml 2>&1 | tail -8

Cross-build linux idle-translate binary


Command running in background with ID: bek0mdcz3. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\bek0mdcz3.output. You will be notified when it completes. To check interim output, use Read on that file path.


<task-notification>
<task-id>bek0mdcz3</task-id>
<tool-use-id>toolu_01V88eYF1MnDvNdgNfGoGm23</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\bek0mdcz3.output</output-file>
<status>completed</status>
<summary>Background command "Cross-build linux idle-translate binary" completed (exit code 0)</summary>
</task-notification>


While that builds, bump version + changelog. Manifest:


<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.


30	kind = "harness"
31	# 0.6.1 (2026-06-22): PACKAGING modernization — the release now ships a single MULTI-PLATFORM fat
32	# .spt (per-triple subdirs, ADR-0024 W1) which only spt-core >= 0.13.2 can extract, so the floor moves
33	# to 0.13.2 (see below) and /sptc:setup drops its now-redundant os-detection (`adapter add --release`
34	# is host-agnostic). The seam surface is unchanged from 0.6.0 (the v0.13.0 RUN-group seams
35	# [session.resume] + [message-idle-translation-binary], added in 0.6.0). Fresh tag, not an in-place
36	# v0.6.0 re-cut (a re-cut asset on the same tag is ambiguous — operator + doyle call).
37	version = "0.6.1"
38	# Floor BUMPED to 0.13.2 (2026-06-22, C / F-014 drop): the release packaging moved from the per-OS
39	# stopgap (`adapter.spt` = a windows-binary copy — F-014; broke on a linux host) to ONE fat .spt that
40	# bundles both x86_64 triples beside a shared manifest. spt-core extracts a fat archive (classify
41	# top-level triple dirs → flatten this node's triple into the install dir, preserving bare-name
42	# resolution) ONLY on >= 0.13.2 (doyle, extract_release_archive); an older node cannot read the asset
43	# at all, so declaring the floor is honest, not a needless block. (Prior floor 0.9.0 was the
44	# adapter-agnostic seed/listen-by-host_binaries requirement, PREP-4 — still satisfied, just superseded.)

kind = "harness"
# 0.6.1 (2026-06-22): PACKAGING modernization — the release now ships a single MULTI-PLATFORM fat
# .spt (per-triple subdirs, ADR-0024 W1) which only spt-core >= 0.13.2 can extract, so the floor moves
# to 0.13.2 (see below) and /sptc:setup drops its now-redundant os-detection (`adapter add --release`
# is host-agnostic). The seam surface is unchanged from 0.6.0 (the v0.13.0 RUN-group seams
# [session.resume] + [message-idle-translation-binary], added in 0.6.0). Fresh tag, not an in-place
# v0.6.0 re-cut (a re-cut asset on the same tag is ambiguous — operator + doyle call).
version = "0.6.1"
# Floor BUMPED to 0.13.2 (2026-06-22, C / F-014 drop): the release packaging moved from the per-OS
# stopgap (`adapter.spt` = a windows-binary copy — F-014; broke on a linux host) to ONE fat .spt that
# bundles both x86_64 triples beside a shared manifest. spt-core extracts a fat archive (classify
# top-level triple dirs → flatten this node's triple into the install dir, preserving bare-name
# resolution) ONLY on >= 0.13.2 (doyle, extract_release_archive); an older node cannot read the asset
# at all, so declaring the floor is honest, not a needless block. (Prior floor 0.9.0 was the
# adapter-agnostic seed/listen-by-host_binaries requirement, PREP-4 — still satisfied, just superseded.)


Prompt:
<task-notification>
<task-id>bek0mdcz3</task-id>
<tool-use-id>toolu_01V88eYF1MnDvNdgNfGoGm23</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\bek0mdcz3.output</output-file>
<status>completed</status>
<summary>Background command "Cross-build linux idle-translate binary" completed (exit code 0)</summary>
</task-notification>


Edit

# v0.6.0 re-cut (a re-cut asset on the same tag is ambiguous — operator + doyle call).
version = "0.6.1"

# v0.6.0 re-cut (a re-cut asset on the same tag is ambiguous — operator + doyle call).
# 0.6.2 (2026-06-23): FIX — idle message delivery now submits with a discrete Enter keypress instead of
# a trailing carriage return in the injected text. A `\r` byte does NOT trigger Claude Code's message
# submission (newly discovered), so idle-delivered messages typed into the input box but never sent;
# the [message-idle-translation-binary] choreography now ends with {key:enter} after the text. Binary
# rebuild only — manifest seams + floor unchanged.
version = "0.6.2"


The file C:\Users\decid\Documents\projects\spt-claude-code\adapter\claude-spt.toml has been updated successfully. (file state is current in your context — no need to Read it back)

# v0.6.0 re-cut (a re-cut asset on the same tag is ambiguous — operator + doyle call).
version = "0.6.1"

# v0.6.0 re-cut (a re-cut asset on the same tag is ambiguous — operator + doyle call).
# 0.6.2 (2026-06-23): FIX — idle message delivery now submits with a discrete Enter keypress instead of
# a trailing carriage return in the injected text. A `\r` byte does NOT trigger Claude Code's message
# submission (newly discovered), so idle-delivered messages typed into the input box but never sent;
# the [message-idle-translation-binary] choreography now ends with {key:enter} after the text. Binary
# rebuild only — manifest seams + floor unchanged.
version = "0.6.2"

# is host-agnostic). The seam surface is unchanged from 0.6.0 (the v0.13.0 RUN-group seams

# [session.resume] + [message-idle-translation-binary], added in 0.6.0). Fresh tag, not an in-place

+# 0.6.2 (2026-06-23): FIX — idle message delivery now submits with a discrete Enter keypress instead of

+# a trailing carriage return in the injected text. A `\r` byte does NOT trigger Claude Code's message

+# submission (newly discovered), so idle-delivered messages typed into the input box but never sent;

+# the [message-idle-translation-binary] choreography now ends with {key:enter} after the text. Binary

+# rebuild only — manifest seams + floor unchanged.

# Floor BUMPED to 0.13.2 (2026-06-22, C / F-014 drop): the release packaging moved from the per-OS

# stopgap (`adapter.spt` = a windows-binary copy — F-014; broke on a linux host) to ONE fat .spt that

# bundles both x86_64 triples beside a shared manifest. spt-core extracts a fat archive (classify


Linux build done. Add changelog section:


## [0.6.2] - 2026-06-23

> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no
> change to commands or setup.

### Fixed
- **Messages delivered while you're idle now actually send.** Previously an incoming message
  from another agent could be typed into your Claude Code input box but never submitted — it just
  sat there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered
  messages arrive and run on their own. Your half-typed draft is still stashed and restored around
  the delivery, exactly as before.

## [0.6.1] - 2026-06-22


1	# Changelog
2
3	All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
4	The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
5	public release body verbatim.
6
7	> Each release below is keyed to the **adapter version of truth** (the version you see in
8	> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
9	> moves on its own slower schedule and may differ.
10
11	## [0.6.1] - 2026-06-22
12

# Changelog

All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
public release body verbatim.

> Each release below is keyed to the **adapter version of truth** (the version you see in
> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
> moves on its own slower schedule and may differ.

## [0.6.1] - 2026-06-22


## [0.6.2] - 2026-06-23

> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no
> change to commands or setup.

### Fixed
- **Messages delivered while you're idle now actually send.** Previously an incoming message from
  another agent could be typed into your Claude Code input box but never submitted — it just sat
  there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered
  messages arrive and run on their own. Your half-typed draft is still stashed and restored around
  the delivery, exactly as before.

## [0.6.1] - 2026-06-22


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

## [0.6.2] - 2026-06-23

> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no
> change to commands or setup.

### Fixed
- **Messages delivered while you're idle now actually send.** Previously an incoming message from
  another agent could be typed into your Claude Code input box but never submitted — it just sat
  there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered
  messages arrive and run on their own. Your half-typed draft is still stashed and restored around
  the delivery, exactly as before.

## [0.6.1] - 2026-06-22

# Changelog

All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
public release body verbatim.

> Each release below is keyed to the **adapter version of truth** (the version you see in
> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
> moves on its own slower schedule and may differ.

## [0.6.1] - 2026-06-22

> Requires spt-core **v0.13.2 or newer** (the version that can install a multi-platform package).
> Packaging-only release — no change to what the adapter does, only how it ships.

### Packaging
- **One download now covers every platform.** The adapter ships as a single multi-platform
  `adapter.spt` that bundles both the Windows and Linux builds beside one shared manifest; installing
  picks your platform automatically. This replaces the previous per-OS files and the Windows-only
  stopgap `adapter.spt` (which broke self-update on Linux), so `spt adapter add --release …` and
  `spt adapter update` now just work on either OS with no platform to pick. Requires spt-core
  **v0.13.2+** (the version that can read a multi-platform package).

### Changed
- **Setup is simpler.** `/sptc:setup` now activates the adapter with a plain
  `spt adapter add --release SaberMage/spt-claude-code` — no OS detection or asset selection, since
  the one package is host-agnostic.
- Adapter version of truth is now **0.6.1** (shown in `/sptc:version` and the release tag). The
  cplugs plugin skeleton bumps with the simplified setup body.

## [0.6.0] - 2026-06-22

> Requires spt-core **v0.13.0 or newer** for the two new capabilities below. On older spt-core
> they're simply inactive — nothing else changes, so this release is safe to take on any version.

### Added
- **Idle agents now receive your messages.** When an spt-hosted agent is sitting at its prompt
  (idle, not mid-task), a message sent to it now lands *in that session* — typed in and submitted
  for you — instead of going unseen until the agent next acted. If you were part-way through typing
  your own input, your draft is stashed first and restored right after, so an incoming message never
  eats what you were writing.
- **Resuming an agent brings back its conversation.** Relaunching a session now reloads its prior
  transcript instead of cold-starting a blank one, and the resume picker shows each session's project
  folder so you can tell them apart.

### Changed
- Adapter version of truth is now **0.6.0** (shown in `/sptc:version` and the release tag). The
  cplugs plugin skeleton is unchanged (still on its own slower track).

## [0.5.0] - 2026-06-19

### Added
- **Agents now know who they are the moment a session starts.** When a session is already
  reachable — you launched it through `cc`, or it cleared/compacted mid-run — it now opens with a
  short brief: its own agent name, a reminder that it's already live (so it won't try to re-arm),
  and how to message other agents (`spt send` and reply, plus `spt endpoint list` to see who's
  around). Previously a resumed or spt-launched session could start not knowing its own name.
- **Sessions that aren't reachable yet learn how to reach out.** On a machine that belongs to a
  subnet, a plain session with no perch now gets a one-line tip on reaching other agents with
  `spt ring` — without having to go reachable first. Solo machines with no peers see nothing.

## [0.4.0] - 2026-06-18

### Changed
- **Your live-agent companion now runs in a safe, limited mode.** When you go live with
  `/sptc:live`, the background companion that writes your context-resume notes works with a
  file-only toolset and a lighter, cheaper model. It is more contained and costs less to run,
  with no change to what you see or do.
- **Removed the experimental `:deep` profile.** `claude-spt:deep` was an unused placeholder. The
  one shipped profile overlay is `claude-spt:ccs` (routes sessions through `ccs`). If you ever
  selected `:deep`, use `:ccs` or the base adapter instead.

### Fixed
- **Sessions that spt starts for you no longer stall on a permission prompt.** When spt brings up
  a Claude Code session on your behalf (the live companion, and the spt-hosted launcher), it could
  hang waiting on an approval prompt that no one was there to answer. Those sessions now start
  cleanly.

## [0.3.0] - 2026-06-17

### Changed
- **Going live is now a single, seamless step.** A live agent comes up directly — no behind-the-scenes
  adapter selection, no chained setup commands. `/sptc:live` just brings your session up live, and
  `/sptc:ready` brings it up reachable-but-light; the difference is simply which one you run. This is
  full parity with the original live-agent experience.
- **Requires spt-core 0.9.0 or newer.** This release uses spt-core's newer automatic
  harness-resolution, so the adapter now needs spt-core **0.9.0+**. If you keep spt-core up to date
  (it self-updates), there's nothing to do; on an older spt-core the adapter will decline to install
  until you update.

### Fixed
- **Setup pins this adapter as your default.** `/sptc:setup` now records `claude-spt` as the active
  adapter for Claude Code, so going live or ready needs no extra flags.

## [0.2.1] - 2026-06-16

### Fixed
- **Going live shows a clean status, not raw machine output.** Starting a live agent previously
  leaked internal setup markers and tokens into the chat. `/sptc:live` now reports a single tidy
  summary — your agent id, online status, how other agents reach you, and how to reply — and
  nothing else.

### Changed
- **Self-contained live setup.** Bringing a session live no longer bounces you to a separate help
  topic; `/sptc:live` carries the full bringup itself, so going live works in one step.

## [0.2.0] - 2026-06-16

### Fixed
- **Live agents now reliably start their background companion.** Previously a live agent could come
  up marked "online" while its persistent companion silently failed to start, leaving it with no
  running context. The companion now launches and stays resident.

### Changed
- **Simpler setup — no manual PATH step.** The adapter's helper tools are now found automatically
  from where the adapter is installed; `/sptc:setup` no longer asks you to copy anything onto your
  PATH.
- **Automatic adapter updates.** The adapter now updates itself from this project's GitHub releases
  (`spt adapter update`) — logic and instruction changes reach you without a reinstall.

### Added
- **Linux support.** The adapter ships native **Windows and Linux** builds; `/sptc:setup` detects
  your OS and installs the right one.
- **ccs integration.** If you use [ccs](https://github.com/kaitranntt/ccs), `/sptc:setup` wires the
  shipped `claude-spt:ccs` profile so live/ready agents can run on your ccs backends instead of
  `claude`.
- **Private-network onboarding.** `/sptc:setup` now offers to create or join a private network
  (subnet) so you can pair machines and reach agents across them.

## [0.1.0] - 2026-06-15

First public release.

### Added
- **One-step setup — `/sptc:setup`.** Sets the plugin up and installs the Spacetime (`spt`)
  engine for you if it isn't already present, so messaging and live agents just work. Also offers
  to create or join a private network and to register the always-on background service.
- **Agent messaging.** `/sptc:ready` turns on your inbox so other agents can reach this session;
  `/sptc:send` messages another agent; `/sptc:list-agents` shows who is currently active.
- **Live agents.** `/sptc:live` upgrades the current session into a live agent with a persistent
  companion that keeps its own running context. `/sptc:commune` pushes a context update to that
  companion, and `/sptc:signoff` ends a live session cleanly with a final summary.
- **`/sptc:force-stop`** immediately tears down a listening or live agent when you need it gone.
- **Cross-machine networks — `/sptc:subnet`.** Pair machines into a private network (create,
  show the join code, or join an existing one) so your agents can reach each other across nodes.
- **`/sptc:version`** reports the running engine version.
- **Alternate model backends (advanced).** Ships a ready-made `ccs` profile template you can
  select to launch sessions through [ccs](https://github.com/kaitranntt/ccs) — useful for routing
  an agent to a different model or billing backend. You supply your own ccs configuration.
- **Invisible engine install.** Starting a session automatically installs the `spt` engine the
  first time if it is missing — no separate install step.

+> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no

+- **Messages delivered while you're idle now actually send.** Previously an incoming message from

+  another agent could be typed into your Claude Code input box but never submitted — it just sat

+  there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered

+  messages arrive and run on their own. Your half-typed draft is still stashed and restored around


Check version.md skill (manifest references `skills/version.md`):


Bash

grep -n "0.6.1\|0\.6\.1" adapter/skills/version.md 2>/dev/null; echo "---file---"; cat adapter/skills/version.md 2>/dev/null | head -30

Inspect version.md


---file---


find adapter -name version.md 2>/dev/null; echo "---strings?---"; ls adapter/strings/ 2>/dev/null; grep -rn "file = \"skills/version.md\"\|version.md" adapter/claude-spt.toml

Locate version.md referenced by manifest


adapter/strings/skills/version.md
---strings?---
briefs
skills
343:version = { file = "skills/version.md" }


1	# /sptc:version — operative instructions
2
3	Report the **version-of-truth** — the spt-core-tracked binary + adapter manifest, NOT the marketplace
4	`plugin.json` version.
5
6	1. `spt --version` — the binary.
7	2. `spt adapter list` — the registered `claude-spt` entry + active profile (what drives the session).
8	3. Note that the cplugs plugin version is intentionally not the version-of-truth.
9

# /sptc:version — operative instructions

Report the **version-of-truth** — the spt-core-tracked binary + adapter manifest, NOT the marketplace
`plugin.json` version.

1. `spt --version` — the binary.
2. `spt adapter list` — the registered `claude-spt` entry + active profile (what drives the session).
3. Note that the cplugs plugin version is intentionally not the version-of-truth.


version.md = dynamic, no bump needed. Verify new linux ELF, then pack fat adapter.spt (dry-run first):


file tools/cc-spt-idle-translate/target/x86_64-unknown-linux-gnu/release/cc-spt-idle-translate; echo "--- pack dry-run ---"; sh ci/publish/package-adapter.sh 2>&1 | tail -25

Verify ELF + dry-run pack


tools/cc-spt-idle-translate/target/x86_64-unknown-linux-gnu/release/cc-spt-idle-translate: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, stripped
--- pack dry-run ---
== validate manifest ==
ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json

== tool binaries ==
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-digest/target/release/claude-spt-digest.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-digest/target/x86_64-unknown-linux-gnu/release/claude-spt-digest
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-psyche/target/release/claude-spt-psyche.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-psyche/target/x86_64-unknown-linux-gnu/release/claude-spt-psyche
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/cc-spt-idle-translate/target/release/cc-spt-idle-translate.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/cc-spt-idle-translate/target/x86_64-unknown-linux-gnu/release/cc-spt-idle-translate

== plan (DRY-RUN) ==
manifest : /c/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml  ->  (root) manifest.toml          [min_spt_core 0.13.2]
strings  : /c/Users/decid/Documents/projects/spt-claude-code/adapter/strings/  ->  (root) strings/
win bins : -> x86_64-pc-windows-msvc/{claude-spt-digest,claude-spt-psyche,cc-spt-idle-translate}.exe
linux bins: -> x86_64-unknown-linux-gnu/{claude-spt-digest,claude-spt-psyche,cc-spt-idle-translate}
asset    : /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter.spt  (the single fat adapter.spt — auto-resolves the host triple on install)

DRY-RUN: nothing written. Re-run with --apply to write /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter.spt, then attach it as 'adapter.spt'
to a GitHub release on the monorepo. End users: 'spt adapter add --release SaberMage/spt-claude-code'
(default asset adapter.spt). Needs spt v0.13.2+. See docs/RELEASE-RUNBOOK.md.


Validation green. Run full gate suite before packing:


Run all CI gates


Command running in background with ID: b8kgb73mx. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b8kgb73mx.output. You will be notified when it completes. To check interim output, use Read on that file path.


<system-reminder>Warning: the file exists but is shorter than the provided offset (1). The file has 1 lines.</system-reminder>


Check gate output progress


0 C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b8kgb73mx.output


<task-notification>
<task-id>b8kgb73mx</task-id>
<tool-use-id>toolu_01XwQB1tL8VmKg4aLdLcG6UJ</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b8kgb73mx.output</output-file>
<status>completed</status>
<summary>Background command "Run all CI gates" completed (exit code 0)</summary>
</task-notification>


Gates running (tail buffers until exit). Waiting for completion.


until [ -s "C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b8kgb73mx.output" ]; do sleep 3; done; echo "=== gates output ==="; cat "C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b8kgb73mx.output" | tail -40

Wait for gates then show output


=== gates output ===

=== GATE: digest-extractor (cargo build + test) ===

running 12 tests
............
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  claude-spt-digest: cargo test + release build

=== GATE: psyche-runner (cargo build + test) ===

running 11 tests
...........
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  claude-spt-psyche: cargo test + release build

=== GATE: idle-translate (cargo build + test) ===

running 11 tests
...........
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  cc-spt-idle-translate: cargo test + release build

=== GATE: traceable-reqs check (requirement coverage) ===
ok  coverage green

=== GATE: skeleton-validate (cplugs installability) ===
ok  skeleton installable

=== GATE: manifest-schema ===
ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json

=== GATE: docs-drift ===
ok   mdbook build
ok   llms.txt in sync

=== RESULT: PASS ===


Prompt:
<task-notification>
<task-id>b8kgb73mx</task-id>
<tool-use-id>toolu_01XwQB1tL8VmKg4aLdLcG6UJ</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\e2a44c8e-df24-4cba-8ede-fff02a618f5e\tasks\b8kgb73mx.output</output-file>
<status>completed</status>
<summary>Background command "Run all CI gates" completed (exit code 0)</summary>
</task-notification>


All gates PASS. Pack fat adapter.spt:


sh ci/publish/package-adapter.sh --apply 2>&1 | tail -12; echo "--- asset ---"; ls -la dist/adapter.spt

Pack fat adapter.spt


manifest : /c/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml  ->  (root) manifest.toml          [min_spt_core 0.13.2]
strings  : /c/Users/decid/Documents/projects/spt-claude-code/adapter/strings/  ->  (root) strings/
win bins : -> x86_64-pc-windows-msvc/{claude-spt-digest,claude-spt-psyche,cc-spt-idle-translate}.exe
linux bins: -> x86_64-unknown-linux-gnu/{claude-spt-digest,claude-spt-psyche,cc-spt-idle-translate}
asset    : /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter.spt  (the single fat adapter.spt — auto-resolves the host triple on install)

== validate archive ==
ok   manifest.toml + strings/ at root; x86_64-pc-windows-msvc/ + x86_64-unknown-linux-gnu/ binaries present; no stray top-level dirs

WROTE /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter.spt (single fat adapter.spt). Next (operator): attach as 'adapter.spt' to a GitHub release
on SaberMage/spt-claude-code; end users 'spt adapter add --release SaberMage/spt-claude-code' (spt
v0.13.2+). The fat archive auto-resolves the host's binaries — no per-OS --asset needed.
--- asset ---
-rw-r--r-- 1 decid 197609 1063627 Jun 23 04:23 dist/adapter.spt


Asset written (1.06 MB). Commit source changes. Check status:


Git status


 M CHANGELOG.md
 M adapter/claude-spt.toml
 M ci/idle-translate/translate-proof-int.sh
 M tools/cc-spt-idle-translate/src/main.rs
 M traceable-reqs.toml


Clean set (dist gitignored). Commit:


git add CHANGELOG.md adapter/claude-spt.toml ci/idle-translate/translate-proof-int.sh tools/cc-spt-idle-translate/src/main.rs traceable-reqs.toml && git commit -m "$(cat <<'EOF'
fix(idle-translate): submit with discrete {key:enter}, not a trailing \r

A `\r` byte in injected text does NOT trigger Claude Code's message
submission — idle-delivered messages were typed into the input box but
never sent. Drop the trailing CR from the {text} command and append a
discrete {key:enter} (after a settle delay) as the submit. Envelope
CR/LF neutralization stays (verbatim {text} application). Updates the
6-step choreography, its module/REQ docs, cargo tests, and the
translate-proof int (now asserts {key:enter} + no trailing \r). Cut as
adapter v0.6.2.

Co-authored by: perri
EOF
)"

Commit the fix


[main 1f28061] fix(idle-translate): submit with discrete {key:enter}, not a trailing \r
 5 files changed, 84 insertions(+), 42 deletions(-)


git push origin main && git tag v0.6.2 && git push origin v0.6.2

Push main + tag v0.6.2


To https://github.com/SaberMage/spt-claude-code.git
   24c620a..1f28061  main -> main
To https://github.com/SaberMage/spt-claude-code.git
 * [new tag]         v0.6.2 -> v0.6.2


Pushed + tagged. Create GitHub release with the fat asset:


cat > /tmp/rel-0.6.2.md <<'EOF'
> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no
> change to commands or setup.

### Fixed
- **Messages delivered while you're idle now actually send.** Previously an incoming message from
  another agent could be typed into your Claude Code input box but never submitted — it just sat
  there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered
  messages arrive and run on their own. Your half-typed draft is still stashed and restored around
  the delivery, exactly as before.
EOF
gh release create v0.6.2 dist/adapter.spt --repo SaberMage/spt-claude-code --title "v0.6.2" --notes-file /tmp/rel-0.6.2.md 2>&1

Create GitHub release v0.6.2 with asset


Release published. Verify asset attached:


gh release view v0.6.2 --repo SaberMage/spt-claude-code --json assets,tagName,name -q '.tagName, .name, (.assets[] | "\(.name) \(.size)b")'

Verify release asset


v0.6.2
v0.6.2
adapter.spt 1063627b