Tie nudge validity to the on-task stretch
A soft nudge advisory belongs to one continuous on-task stretch in an allowed app. Guard it with an on-task-stretch epoch (advanced on session reset and on any non-on-task-match observation) instead of a session-only counter, and clear the advisory on leaving the allowed app, so a stale 'Heads up' never resurfaces after a drift episode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -64,7 +64,7 @@ infrastructure. The `ai` package gains a third role (`Nudger`) but stays a
|
||||
**leaf package**: like `Coach` and `DriftJudge`, `Nudger` takes primitive
|
||||
strings, not `domain`/`evidence` types. `session.Controller` orchestrates the
|
||||
debounced async nudge with the *exact same discipline* as the M3 drift judge —
|
||||
debounce, session-generation guard, goroutine launched after releasing the
|
||||
debounce, on-task-stretch epoch guard, goroutine launched after releasing the
|
||||
mutex, `notify()` only with the mutex released, never fabricate on error.
|
||||
|
||||
### The `ai.Nudger` role
|
||||
@@ -109,15 +109,17 @@ New fields on `Controller` (all reset per session in `resetDriftLocked`):
|
||||
- `nudgeMessage string` — the current soft advisory ("" = none).
|
||||
- `lastNudgedAt time.Time` — debounce timestamp.
|
||||
|
||||
The nudge needs a session-identity guard, but `driftGen` cannot serve it:
|
||||
`driftGen` is bumped on every drift-judgment launch (not only at session
|
||||
boundaries), so an interleaved drift judgment within the same session would
|
||||
wrongly discard a valid in-flight nudge. The nudge therefore uses a dedicated
|
||||
`sessionGen` counter, incremented only in `resetDriftLocked`, so it survives an
|
||||
interleaved drift judgment yet is still discarded when the session ends. A nudge
|
||||
in flight when the user clicks "This is on task" / "Back to task" likewise
|
||||
remains valid — those actions do not change `sessionGen` and the nudge is about
|
||||
the session trajectory, not a single class.
|
||||
A soft nudge advisory belongs to one continuous on-task stretch in an allowed
|
||||
app, so the nudge is guarded by an on-task-stretch epoch (`nudgeEpoch`) rather
|
||||
than `driftGen` (which bumps on every drift-judgment launch). `nudgeEpoch`
|
||||
advances on session reset (`resetDriftLocked`) and whenever an observation is
|
||||
not a local on-task match — i.e. the stretch ended. A nudge captures the epoch
|
||||
at launch and applies its result only if the epoch is unchanged, so a nudge
|
||||
whose stretch has since ended (a drift episode or a session change) is discarded
|
||||
instead of surfacing stale. Leaving the allowed app additionally clears any
|
||||
already-set advisory. The net effect: the advisory auto-clears when the
|
||||
trajectory changes or recovers, and a stale "Heads up" never resurfaces after a
|
||||
drift episode.
|
||||
|
||||
New constants alongside the drift ones:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user