Context
Tracked from PR #477 (#401 teachback gate) peer review. Finding F3 from review-backend-coder (task #19 HANDOFF).
Problem
teachback_gate._emit_state_transition_if_changed at teachback_gate.py:266-326 reads the full session journal per deny decision to dedup state-transition emissions. Architecture doc acknowledges ~5ms per call. Fires only on deny code path (allow fast-path unaffected).
Why it matters
At Phase 2 flip (advisory→blocking), sustained high-deny workflows will scale the journal-scan cost linearly with journal size. Current design is stateless (journal is source of truth) — fail-open friendly.
Proposed fix
Per-process in-memory dict caching task_id → last_emitted_state. Invalidate on process exit (hooks are short-lived anyway). ~15-25 LOC.
Why deferred from PR #477
Premature optimization per YAGNI. Phase 2 flip must come first; observation data from Phase 1 advisory mode will reveal whether deny rates justify the cache. Revisit after Phase 2 ships and 2-3 workflows generate real data.
Trade-off
Adding cache introduces state management to a currently-stateless hook. Fail-open friendliness is preserved (empty cache = journal scan fallback), but correctness surface grows.
Context
Tracked from PR #477 (#401 teachback gate) peer review. Finding F3 from review-backend-coder (task #19 HANDOFF).
Problem
teachback_gate._emit_state_transition_if_changedat teachback_gate.py:266-326 reads the full session journal per deny decision to dedup state-transition emissions. Architecture doc acknowledges ~5ms per call. Fires only on deny code path (allow fast-path unaffected).Why it matters
At Phase 2 flip (advisory→blocking), sustained high-deny workflows will scale the journal-scan cost linearly with journal size. Current design is stateless (journal is source of truth) — fail-open friendly.
Proposed fix
Per-process in-memory dict caching
task_id → last_emitted_state. Invalidate on process exit (hooks are short-lived anyway). ~15-25 LOC.Why deferred from PR #477
Premature optimization per YAGNI. Phase 2 flip must come first; observation data from Phase 1 advisory mode will reveal whether deny rates justify the cache. Revisit after Phase 2 ships and 2-3 workflows generate real data.
Trade-off
Adding cache introduces state management to a currently-stateless hook. Fail-open friendliness is preserved (empty cache = journal scan fallback), but correctness surface grows.