|
| 1 | +# Codi in-message status header — no-fabricate / no-silent-drop |
| 2 | + |
| 3 | +status: design |
| 4 | +slug: codi-status-no-fabricate |
| 5 | +ticket: XOS-146 |
| 6 | +repo: ~/aiprojects/prompt-engineering-in-action (plugins/co-dialectic) |
| 7 | + |
| 8 | +## What |
| 9 | + |
| 10 | +Make codi's **in-message** status header (`{icon} {Persona} · X% · Cal: Y% · [HH:MM]`, |
| 11 | +Protocol 1) impossible to fabricate or silently drop. XOS-141 fixed the **terminal** |
| 12 | +statusLine (hook-owned, fail-loud); this fixes the **other** surface — the line the |
| 13 | +model types at the top of every response, which is currently model-authored prose with |
| 14 | +no grounded source. |
| 15 | + |
| 16 | +## Why |
| 17 | + |
| 18 | +2026-06-29, live proof: codi was dead the whole session (`~/.codialectic/state.json` |
| 19 | +frozen, `last_protocol_ts` null, cache stale), yet the model TYPED `91% · Cal 95%` |
| 20 | +headers with invented numbers, then silently dropped the header partway through, and |
| 21 | +mistook its own performance for codi running. The terminal statusLine being reliable |
| 22 | +does not help — the in-message header the user actually reads can still lie or vanish. |
| 23 | +Anchors: Zero-Hallucination (don't fabricate the score), verification-before-completion, |
| 24 | +[[feedback_dont_perform_a_dead_feature]]. |
| 25 | + |
| 26 | +## Scope |
| 27 | + |
| 28 | +- **In:** |
| 29 | + - Contract rewrite in `SKILL.md` + `SKILL-lite.md` Protocol 1: a score `%`/`Cal:%` |
| 30 | + may appear ONLY when codi is live (a fresh `last_protocol_ts` heartbeat was written |
| 31 | + THIS turn AND the rendered numbers equal the `last_score`/`last_cal` just written). |
| 32 | + When DEGRADED (stale/absent `last_protocol_ts`, or `installed_version` skew) → header |
| 33 | + MUST read `⚠ Codi DEGRADED · [HH:MM]` with NO numbers. Never invent / recall a score |
| 34 | + from re-injected prose. `[HH:MM]` is always OS-grounded (Protocol 17). |
| 35 | + - NEW deterministic **Stop hook** `hooks/status-liveness-check.ts` (wired in |
| 36 | + `hooks.json`, following the existing `peer-parity-nudge.ts` Stop-hook pattern): |
| 37 | + reads the model's final assistant message + `state.json`, computes liveness, and |
| 38 | + emits a LOUD deterministic nudge on any of the three failure modes (below). This is |
| 39 | + the codification — replaces "the agent notices the drop" with a hook that always |
| 40 | + checks. Reuses the SAME freshness rule as `statusline.sh` (XOS-141) so the two |
| 41 | + surfaces can never disagree (extract/share the freshness predicate; if bash↔TS |
| 42 | + sharing is impractical, replicate the exact same threshold + skew rule and assert |
| 43 | + it in a test). |
| 44 | + - Tighten the `user-prompt-submit.ts` survival reminder text to state the contract |
| 45 | + explicitly ("a score requires a fresh heartbeat you write THIS turn; else |
| 46 | + `⚠ Codi DEGRADED`, never a %"). |
| 47 | + - Version bump 4.27.0 across all 4 sources + root CHANGELOG + SKILL-lite. |
| 48 | +- **Out:** |
| 49 | + - Deprecating the in-message header entirely in favor of the terminal statusLine |
| 50 | + (ticket direction 4) — bigger UX call; the contract + Stop hook make the in-message |
| 51 | + surface trustworthy without removing it. Noted as a future option, not this ticket. |
| 52 | + - Any change to the XOS-141 `statusline.sh` behavior (only share its freshness rule). |
| 53 | + - Any phone-home / telemetry. Local-only, as always. |
| 54 | + |
| 55 | +## Acceptance criteria |
| 56 | + |
| 57 | +- [ ] In-message header shows `X%`/`Cal: Y%` ONLY when `last_protocol_ts` is fresh AND |
| 58 | + the rendered numbers equal `state.json` `last_score`/`last_cal`. |
| 59 | +- [ ] When codi is DEGRADED (stale/absent `last_protocol_ts` or version skew), the |
| 60 | + correct header is `⚠ Codi DEGRADED · [HH:MM]` with no numbers — documented in |
| 61 | + Protocol 1 (SKILL.md + SKILL-lite.md). |
| 62 | +- [ ] `hooks/status-liveness-check.ts` (Stop hook) deterministically emits a loud nudge on: |
| 63 | + (a) **fabrication** — a numeric score present in the response but state NOT fresh; |
| 64 | + (b) **inconsistent** — rendered number ≠ `state.json` `last_score`/`last_cal`; |
| 65 | + (c) **silent drop** — state IS fresh but the response has no status line. |
| 66 | + No nudge when the DEGRADED header is correct, or when fresh + numbers match. |
| 67 | +- [ ] The Stop hook fails safe: missing/corrupt `state.json` → treat as DEGRADED |
| 68 | + (expect DEGRADED header), never crash. |
| 69 | +- [ ] Freshness predicate matches `statusline.sh` (XOS-141); a test asserts they agree. |
| 70 | +- [ ] Version 4.27.0 in: `.claude-plugin/marketplace.json`, `plugins/co-dialectic/.claude-plugin/plugin.json`, |
| 71 | + `plugins/co-dialectic/skills/co-dialectic/SKILL.md` `**Version:**`, `install.sh` `VERSION=`, |
| 72 | + and `SKILL-lite.md` `**Version:**`; root `CHANGELOG.md` `## [4.27.0]` entry. |
| 73 | +- [ ] `test-plugin.sh` validate green; `gh pr checks` green. |
| 74 | + |
| 75 | +## Test plan |
| 76 | + |
| 77 | +- [ ] `bun test` unit tests for `status-liveness-check.ts` covering the 4 cases |
| 78 | + (fresh+match → no nudge; stale+number → fabrication nudge; mismatch → inconsistent |
| 79 | + nudge; fresh+no-line → silent-drop nudge) + fail-safe on missing state.json. |
| 80 | +- [ ] Freshness-parity test: same (last_protocol_ts, now, installed_version) inputs → |
| 81 | + same live/degraded verdict as the statusline.sh rule. |
| 82 | +- [ ] CLI render smoke (Stage 5.5 plugin/CLI mode): hooks load + run without stack traces |
| 83 | + at narrow + standard widths; the new hook handles absent state.json gracefully. |
| 84 | + |
| 85 | +## Rollback |
| 86 | + |
| 87 | +Revert the PR; remove the `status-liveness-check` entry from `hooks.json`. Additive hook |
| 88 | ++ doc edits in one plugin; no data migration; low blast radius. The terminal statusLine |
| 89 | +(XOS-141) is unaffected, so liveness signalling never regresses below today. |
0 commit comments