All checks were successful
Quality gates / quality (pull_request) Successful in 1m28s
Issue #21 (epic #5). Prove model behavior can never suppress or soften urgent symptom handling. - Pin the six authoritative red flags, their canonical order, and the exact urgent copy as read-only exports; hostile provider output cannot reassemble it. - Grow the positive text matrix from 24 to 56 pinned clinical phrases across all flags (bloody stool, dark-red/black descriptions, severe abdominal pain variants, vomiting tenses/slang, fever phrasings, inability to pass gas) and pin 27 idiomatic negatives that must not escalate (threw up my hands, yellow fever history class, black tea). - Authoritative boundary suite: every red-flag phrase and confirmed ledger symptom/note intercepts chat with zero Hermes calls; malicious/missing provider replies cannot weaken the deterministic response; truthy junk symptoms can neither fabricate nor suppress escalation. - Wiring suite: the single chat gate screens urgency before any agent turn in both service and browser code; detection stays centralized in the frozen domain pattern table. RED evidence: URGENT_MESSAGE unexported, 9 matrix misses (bleeding from my rectum, bloody stool/poop, severe pain in my abdomen), 8 false positives (I threw up my hands, feverish about the election). GREEN: 87/87 npm test, syntax/diff gates clean, 0 vulnerabilities, browser suites pass with zero /api/agent/chat calls on urgent input. Closes #21
48 lines
2.4 KiB
JavaScript
48 lines
2.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
|
|
const packagePath = new URL('../package.json', import.meta.url);
|
|
const domainPath = new URL('../src/domain.js', import.meta.url);
|
|
const servicePath = new URL('../src/hermes-agent-service.js', import.meta.url);
|
|
const appPath = new URL('../app.js', import.meta.url);
|
|
|
|
test('expanded escalation suites are first-class gates in the default npm test run', async () => {
|
|
const packageJson = JSON.parse(await readFile(packagePath, 'utf8'));
|
|
const script = packageJson.scripts.test;
|
|
|
|
assert.match(script, /tests\/symptom-escalation\.regression\.test\.js/, 'domain regression matrix must run in CI');
|
|
assert.match(script, /tests\/escalation-boundary\.test\.js/, 'authoritative boundary suite must run in CI');
|
|
});
|
|
|
|
test('the authoritative zero-Hermes-call invariant stays wired at the single chat gate', async () => {
|
|
const [service, app] = await Promise.all([
|
|
readFile(servicePath, 'utf8'),
|
|
readFile(appPath, 'utf8'),
|
|
]);
|
|
|
|
// Server: urgent detection happens before any Hermes turn is spawned.
|
|
const chatIndex = service.indexOf('async chat(');
|
|
assert.ok(chatIndex > 0, 'chat entry point exists');
|
|
const chatBody = service.slice(chatIndex);
|
|
const urgentGate = chatBody.indexOf('detectUrgentText(message).urgent || hasUrgentLedgerContext(ledger)');
|
|
const runTurnCall = chatBody.indexOf('await runTurn(');
|
|
assert.ok(urgentGate > 0, 'service must screen message and ledger urgency');
|
|
assert.ok(runTurnCall > 0, 'service must call the agent adapter');
|
|
assert.ok(urgentGate < runTurnCall, 'urgent override must execute before the Hermes turn');
|
|
assert.match(chatBody, /safetyOverride: true/, 'override response is explicit');
|
|
|
|
// Browser: the same deterministic rules intercept before the network call.
|
|
assert.match(app, /detectUrgentText\(message\)\.urgent\|\|hasUrgentLedgerContext\(ledgerForAgent\(\)\)/);
|
|
assert.match(app, /urgentChatMessage/);
|
|
});
|
|
|
|
test('detection logic stays centralized in the shared domain module', async () => {
|
|
const domain = await readFile(domainPath, 'utf8');
|
|
// One frozen pattern table drives text detection; no parallel detector copies exist.
|
|
assert.match(domain, /const URGENT_TEXT_PATTERNS = Object\.freeze\(/);
|
|
assert.match(domain, /const URGENT_KEYS = /);
|
|
assert.equal([...domain.matchAll(/URGENT_MESSAGE/g)].length >= 3, true,
|
|
'flags, ledger, and chat paths share one urgent copy constant');
|
|
});
|