0d69f82139
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>
43 lines
1.7 KiB
TypeScript
43 lines
1.7 KiB
TypeScript
import type { Collision, SuggestedActionKind } from '@podman/shared';
|
|
import type { RecalledCollision } from './vectors.js';
|
|
|
|
const lastNudgeByPod = new Map<string, number>();
|
|
|
|
function cooldownMs(): number {
|
|
return Number(process.env.NUDGE_COOLDOWN_MS ?? '180000');
|
|
}
|
|
|
|
/** Policy gate: combines severity, exact recall outcomes, and per-pod cooldown. */
|
|
export function shouldIntervene(collision: Collision, prior: RecalledCollision | null): boolean {
|
|
if (collision.severity === 'info') return false;
|
|
|
|
const priorOutcome = prior?.priorOutcome;
|
|
// 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 last = lastNudgeByPod.get(collision.podId) ?? 0;
|
|
if (cooldown > 0 && Date.now() - last < cooldown) {
|
|
return false;
|
|
}
|
|
|
|
lastNudgeByPod.set(collision.podId, Date.now());
|
|
return true;
|
|
}
|
|
|
|
/** Preferred action selection based on collision severity and prior accepted actions. */
|
|
export function preferredAction(
|
|
collision: Collision,
|
|
prior: RecalledCollision | null,
|
|
): SuggestedActionKind {
|
|
const acceptedKind = prior?.priorOutcome?.accepted
|
|
? prior.priorIntervention?.suggestedAction.kind
|
|
: undefined;
|
|
if (acceptedKind && acceptedKind !== 'none') return acceptedKind;
|
|
return collision.severity === 'critical' ? 'open_sync_pr' : 'ping_teammate';
|
|
}
|