Summary
A Spacedock first officer whose spacedock status --boot output lands more than 512 KB into its transcript gets no role badge and no stage strip. The session renders as an ordinary one and the ?next=true project panel reads This project declares no workflow.
transcript_boot() reads only the first spacedock_boot_scan_bytes (512,000) of the transcript. Its docstring states the assumption:
Boot output is written once at session start and never rewritten, so the scan is amortised
That holds when the FO agent is launched directly. It does not hold when a normal session adopts the first-officer role mid-conversation by loading the skill — boot then runs wherever the conversation happened to reach, and on a long session that is far past the window.
Verified against main at d4e3904 (v0.16.0).
Affected code
cargento/skills/cargento/cargento_runtime/spacedock.py
transcript_boot() — reads handle.read(config.spacedock_boot_scan_bytes) and nothing else. There is no tail read and no incremental resume, so an envelope past the window is unreachable for the life of the session.
boot_records() / workflow_dirs() — both correct; they simply never receive the bytes.
cargento/skills/cargento/cargento_runtime/config.py
spacedock_boot_scan_bytes=512_000 (line 468). No CLI flag and no environment override, so an operator who hits this has nothing to turn.
Deterministic reproduction
Two transcripts carrying the same valid boot envelope, differing only in where it sits:
import json, os, sys, tempfile, time
from pathlib import Path
from cargento_runtime import config as C, spacedock as S, state as runtime_state
cfg = C.build_runtime_config(environ=os.environ, platform_name=sys.platform,
os_name=os.name, launcher_path=Path("server.py").resolve())
st = runtime_state.RuntimeState(config=cfg, server_started=time.time())
def rec(t):
return json.dumps({"type": "user", "message": {"role": "user",
"content": [{"type": "tool_result", "tool_use_id": "t", "content": t}]}})
env = json.dumps({"command": "boot",
"definition_dir": "/tmp/wf/docs/dev",
"entity_dir": "/tmp/wf/docs/dev/.spacedock-state"})
deep = ("\n".join([rec("x" * 20_000)] * 40 + [rec("boot:\n" + env)]) + "\n").encode()
shallow = (rec("boot:\n" + env) + "\n").encode()
with tempfile.TemporaryDirectory() as d:
for name, blob in (("deep", deep), ("shallow", shallow)):
p = os.path.join(d, name + ".jsonl")
open(p, "wb").write(blob)
r = S.transcript_boot(cfg, st, p)
print(name, len(blob), len(r), S.workflow_dirs(cfg, r))
Actual:
deep 805006 0 []
shallow 246 1 ['/tmp/wf/docs/dev']
Expected: both find the envelope.
boot_records() on the same bytes confirms the parser is not at fault — it is purely the slice it is handed:
boot_records(cfg, blob[:512_000]) -> 0 envelopes
boot_records(cfg, blob) -> 1 envelope
Observed in the wild
A real first-officer transcript on this machine, paths generalised:
|
|
| transcript |
~/.claude/projects/<encoded>/<session>.jsonl, 6,995,235 bytes |
| boot command |
${SPACEDOCK_BIN:-spacedock} status --boot --identify --json, at record 1927 of 4412 |
earliest definition_dir byte offset |
3,306,740 (4.7% → 47% of the file; 6.5× past the window) |
transcript_boot() result |
[] |
boot_records() on the whole file |
1 envelope, definition_dir = <workflow-repo>/docs/dev |
So the data is well-formed and present; it is only out of reach.
Suggested fixes
Rough order of cost:
- Also scan the tail. Cheapest and it matches how the rest of the collector already treats transcripts (head + tail). Boot is never rewritten, so a head-plus-tail union has no correctness cost, but it still misses an envelope stranded in the middle of a very long session — as the one above is.
- Incremental whole-file scan, the way the turn scanner already does it (
SKILL.md: "JSONL harnesses use an incremental whole-file scanner"). Reuses an existing pattern, is complete, and the existing (path, size) cache key already gives the resume point. Preferred if the scanner is reusable.
- Cheap prefilter, then targeted decode.
definition_dir is a rare literal; one pass of bytes.find over the file to locate candidate offsets, then decode only those lines. Bounded cost on a multi-MB transcript without holding it all in memory.
- Expose the window (
--spacedock-boot-scan-bytes, or an env var). Not a fix, but it would have let this be diagnosed and worked around locally instead of by reading the source.
Notes
- Filing this as the scan-window bug alone. Two adjacent behaviours came up while tracking it down and are not claimed as defects here:
- A mid-session FO has no
agentSetting, so it gets no role badge even if the strip is fixed. That looks like intended behaviour per SKILL.md, but it means the two signals fail together for exactly this launch shape.
definition_dir can name a different repo than the session's cwd, so the strip would attach to the label of the FO's working directory rather than the workflow's. Worth a separate look at whether that is the intended attribution.
- Nothing here needs the ask lane, usage fetch, or events;
--no-spacedock is unrelated.
Summary
A Spacedock first officer whose
spacedock status --bootoutput lands more than 512 KB into its transcript gets no role badge and no stage strip. The session renders as an ordinary one and the?next=trueproject panel readsThis project declares no workflow.transcript_boot()reads only the firstspacedock_boot_scan_bytes(512,000) of the transcript. Its docstring states the assumption:That holds when the FO agent is launched directly. It does not hold when a normal session adopts the first-officer role mid-conversation by loading the skill — boot then runs wherever the conversation happened to reach, and on a long session that is far past the window.
Verified against
mainatd4e3904(v0.16.0).Affected code
cargento/skills/cargento/cargento_runtime/spacedock.pytranscript_boot()— readshandle.read(config.spacedock_boot_scan_bytes)and nothing else. There is no tail read and no incremental resume, so an envelope past the window is unreachable for the life of the session.boot_records()/workflow_dirs()— both correct; they simply never receive the bytes.cargento/skills/cargento/cargento_runtime/config.pyspacedock_boot_scan_bytes=512_000(line 468). No CLI flag and no environment override, so an operator who hits this has nothing to turn.Deterministic reproduction
Two transcripts carrying the same valid boot envelope, differing only in where it sits:
Actual:
Expected: both find the envelope.
boot_records()on the same bytes confirms the parser is not at fault — it is purely the slice it is handed:Observed in the wild
A real first-officer transcript on this machine, paths generalised:
~/.claude/projects/<encoded>/<session>.jsonl, 6,995,235 bytes${SPACEDOCK_BIN:-spacedock} status --boot --identify --json, at record 1927 of 4412definition_dirbyte offsettranscript_boot()result[]boot_records()on the whole filedefinition_dir = <workflow-repo>/docs/devSo the data is well-formed and present; it is only out of reach.
Suggested fixes
Rough order of cost:
SKILL.md: "JSONL harnesses use an incremental whole-file scanner"). Reuses an existing pattern, is complete, and the existing(path, size)cache key already gives the resume point. Preferred if the scanner is reusable.definition_diris a rare literal; one pass ofbytes.findover the file to locate candidate offsets, then decode only those lines. Bounded cost on a multi-MB transcript without holding it all in memory.--spacedock-boot-scan-bytes, or an env var). Not a fix, but it would have let this be diagnosed and worked around locally instead of by reading the source.Notes
agentSetting, so it gets no role badge even if the strip is fixed. That looks like intended behaviour perSKILL.md, but it means the two signals fail together for exactly this launch shape.definition_dircan name a different repo than the session'scwd, so the strip would attach to the label of the FO's working directory rather than the workflow's. Worth a separate look at whether that is the intended attribution.--no-spacedockis unrelated.