diff --git a/docs/superpowers/specs/2026-05-31-m3.5-semantic-nudge-design.md b/docs/superpowers/specs/2026-05-31-m3.5-semantic-nudge-design.md index 0eecfe7..f4d3220 100644 --- a/docs/superpowers/specs/2026-05-31-m3.5-semantic-nudge-design.md +++ b/docs/superpowers/specs/2026-05-31-m3.5-semantic-nudge-design.md @@ -145,14 +145,14 @@ both. nudge eligibility: - `c.nudge != nil`, runtime is Active, `len(c.recentTitles) >= 2`, and `lastNudgedAt` is zero or `now.Sub(lastNudgedAt) >= nudgeDebounce`. - - If eligible: stamp `lastNudgedAt = now`, capture `gen := c.driftGen`, the - commitment string (same `NextAction — SuccessCondition` form as the drift - judge), and a **copy** of `recentTitles`; return the nudge closure. + - If eligible: stamp `lastNudgedAt = now`, capture `epoch := c.nudgeEpoch`, + the commitment string (same `NextAction — SuccessCondition` form as the + drift judge), and a **copy** of `recentTitles`; return the nudge closure. - If not eligible: return `nil` (unchanged behavior). 3. **Nudge closure** (runs in the goroutine): - Calls `nudge.Nudge(ctx, commitment, titles)` under a `nudgeTimeout` context. - - Re-acquires the lock; if `gen != c.driftGen || c.runtimeState != + - Re-acquires the lock; if `epoch != c.nudgeEpoch || c.runtimeState != RuntimeActive` → stale, return. - On error → log, leave `nudgeMessage` unchanged (no fabrication), unlock, return. (`lastNudgedAt` stays set, so a failed call does not immediately @@ -209,8 +209,8 @@ avoid stale interrupts after a restart). nudge never fabricates a concern, mirroring the drift judge's "never fabricate drift" rule. - A reasonless concern from the model degrades to silence (see parsing). -- Stale results (session ended or restarted mid-call) are discarded by the - `driftGen` guard. +- Stale results (the on-task stretch ended via a drift episode, or the session + ended/restarted mid-call) are discarded by the `nudgeEpoch` guard. ## Testing