Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 112 additions & 51 deletions VA_BACKLOG.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions __tests__/safeguarding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,22 @@

});

// =============================================================================
// Crisis gate option 2 — safeguarding state set before LA prompt (PR #19)
// =============================================================================

describe('Crisis gate option 2', () => {

test('GATE0 option 2 sets safeguardingTriggered and safeguardingType before LA prompt', () => {
const session = sessionAt('GATE0_CRISIS_DANGER');
const result = select(session, 2);
expect(result.stateUpdates.currentGate).toBe('CRISIS_UNDER16_LOCATION');
expect(result.stateUpdates.safeguardingTriggered).toBe(true);
expect(result.stateUpdates.safeguardingType).toBe('UNDER_16');
});

});

// =============================================================================
// 16-17 - Must continue profiling (not exit), but flag youth services
// =============================================================================
Expand Down Expand Up @@ -1015,16 +1031,16 @@

// Find the safeguarding-trigger log call
const safeguardingCalls = logSpy.mock.calls.filter(
(c) => typeof c[0] === 'string' && c[0].includes('SAFEGUARDING_TRIGGER')

Check warning on line 1034 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access [0] on an `any` value

Check warning on line 1034 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access [0] on an `any` value

Check warning on line 1034 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access [0] on an `any` value

Check warning on line 1034 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access [0] on an `any` value
);
expect(safeguardingCalls.length).toBeGreaterThan(0);

const payload = JSON.parse(safeguardingCalls[0][1]);

Check warning on line 1038 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access [1] on an `any` value

Check warning on line 1038 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe argument of type `any` assigned to a parameter of type `string`

Check warning on line 1038 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe assignment of an `any` value

Check warning on line 1038 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access [1] on an `any` value

Check warning on line 1038 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe argument of type `any` assigned to a parameter of type `string`

Check warning on line 1038 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe assignment of an `any` value
expect(payload.type).toBe('AGE_NUMERIC');

Check warning on line 1039 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .type on an `any` value

Check warning on line 1039 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .type on an `any` value
expect(payload.code).toBe('AGE_NUMERIC_14');

Check warning on line 1040 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .code on an `any` value

Check warning on line 1040 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .code on an `any` value
expect(payload.sessionId).toBe('test-session-abc-123');

Check warning on line 1041 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .sessionId on an `any` value

Check warning on line 1041 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .sessionId on an `any` value
expect(payload.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);

Check warning on line 1042 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .timestamp on an `any` value

Check warning on line 1042 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .timestamp on an `any` value
expect(payload.event).toBe('UNDER_16_DETECTED');

Check warning on line 1043 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .event on an `any` value

Check warning on line 1043 in __tests__/safeguarding.test.ts

View workflow job for this annotation

GitHub Actions / test

Unsafe member access .event on an `any` value
expect(payload.pathwayChange).toBe('TERMINATED_TO_UNDER_16_EXIT');

// Verify NO log call across the whole spy contains the raw user text
Expand Down
6 changes: 5 additions & 1 deletion lib/handlers/crisis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,11 @@ export function handleCrisisDanger(session: SessionState, choice: number | null)
case 2: // Under 16 -> ask location first
return {
...phrase('CRISIS_UNDER16_LOCATION', session.isSupporter),
stateUpdates: { currentGate: 'CRISIS_UNDER16_LOCATION' }
stateUpdates: {
currentGate: 'CRISIS_UNDER16_LOCATION',
safeguardingTriggered: true,
safeguardingType: 'UNDER_16',
},
};
case 3: // Self-harm
return buildSelfHarmExit(session);
Expand Down
4 changes: 2 additions & 2 deletions lib/handlers/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { getPhrase } from '../phrasebank';
import type { SessionState, RoutingResult, GateType } from '../types';
import endpointsData from '../data/housing-pathway-endpoints.json';

export function phrase(key: string, isSupporter: boolean): RoutingResult {
const p = getPhrase(key, isSupporter);
export function phrase(key: string, audience: 'SELF' | 'SUPPORTER' | 'PROFESSIONAL' | null | boolean): RoutingResult {
const p = getPhrase(key, audience);
return {
text: p?.text || `[Missing phrase: ${key}]`,
options: p?.options,
Expand Down
13 changes: 11 additions & 2 deletions lib/phrasebank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2387,8 +2387,17 @@ The more details you can provide (location, description, best time to find them)
};

// Helper function
export function getPhrase(key: string, isSupporter: boolean): PhraseEntry | null {
if (isSupporter) {
export function getPhrase(key: string, audience: 'SELF' | 'SUPPORTER' | 'PROFESSIONAL' | null | boolean): PhraseEntry | null {
if (audience === 'PROFESSIONAL') {
const professionalKey = `${key}__PROFESSIONAL`;
if (phrasebank[professionalKey]) {
return phrasebank[professionalKey];
}
const supporterFallback = `${key}__SUPPORTER`;
if (phrasebank[supporterFallback]) {
return phrasebank[supporterFallback];
}
} else if (audience === 'SUPPORTER' || audience === true) {
const supporterKey = `${key}__SUPPORTER`;
if (phrasebank[supporterKey]) {
return phrasebank[supporterKey];
Expand Down
4 changes: 2 additions & 2 deletions lib/stateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ export function createSession(sessionId: string): SessionState {
// HELPERS
// ============================================================

function phrase(key: string, isSupporter: boolean): RoutingResult {
const p = getPhrase(key, isSupporter);
function phrase(key: string, audience: 'SELF' | 'SUPPORTER' | 'PROFESSIONAL' | null | boolean): RoutingResult {
const p = getPhrase(key, audience);
return {
text: p?.text || `[Missing phrase: ${key}]`,
options: p?.options,
Expand Down
Loading