Quality gates / quality (pull_request) Successful in 18m50s
A backup is the person's own record and has to keep working as the AI fields evolve. - Export stamps schema version 2 and carries the confirmed logs plus the training contribution receipts, including withdrawn tombstones; both are written through an allowlist so unknown keys and credentials never leave the device. - Import accepts version 1 and 2. A version 1 file is upgraded, the notice names the version it came from, and entries that predate provenance stay null instead of being given a source they never recorded. - A file from a newer Timmy is refused with an update instruction, malformed, oversized and over-limit files fail closed, and a refused import leaves the ledger unchanged. - A log saved from a confirmed or corrected AI suggestion records its provenance (source, suggested visible fields, model, confirmedAt) and the entry sheet states it. - Service worker shell cache v5 -> v6 so installed clients actually receive the change. Evidence: tests/portability.test.js (7 unit cases), tests/portability.acceptance.mjs (real browser: v1 upgrade, future refusal with unchanged ledger, provenance recorded, export v2, round trip), release demo scenes with on-camera caption proof, and the release builder gate portable_backup_restore. Refs #35
53 lines
2.8 KiB
JavaScript
53 lines
2.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
|
|
const decisionPath = new URL('../docs/PRODUCT-DECISIONS.md', import.meta.url);
|
|
const policyPath = new URL('../docs/TIMMY-AGENT-POLICY.md', import.meta.url);
|
|
const readmePath = new URL('../README.md', import.meta.url);
|
|
|
|
test('product decision record fixes the AI boundary and release authority', async () => {
|
|
const decision = await readFile(decisionPath, 'utf8');
|
|
|
|
assert.match(decision, /Observable AI fields[\s\S]*Bristol form[\s\S]*broad color[\s\S]*image quality/i);
|
|
assert.match(decision, /Prohibited inferences[\s\S]*disease[\s\S]*bleeding[\s\S]*pain[\s\S]*urgency[\s\S]*fever[\s\S]*vomiting[\s\S]*food/i);
|
|
assert.match(decision, /release authority/i);
|
|
assert.match(decision, /tool-capable Hermes Agent/i);
|
|
assert.match(decision, /browser never receives provider credentials or Hermes session IDs/i);
|
|
assert.match(decision, /deterministic urgent-symptom guidance runs before chat/i);
|
|
assert.match(decision, /Human gates[\s\S]*clinical and privacy review[\s\S]*beta consent[\s\S]*RC approval/i);
|
|
assert.match(decision, /\.\.\/PRODUCT\.md/);
|
|
assert.match(decision, /\.\.\/AI-EVIDENCE\.md/);
|
|
assert.match(decision, /\.\.\/research\/SELF-HOSTED-STOOL-VISION\.md/);
|
|
});
|
|
|
|
test('the decision record keeps training contribution as its own versioned, withdrawable consent', async () => {
|
|
const decision = await readFile(decisionPath, 'utf8');
|
|
|
|
assert.match(decision, /### Training contribution consent/);
|
|
assert.match(decision, /never a training contribution/i);
|
|
assert.match(decision, /withdrawal keeps a tombstone naming every derived artifact by contribution id/i);
|
|
assert.match(decision, /does not transmit a contribution until the corpus pipeline and privacy copy are reviewed/i);
|
|
});
|
|
|
|
test('Hermes deployment installs a checked-in non-diagnostic workspace policy', async () => {
|
|
const [policy, readme] = await Promise.all([readFile(policyPath, 'utf8'), readFile(readmePath, 'utf8')]);
|
|
assert.match(policy, /never diagnose/i);
|
|
assert.match(policy, /urgent symptoms/i);
|
|
assert.match(policy, /explicit user intent and confirmation/i);
|
|
assert.match(readme, /install -m 600 docs\/TIMMY-AGENT-POLICY\.md/);
|
|
});
|
|
|
|
test('the decision record keeps a backup versioned and refuses files it cannot read', async () => {
|
|
const decision = await readFile(decisionPath, 'utf8');
|
|
const readme = await readFile(readmePath, 'utf8');
|
|
|
|
assert.match(decision, /### Backup portability and entry provenance/);
|
|
assert.match(decision, /schema version/i);
|
|
assert.match(decision, /upgraded on import/i);
|
|
assert.match(decision, /newer version is refused/i);
|
|
assert.match(decision, /a refused import leaves the ledger unchanged/i);
|
|
assert.match(decision, /provenance is stored on the confirmed log/i);
|
|
assert.match(readme, /versioned JSON backup/i);
|
|
});
|