# v0.16.0 W6 (code half) — REQ-RC-IDENTITY + REQ-CLI-JSON int

Branch v0.16.0-update-arc @ HEAD 74aec80. doyle authors prose/CHANGELOG/release in parallel (do NOT touch MANIFEST.md, gh-pages, CHANGELOG, traceable-reqs.toml). Same gate bar. nextest only (never bare cargo test on Win). Pre-kill orphan workspace spt.exe.

## PART 1 — REQ-RC-IDENTITY (impl+unit+int), all in crates/spt/src/rc.rs

A reserved TOP status row via DECSTBM scroll-region (design B5). CONTROL attach only (controller=true; viewer path unchanged).

### Identity string (resolve ONCE at attach, in run_attach_inner before the pump)
`{subnet} : {endpoint_id} @ {node}` — right-aligned, CYAN. 
- subnet = the endpoint's HOME/PRIMARY subnet, "local" if none. Ground the source: read it from the perch info.json (spt_store::info::read_info — look for a home_subnet field) or the subnet store; if absent → "local".
- node = THIS machine's label: spt_store::hostlabel::os_hostname() (fall back to "" → omit @node if empty).
- endpoint_id = the attach target.
NO window title (no OSC) — CC owns the title.

### VT byte sequences (exact — verify these)
- Set scroll region reserving row 1: `ESC[2;{rows}r` (top margin = line 2, bottom = rows). This is the "own the top row" margin.
- Paint row 1 (repaint): `ESC7` (DECSC save cursor) + `ESC[1;1H` (row1 col1) + `ESC[2K` (clear line) + `ESC[1;{col}H` (col = rows-1-based right-align start = cols.saturating_sub(display_width(text)) + 1, clamp ≥1) + `ESC[36m` (cyan) + text + `ESC[0m` (reset) + `ESC8` (DECRC restore cursor).
- Restore on exit: `ESC[r` (reset scroll region to full) + `ESC[1;1H` `ESC[2K` (clear the row) — so the terminal is clean after detach.
- display width = text.chars().count() (ASCII status; fine).

### StatusRow (a pure, unit-testable struct)
```
struct StatusRow { text: String, rows: u16, cols: u16 }
impl StatusRow {
  fn assert_bytes(&self) -> Vec<u8>   // set margin + repaint
  fn repaint_bytes(&self) -> Vec<u8>  // save+row1+clear+rightalign+cyan+text+reset+restore
  fn restore_bytes() -> Vec<u8>       // ESC[r + clear row1
  fn update_size(&mut self, rows, cols)
}
```
Unit-test the exact byte shapes + right-align column math (short text vs text wider than cols → clamp to col 1) + restore.

### Row-shrink (reserve the row from the PTY)
The harness PTY must be told it has ONE FEWER row. In run_attach_inner the initial resize (~line 871 `send_attach_resize(rows, cols)`) and in pump the resize-on-change (~line 1051 `send_attach_resize(rows, cols)`) — when the status row is active (controller), send `rows.saturating_sub(1)` instead of `rows` (never 0). Keep cols unchanged. The StatusRow tracks the FULL `rows` (it paints on the real terminal's row 1) while the PTY gets rows-1.

### Re-assert scanner (output-scan, mirror MouseModeScanner carry-buffer pattern, rc.rs:79)
A struct that feeds harness output chunks and returns whether a re-assert trigger fired:
- ALT-SCREEN enter: private mode `?1049h` (also `?47h`/`?1047h` legacy) — reuse parse_decset_private (rc.rs:142) to detect set of those modes.
- DECSTBM RESET: a CSI `ESC[r` or `ESC[0r` or `ESC[;r` (no/zero params, final byte 'r', NO leading '?') — the harness clearing the scroll region. Add a small split-safe parser (carry partials like MouseModeScanner, bounded).
Carry a trailing partial across chunk boundaries (bounded, like MOUSE_DECSET_CARRY_MAX). Unit-test: detects ?1049h; detects ESC[r; split across two feeds; ignores unrelated CSI.

### Pump integration (pump fn, rc.rs:1006)
- Genericize `stdout: &mut std::io::Stdout` → `stdout: &mut impl std::io::Write` (so an int test can capture output). Update the call site (run_attach_inner) — std::io::stdout() still satisfies impl Write.
- Add a `status: Option<&mut StatusRow>` param (Some for controller, None for viewer/no-status).
- On pump START (before the loop, when status present): write status.assert_bytes() to stdout+flush.
- In the controller resize branch (~1047): when size changes, update status.update_size(rows, cols), send the SHRUNK resize (rows-1), and write status.assert_bytes() (re-assert margin+repaint after a resize).
- After writing harness output (~after line 1107 mouse_scanner.feed + stdout.write_all): feed the SAME bytes to the re-assert scanner; if it fired, write status.repaint_bytes()+flush (re-assert after alt-screen/DECSTBM-reset).
- On EVERY pump-exit path the caller restores: in run_attach_inner, before/with `drop(_raw)` (line 889), when status was active write StatusRow::restore_bytes() to stdout+flush.

### INT test [int->REQ-RC-IDENTITY]
A pump-level in-process test (NOT a live console — rc live is HITL; this drives the pump path with a fake Write + injected broker events). If pump can't be called in isolation easily, extract the status-emit logic so it IS testable: assert that (a) assert_bytes is emitted at start, (b) feeding an injected output chunk containing `ESC[?1049h` triggers a repaint emit, (c) restore is emitted on exit. Use a Vec<u8> as the impl Write sink. Tag [int->REQ-RC-IDENTITY]. If a true pump-drive needs a Brain mock that's infeasible, make the int a focused integration over the status state-machine (assert+scan-trigger+restore sequence through one helper) — still an int-stage exercise of the wired path, tagged accordingly. Prefer driving pump if a lightweight broker stub exists in the test module.

## PART 2 — REQ-CLI-JSON int (activate; the W4 carry)
A `--json` runtime-emit smoke over the daemon-reachable read/status subset, asserting PARSEABLE JSON (not human text) — closes the "captures the flag but doesn't route to print_json" gap.
- Find the existing live-test-daemon harness (crates/spt/tests/*e2e* — e.g. how daemon_e2e/livehost tests bring a daemon up under an isolated SPT_HOME and run the spt binary).
- For the daemon-reachable subset — whoami, how-to, adapter version, daemon status — run `spt <cmd> --json` as a subprocess (CARGO_BIN_EXE_spt) under the test home and assert stdout parses as JSON (serde_json::from_str::<serde_json::Value> Ok) AND is not the human render. (serde_json IS available to the spt integration tests.)
- Put it in a tests/ integration file (e.g. tests/json_emit.rs) so it runs in nextest (doyle's ×3 both-runner matrix). Tag [int->REQ-CLI-JSON]. Name the test fn clearly.
- GOTCHA (house lore): a manifest-less test exe whose NAME contains "update"/"install"/"setup" trips Windows installer-detection → os-error-740. Keep the test/file name clear of those.

## Workflow
1. Read rc.rs fully (MouseModeScanner ~79, run_attach_inner ~760, pump ~1006, send_attach_resize, RawGuard).
2. Implement Part 1 + Part 2.
3. cargo check --workspace --tests — fix all.
4. cargo clippy --workspace --all-targets — fix all warnings.
5. Do NOT run nextest/xtask, do NOT commit, do NOT touch traceable-reqs.toml/MANIFEST.md/gh-pages/CHANGELOG.
6. Report: the exact StatusRow byte sequences, the scanner triggers, the pump integration points, the row-shrink sites, the two int tests + names, and the final check/clippy status. Flag any VT decision or test-infra compromise.
