All checks were successful
Quality gates / quality (pull_request) Successful in 3m31s
Second hostile review of 1aadca91 found nine ordinary urgent phrasings
bypassing the deterministic gate at detector and service layers, plus
punctuation-fragile contextual exclusions. Replace the accumulated
narrow regex table with a structured, versioned, frozen urgent-expression
grammar and one shared surface normalizer:
- normalizeUrgentText: case folding, apostrophe unification, contraction
expansion (can't/cant/can not -> cannot, haven't -> have not, ...),
hyphen splitting, punctuation stripping, whitespace collapse
- URGENT_EXPRESSION_GRAMMAR v2.0: per-flag ordered match expressions with
bounded nonclinical anchor exclusions; anchors veto only the occurrence
they sit beside (36-char window), so arbitrary future symptom language
keeps escalating with no continuation-word allowlist
- new RED->GREEN coverage at every layer: 9 review phrases + 5 prior
phrases with tense/plural/pronoun/word-order/case/contraction/
punctuation variants, normalization-equivalence groups, anti-allowlist
continuation sweep (147 combos), grammar structure audit, service
zero-Hermes-call interception, and live-HTTP wiring proof with the
bounded fake adapter (tests/escalation-http.test.js)
Gates: npm test 99/99, check:syntax, check:diff, audit 0 vulns,
staging-deploy 20/20, test:ui/test:photo/test:sleek against this
checkout. No merge, no deploy.
84 lines
4.0 KiB
JavaScript
84 lines
4.0 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 expression grammar drives text detection; no parallel detector copies exist.
|
|
assert.match(domain, /const URGENT_EXPRESSION_GRAMMAR = 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');
|
|
});
|
|
|
|
// Second hostile review: the HTTP wiring layer must prove every new phrase is
|
|
// intercepted over a live socket before any Hermes adapter process can start.
|
|
const WIRING_REVIEW_PHRASES = [
|
|
'My stool had blood.',
|
|
'My stools are bloody.',
|
|
'My stools are black.',
|
|
'My stool has turned black.',
|
|
'My abdominal pain is severe.',
|
|
'Pain in my abdomen is severe.',
|
|
'I threw my lunch up.',
|
|
'I can not pass gas.',
|
|
"I haven't been able to pass gas.",
|
|
'yellow-fever outbreak in history class',
|
|
'The fever-tree is a plant',
|
|
'I threw up, my hands in surrender.',
|
|
'Saturday Night Fever won awards',
|
|
];
|
|
|
|
test('urgent review phrases and contextual controls are wired through one shared domain grammar', async () => {
|
|
const [service, app] = await Promise.all([
|
|
readFile(servicePath, 'utf8'),
|
|
readFile(appPath, 'utf8'),
|
|
]);
|
|
// Server gate screens the message through detectUrgentText before spawning Hermes.
|
|
const chatIndex = service.indexOf('async chat(');
|
|
const chatBody = service.slice(chatIndex);
|
|
assert.ok(chatBody.indexOf('detectUrgentText(message).urgent') > 0);
|
|
assert.ok(chatBody.indexOf('detectUrgentText(message).urgent') < chatBody.indexOf('await runTurn('));
|
|
// Browser gate re-exports the same detection for pre-network interception.
|
|
assert.match(app, /detectUrgentText\(message\)\.urgent/);
|
|
assert.match(app, /urgentChatMessage/);
|
|
// The grammar lives only in the domain module; the service layer never
|
|
// re-implements symptom vocabulary of its own.
|
|
assert.doesNotMatch(service, /fever|pass gas|stool|vomit/i);
|
|
});
|