---
name: v0120-checkpoint-race-project-names
description: "adapter v0.12.0 SHIPPED — checkpoint clear/wake race fix (arm/fire split) + project-named sessions; binary-only, floor stays 0.20.0; on-node verify pending."
metadata: 
  node_type: memory
  type: project
  originSessionId: 10d16c7b-3bb4-4b80-9510-b45c2039b4b3
---

**adapter v0.12.0 SHIPPED 2026-07-03** (tag v0.12.0, GitHub release Latest, asset `adapter.spt`,
@6e8a37d). Two binary changes, NO manifest command/floor change (floor stays 0.20.0); cplugs
skeleton unchanged (stays 0.1.9 — no structural change). All gates green (123 cargo tests +
traceable-reqs + shell + manifest-schema + docs-drift). Packed archive dogfood-proven on-node
(translate-proof: checkpoint envelope emits clear-only ARM sequence).

**1. Checkpoint clear/wake race fix (REQ-HAZARD-CHECKPOINT-CLEAR-RACE, KH §2.4).** Bug: the
`!!checkpoint!!` macro submitted `/clear` + wake in ONE inject sequence with a fixed 500ms gap; a
single sequence CANNOT straddle CC's async `/clear` (re-runs SessionStart w/ network I/O, and the
whole sequence must `{commit}` within the 5s INJECT_COMMIT_DEADLINE) → the wake raced ahead and
landed in the OLD session (field-observed: followup hit first). Fix = SPLIT + sync on CC's own
clear-done signal:
- **ARM:** `{"checkpoint":"v1",…}` envelope → translate stashes wake in in-memory `pending_wake`
  + emits CLEAR-ONLY (`ctrl+s·50ms·/clear·enter·commit`). translate is one persistent process per
  endpoint, reaped on shutdown NOT on /clear, so the armed wake survives the clear.
- **FIRE:** SessionStart hook on a `clear` boundary UNCONDITIONALLY self-sends
  `{"checkpoint_fire":"v1"}`; translate emits WAKE-ONLY iff armed, else no-ops. State lives ONLY in
  translate memory (stateless hook, no marker file — every-clear-fire, binary decides).
- **`--force-native` on BOTH self-sends** (arm in handle_post_tool_use, fire in
  handle_session_start) — forces delivery through translate stdin where markers parse, never spooled
  to active-poll (the ENLYZEAM plain-text misdelivery mode from [[f023-f024-crossnode-delivery]]
  F-027 arc). Fires ONLY on `clear`, not `compact` (no checkpoint variant uses /compact — confirmed).
- Residual (accepted v1, doc'd in KH): if the checkpoint's own clear misses firing, the armed wake
  fires on the NEXT clear (self-limited). Design converged w/ operator over 2 rounds (memory-armed +
  IPC-via-existing-stdin-channel, NOT a bespoke hook→translate socket).
- Files: translate.rs (commands_for_clear/commands_for_wake/is_checkpoint_fire/dispatch_event +
  pending_wake in run(); CLEAR_DELAY_MS retired), hook.rs (checkpoint_fire_payload + the clear-fire
  block). int (translate-proof, single-event) proves ARM; unit `arm_then_fire_orders_clear_before_wake`
  proves the stateful ordering; full loop = live E2E dogfood (pending).

**2. Project-named sessions (REQ-DIST-RC-STARTUP extended).** Operator ask. Display
`-n "<id> @ <node> (<project>/)"`, RC `--remote-control <id>--<node>--<project-token>`. `<project>` =
launch-cwd basename (computed on-node; no `{project}` fill exists — same self-compute as node_name).
Display uses raw project (spaces/parens/slash fine — one argv element; -n confirmed flexible via
existing `@`/space usage). RC uses `rc_project_token` sanitizer (alphanumeric + `-`/`_`, other runs →
`_`, empty → drop suffix) since RC space-safety is unverifiable from public surface. Unknown project
⇒ drop suffix; unknown node ⇒ bare `<id>` degrade. launch.rs project_name/rc_project_token +
display_name/rc_name/cli_argv signatures gained the project param.

**Open / next:** on-node live verify of BOTH — the checkpoint arm→fire→wake E2E on a real
spt-hosted CC (the deferred harness-glue E2E) AND the `-n`/RC project naming appearing in a live PTY
prompt box. This node (perri) NOT self-updated (mid-session adapter swap on the live perch risks the
v0.9.2-class skew-brick — [[v092-onnode-update-skew-brick]]); update on a fresh/operator-gated node.
Published for consumption; nodes update on their own schedule.
