feat(ai-psychosis): same-invocation valence-aware pushback detection

This commit is contained in:
Kjell Tore Guttormsen 2026-05-01 17:28:54 +02:00
commit eca30b4682
2 changed files with 44 additions and 0 deletions

View file

@ -111,10 +111,20 @@ for (const p of depPatterns) { if (p.test(prompt)) { depHit = 1; break; } }
for (const p of escPatterns) { if (p.test(prompt)) { escHit = 1; break; } }
for (const p of fatPatterns) { if (p.test(prompt)) { fatHit = 1; break; } }
for (const p of valPatterns) { if (p.test(prompt)) { valHit = 1; break; } }
let pbReactiveHit = 0; for (const p of pbReactivePatterns) { if (p.test(prompt)) { pbReactiveHit = 1; break; } }
let pbPreemptiveHit = 0; for (const p of pbPreemptivePatterns) { if (p.test(prompt)) { pbPreemptiveHit = 1; break; } }
let domainHit = 0; for (const p of domainRelationshipPatterns) { if (p.test(prompt)) { domainHit = 1; break; } }
// Clear prompt from memory
prompt = '';
// Same-invocation valence guard (research/01 frustration-spiral finding):
// pushback in fat/esc context is NOT protective — suppress in same prompt.
if (fatHit === 1 || escHit === 1) {
pbReactiveHit = 0;
pbPreemptiveHit = 0;
}
// Update state with new flag counts
const state = readState();
const newDep = (Number(state.dep_flags) || 0) + depHit;
@ -126,6 +136,8 @@ state.dep_flags = newDep;
state.esc_flags = newEsc;
state.fatigue_flags = newFat;
state.val_flags = newVal;
state.pushback_count = (Number(state.pushback_count) || 0) + pbReactiveHit + pbPreemptiveHit;
if (domainHit === 1 && !state.domain_context) state.domain_context = 'relationship';
writeState(state);
// Check if any thresholds crossed

View file

@ -396,3 +396,35 @@ describe('domain relationship patterns', () => {
it('does not match "the data is updating" (no dating word boundary)', () => assert.equal(matchesAny(domainRelationshipPatterns, 'the data is updating in real time'), false));
it('does not match "romantic comedy film" (no involved/interested suffix)', () => assert.equal(matchesAny(domainRelationshipPatterns, 'watching a romantic comedy film'), false));
});
// --- v1.1.0 integration: pushback + valence + domain through prompt-analyzer.mjs ---
describe('pushback integration (state accumulation + same-invocation valence)', () => {
it('counts reactive pushback alone (no fatigue/escalation)', () => {
const s = runPrompt('are you sure?');
assert.equal(s.pushback_count, 1);
assert.equal(s.fatigue_flags, 0);
assert.equal(s.esc_flags, 0);
});
it('counts preemptive pushback alone', () => {
const s = runPrompt('please steelman this argument');
assert.equal(s.pushback_count, 1);
});
it('SUPPRESSES pushback when fatigue marker is in same invocation (valence guard)', () => {
const s = runPrompt("are you sure? I'm exhausted by all this");
assert.equal(s.pushback_count, 0, 'pushback must be suppressed when fatigue is co-present');
assert.equal(s.fatigue_flags, 1);
});
it('sets domain_context to "relationship" on positive match', () => {
const s = runPrompt("my partner won't listen to me");
assert.equal(s.domain_context, 'relationship');
});
it('keeps domain_context null on technical "function relationship" (false-positive guard)', () => {
const s = runPrompt('function relationship between input and output');
assert.equal(s.domain_context, null);
});
});