feat(ai-psychosis): add v1.2 thresholds and domain-stakes table

This commit is contained in:
Kjell Tore Guttormsen 2026-05-01 21:22:51 +02:00
commit 7b0afdb541
2 changed files with 119 additions and 0 deletions

View file

@ -131,6 +131,47 @@ export const COOLDOWN_HARD = 3600;
// v1.1.0 — counting threshold; tier-reduction logic is v1.2 scope
export const THRESHOLD_PUSHBACK_FLAGS = 2;
// --- v1.2 thresholds and domain-stakes table ---
//
// Sources: Anthropic guidance paper Appendix (April 2026), Figure A1 (stakes),
// Figure A4 (domain pushback rates). All domain identifiers are SINGULAR to
// match v1.1.0's `state.domain_context = 'relationship'` convention.
export const TIER1_TURN_THRESHOLD = 15;
export const TIER2_SESSION_THRESHOLD = 3;
export const THRESHOLD_VALSEEK_FLAGS = 3;
// Domain stakes weights — Figure A1 high/very-high stakes domains carry
// higher multipliers; consumer/personal_dev are baseline 1.0.
export const DOMAIN_STAKES = Object.freeze({
legal: 1.5,
parenting: 1.5,
health: 1.5,
financial: 1.5,
relationship: 1.3,
spirituality: 1.2,
professional: 1.1,
wellbeing: 1.2,
lifepath: 1.1,
values: 1.2,
personal_dev: 1.0,
consumer: 1.0,
default: 1.0
});
// Pushback in these domains signals validation-pressing (Figure A4 — relationships
// 21%, spirituality 19%); pushback alert fires.
export const HIGH_SYCOPHANCY_DOMAINS = Object.freeze(['relationship', 'spirituality']);
// High-stakes guidance domains (Figure A1 high/very-high). Tier-1 user-info
// alert fires only when domain_context intersects this set.
export const HIGH_STAKES_DOMAINS = Object.freeze(['legal', 'parenting', 'health', 'financial']);
// Info-seeking domains where pushback signals healthy self-advocacy (Figure A4 —
// parenting 7.9%, legal/health/financial 8094% pushback rate). Pushback alert
// is suppressed when domain_context is entirely within this set.
export const INFO_DOMAINS = Object.freeze(['legal', 'parenting', 'health', 'financial', 'professional']);
// --- Session counting ---
export function sessionsToday() {

View file

@ -0,0 +1,78 @@
// Unit tests for shared library constants and helpers.
// Sanity-checks that v1.2 thresholds and domain-stakes table are exported
// with the expected shape. Detector-level behaviour is covered in
// per-detector test files (user-info, validation-seeking, stakes-matrix).
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
import {
TIER1_TURN_THRESHOLD,
TIER2_SESSION_THRESHOLD,
THRESHOLD_VALSEEK_FLAGS,
DOMAIN_STAKES,
HIGH_SYCOPHANCY_DOMAINS,
HIGH_STAKES_DOMAINS,
INFO_DOMAINS,
} from '../hooks/scripts/lib.mjs';
describe('v1.2 thresholds', () => {
test('tier-1 turn threshold is 15', () => {
assert.equal(TIER1_TURN_THRESHOLD, 15);
});
test('tier-2 session threshold is 3', () => {
assert.equal(TIER2_SESSION_THRESHOLD, 3);
});
test('valseek high-stakes flag threshold is 3', () => {
assert.equal(THRESHOLD_VALSEEK_FLAGS, 3);
});
});
describe('DOMAIN_STAKES table', () => {
test('default weight is 1.0', () => {
assert.equal(DOMAIN_STAKES.default, 1.0);
});
test('high-stakes domains weighted 1.5', () => {
assert.equal(DOMAIN_STAKES.legal, 1.5);
assert.equal(DOMAIN_STAKES.parenting, 1.5);
assert.equal(DOMAIN_STAKES.health, 1.5);
assert.equal(DOMAIN_STAKES.financial, 1.5);
});
test('high-sycophancy domains weighted between 1.2 and 1.3', () => {
assert.equal(DOMAIN_STAKES.relationship, 1.3);
assert.equal(DOMAIN_STAKES.spirituality, 1.2);
});
test('table is frozen (immutable)', () => {
assert.equal(Object.isFrozen(DOMAIN_STAKES), true);
});
test('uses singular domain identifiers (relationship, not relationships)', () => {
assert.equal(DOMAIN_STAKES.relationship, 1.3);
assert.equal(DOMAIN_STAKES.relationships, undefined);
});
});
describe('domain classification arrays', () => {
test('HIGH_SYCOPHANCY_DOMAINS contains relationship and spirituality', () => {
assert.deepEqual([...HIGH_SYCOPHANCY_DOMAINS], ['relationship', 'spirituality']);
assert.equal(Object.isFrozen(HIGH_SYCOPHANCY_DOMAINS), true);
});
test('HIGH_STAKES_DOMAINS contains legal, parenting, health, financial', () => {
assert.deepEqual([...HIGH_STAKES_DOMAINS], ['legal', 'parenting', 'health', 'financial']);
assert.equal(Object.isFrozen(HIGH_STAKES_DOMAINS), true);
});
test('INFO_DOMAINS adds professional to HIGH_STAKES_DOMAINS', () => {
assert.deepEqual(
[...INFO_DOMAINS],
['legal', 'parenting', 'health', 'financial', 'professional']
);
assert.equal(Object.isFrozen(INFO_DOMAINS), true);
});
});