feat(continual-learning): activate negative-feedback loop (RSI steps 1-2)
Step 1 — policy.ts shouldIntervene: suppress on a prior dismissal alone. The former `&& !priorOutcome.wasRealCollision` term was dead code (outcomes record wasRealCollision hardcoded true), so the 85 real dismissals in Atlas were never used. Now a prior accepted===false suppresses the next identical nudge. Spec: continual-learning/policy.md:41, spec.md:163. Step 2 — podman.ts handle: only escalate severity to 'critical' (the spoken alert trigger) when the recalled prior was an accepted *real* collision, instead of blanket-escalating every recall. Surfaces learned routing in preferredAction; stops dismissed/false priors over-escalating to voice. Spec: continual-learning/policy.md:62-63, plan.md:66. Documentation-first: adds PLAN.md section 8 "P0.5 - RSI negative-feedback activation" with both rungs + follow-ups. No schema change. backend typecheck passes. Independent of the MongoDB-cleanup handoff (Codex). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -100,7 +100,14 @@ export class PodMan {
|
|||||||
if (this.activeConflicts.has(key)) return; // single-shot: already voiced, still unresolved
|
if (this.activeConflicts.has(key)) return; // single-shot: already voiced, still unresolved
|
||||||
|
|
||||||
const prior = await recallSimilar(collision); // Loop A: exact/vector recall raises confidence
|
const prior = await recallSimilar(collision); // Loop A: exact/vector recall raises confidence
|
||||||
if (prior) collision.severity = 'critical';
|
// Only escalate to critical (which triggers the spoken alert) when the
|
||||||
|
// recalled prior was an *accepted real* collision. Blanket-escalating every
|
||||||
|
// recall — including dismissed/false-positive priors — masked the learned
|
||||||
|
// routing in preferredAction and made recalled noise scream "CRITICAL".
|
||||||
|
// (RSI Step 2 — continual-learning/policy.md:62-63, plan.md:66)
|
||||||
|
if (prior?.priorOutcome?.accepted && prior?.priorOutcome?.wasRealCollision) {
|
||||||
|
collision.severity = 'critical';
|
||||||
|
}
|
||||||
if (!shouldIntervene(collision, prior)) return; // Loop B: policy gate
|
if (!shouldIntervene(collision, prior)) return; // Loop B: policy gate
|
||||||
|
|
||||||
this.activeConflicts.add(key); // claim now we're alerting; re-armed in onScreenFrame on resolution
|
this.activeConflicts.add(key); // claim now we're alerting; re-armed in onScreenFrame on resolution
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ export function shouldIntervene(collision: Collision, prior: RecalledCollision |
|
|||||||
if (collision.severity === 'info') return false;
|
if (collision.severity === 'info') return false;
|
||||||
|
|
||||||
const priorOutcome = prior?.priorOutcome;
|
const priorOutcome = prior?.priorOutcome;
|
||||||
if (priorOutcome && !priorOutcome.accepted && !priorOutcome.wasRealCollision) return false;
|
// Suppress when the identical prior was dismissed (accepted === false). The
|
||||||
|
// former `&& !priorOutcome.wasRealCollision` term was dead code: outcomes are
|
||||||
|
// recorded with wasRealCollision hardcoded true, so the gate never fired and
|
||||||
|
// the 85 real dismissals in Atlas were ignored. Dismissals are the negative
|
||||||
|
// signal per continual-learning/policy.md:41 + spec.md:163. (RSI Step 1)
|
||||||
|
if (priorOutcome && !priorOutcome.accepted) return false;
|
||||||
|
|
||||||
const cooldown = cooldownMs();
|
const cooldown = cooldownMs();
|
||||||
const last = lastNudgeByPod.get(collision.podId) ?? 0;
|
const last = lastNudgeByPod.get(collision.podId) ?? 0;
|
||||||
|
|||||||
@@ -484,6 +484,37 @@ artifact.
|
|||||||
recorded backup.
|
recorded backup.
|
||||||
- Keep backup video on a separate device.
|
- Keep backup video on a separate device.
|
||||||
|
|
||||||
|
### P0.5 - RSI negative-feedback activation (continual-learning)
|
||||||
|
|
||||||
|
The continual-learning loop records outcomes but never feeds the negative
|
||||||
|
signal back. Live Atlas (2026-06-28): `outcomes` = 22 accepted / 85 dismissed,
|
||||||
|
yet `wasRealCollision` is `true` in 107/107 (hardcoded), so the suppression
|
||||||
|
gate is dead and dismissals are unused. These two rungs activate the loop with
|
||||||
|
no schema change. Owner: RSI track. Independent of the MongoDB-cleanup handoff.
|
||||||
|
|
||||||
|
1. **Step 1 - suppress on prior dismissal alone** ✅
|
||||||
|
- `backend/src/memory/policy.ts` `shouldIntervene`: remove the dead
|
||||||
|
`&& !priorOutcome.wasRealCollision` term so a prior `accepted === false`
|
||||||
|
suppresses the next identical-signature nudge.
|
||||||
|
- Spec: `docs/continual-learning/policy.md:41` (dismissed = negative signal),
|
||||||
|
`spec.md:163` (dismissals adapt suppression).
|
||||||
|
- Caveat: recall is single-shot most-recent (`memory/vectors.ts`), so this is
|
||||||
|
"last-outcome-wins" until Step 3 (derive `wasRealCollision`) lands.
|
||||||
|
|
||||||
|
2. **Step 2 - gate the recall severity escalation** ✅
|
||||||
|
- `backend/src/agent/podman.ts` `handle`: only force `severity = 'critical'`
|
||||||
|
when the recalled prior was an accepted *real* collision, instead of
|
||||||
|
blanket-escalating every recall. Surfaces the learned routing in
|
||||||
|
`preferredAction`; stops dismissed/false priors over-escalating to voice.
|
||||||
|
- Spec: `docs/continual-learning/policy.md:62-63` (prefer prior accepted
|
||||||
|
kind), `plan.md:66` (second similar event behaves differently).
|
||||||
|
|
||||||
|
Follow-ups (separate rungs, not in this change): Step 3 derive
|
||||||
|
`wasRealCollision` from git overlap; Step 4-5 `strategy_versions` +
|
||||||
|
Gemini-proposed `LearningProposal` slice; seed a clean demo pod with a repeated
|
||||||
|
dismissed signature (the historic 85 dismissals are orphaned — `collisionId`
|
||||||
|
resolves to no collision — so they cannot drive the demo verifier).
|
||||||
|
|
||||||
### P1 - polish the money moment
|
### P1 - polish the money moment
|
||||||
|
|
||||||
- Add visible live inference captions in the PWA.
|
- Add visible live inference captions in the PWA.
|
||||||
|
|||||||
Reference in New Issue
Block a user