# Upgrade Guide — vNEXT

<!-- assembled-by: assemble-next-md -->
<!-- bump: patch -->

## What Changed

- **The silent-freeze watchdog now corroborates before it cries wolf.** The
  `ActiveWorkSilenceSentinel` used to escalate ("X went quiet… I tried a nudge…
  want me to dig in?") on nothing more than "the terminal screen stopped
  repainting for 15 min" — which a long build, a running sub-agent, or any long
  tool call looks exactly like. It now re-captures the session's LIVE frame and
  suppresses the alert when the frame still shows an active-work indicator (strict
  "generating now": spinner / "esc to interrupt" / running — never the broad
  scrollback-persistent tool names), a sub-agent is live, or it's a clean idle
  prompt. The whole corroboration path **fails closed** (any capture/check error →
  suppress, never a false "it's stuck"). Threshold raised 15m→30m. A 90-minute
  frozen-byte-identical-frame backstop (A5) still surfaces a genuine mid-tool hang,
  extra-hedged. The escalation wording is now evidence-with-uncertainty ("…hasn't
  changed in N min and a nudge didn't wake it — it may be stuck, or on a long task
  I can't see into. Want me to check?"), never an asserted "it's stuck."
- **The promise beacon (⏳) goes silent unless there's something true to say.** The
  zero-information "still on it, no new output" heartbeat is suppressed by default
  — it carried no signal (just a tmux hash that didn't change) and kept firing on
  tasks the user had moved on from. The beacon now speaks only on genuine new
  progress (LLM-summarized), deadline pressure (B1a), a sparse once-per-60m
  liveness line so long tasks aren't fully dark (B1b), or a one-shot turn-finished
  close-out (B2: after 3 idle-frame checks, "that work's session has wrapped —
  pick it back up, or close this out?" then auto-pause). Base cadence relaxed
  10m→20m. `heartbeatCount` now counts only messages actually sent.
- **Both remain pure SIGNALS** — they decide only whether to notify you, never to
  block or gate an action. Every new check is used to *suppress/hedge* the
  notification (the safe direction), in line with the Signal-vs-Authority standard.
- **Observability:** a full suppression-vs-fire funnel lands in
  `logs/sentinel-events.jsonl` and `/metrics/features` (`active-work-silence`,
  `promise-beacon`), so the false-positive-rate drop is measurable, not assumed.
- **Migration parity:** the five operator-tunable / rollback keys ship in
  `ConfigDefaults` (SSOT) and an existence-checked, idempotent, audited
  `PostUpdateMigrator` step (`honest-progress-messaging-defaults`) backfills them
  to deployed agents at the paths the runtime actually reads. CLAUDE.md template +
  content-sniffed `migrateClaudeMd` entry so every agent learns the new behavior
  and how to tune/disable it.

## What to Tell Your User

Two kinds of background messages that were noisy and often wrong are now honest.
The "a session went quiet — want me to dig in?" alert used to fire on any task that
was simply taking a while (a long build looks identical to a frozen screen); it now
checks whether the session is genuinely still working before saying anything, and
when it's truly unsure it says so plainly instead of declaring "it's stuck." The
recurring ⏳ "still on it, no new output" pings are gone by default — the agent only
pings you when there's real progress, a deadline is close, or a task has actually
wrapped up and it wants to close the loop. You'll see far fewer of both, and the ones
you do see will be true. Everything is tunable (or fully restorable to the old
behavior) via config; the agent's CLAUDE.md explains the knobs.

## Summary of New Capabilities

- `ActiveWorkSilenceSentinel` live-frame corroboration (suppress on active-work
  indicator / live sub-agent / clean idle prompt; fail-closed; 90m frozen-indicator
  backstop). Tunable: `monitoring.activeWorkSilenceSentinel.{silenceThresholdMs,activeWorkMaxFrozenIndicatorMs,enabled}`.
- `PromiseBeacon` silent-unless-true behavior (suppress unchanged; sparse liveness;
  deadline-pressure line; turn-finished close-out). Tunable: `promiseBeacon.{suppressUnchangedHeartbeats,beaconLivenessIntervalMs,turnFinishedCloseoutChecks}`.
- Suppression/fire observability funnel via `logs/sentinel-events.jsonl` and
  `/metrics/features` under `active-work-silence` and `promise-beacon`.

## Evidence

- `tests/unit/ActiveWorkSilenceSentinel-honest.test.ts` (7) — A1 suppression on a
  live "generating now" frame, A2 sub-agent-live + clean-idle suppression, A5
  frozen-indicator backstop fire, fail-closed on every corroboration error path,
  legacy no-capture back-compat.
- `tests/unit/PromiseBeacon.test.ts` (5) + `PromiseBeacon-ux-fixes.test.ts` (6) —
  B1 suppress-when-unchanged (default) + the `suppressUnchangedHeartbeats:false`
  legacy-rollback, cadence-anchor advances on suppressed ticks, resume re-arm.
- `tests/unit/PostUpdateMigrator-honestProgressMessaging.test.ts` (7) — ConfigDefaults
  SSOT presence, fresh backfill at the runtime-read paths, operator-override
  preservation (incl. the rollback flag), run-once idempotency, no-config/corrupt-config
  paths, and the CLAUDE.md awareness append.
- Build clean; full lint suite green (incl. dev-agent-dark-gate, guard-manifest,
  llm-attribution). Spec converged + approved: `docs/specs/HONEST-PROGRESS-MESSAGING-SPEC.md`.
- Side-effects review (with independent second-pass concur):
  `upgrades/side-effects/honest-progress-messaging.md`.
