atlas-iac/testing/tests/test_hermes_hux_ui_privacy.mjs
jenkins b66c762f5d hermes(webui): add HUX card UI models with contract-locked suites
Standalone per-card browser model/security/view modules for HUX-01..10
plus node+pytest suites that read the hux.v1 contract schemas directly.
Reconciled drift found on integration: the activity model now accepts
all 32 hux.event.v1 kinds (delegation.*, memory.suppressed,
memory.retrieval_removed, budget.exhausted, side_effect.*), the
autonomy model carries the external_side_effect capability, and the
foundation boundary test now asserts the shipped static HUX surface
exists on disk and that images never bake activated HUX_FLAGS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
2026-08-24 04:12:03 -03:00

313 lines
13 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
createPrivacyClient,
PrivacyContractError,
} from "../../dockerfiles/hermes-webui-hux/privacy/client.ts";
import {
FOUNDATION_FLAG,
GENERIC_NOTICE,
PRIVACY_FLAG,
normalizeNotice,
normalizeSnapshot,
privacyControls,
privacyEnabled,
withoutNotice,
} from "../../dockerfiles/hermes-webui-hux/privacy/model.ts";
import {
asRecord,
exactKeys,
isId,
isSafeText,
isUtc,
normalizeIdentity,
normalizeScope,
privacyQuery,
sameIdentity,
} from "../../dockerfiles/hermes-webui-hux/privacy/security.ts";
const IDENTITY = Object.freeze({
tenantRef: "tnt_0123456789abcdef",
userRef: "usr_0123456789abcdef",
surface: "chat",
});
const RAW_IDENTITY = Object.freeze({
tenant_ref: IDENTITY.tenantRef,
user_ref: IDENTITY.userRef,
surface: IDENTITY.surface,
});
const SCOPE = Object.freeze({
sessionId: "ses_alpha1234",
conversationId: "conv_alpha1234",
});
const SHOWN_AT = "2026-08-24T10:00:00Z";
function notice(extra = {}) {
return {
schema: "hux.privacy_notice.v1",
topic: "health",
conversation_id: SCOPE.conversationId,
text: "This looks like a health topic. It stays in this conversation.",
controls: [
"forget_this_conversation", "switch_to_private", "disable_memory_here", "dismiss",
],
shown_at: SHOWN_AT,
...extra,
};
}
function retention(extra = {}) {
return {
scope: "conversation",
memory_write: "ask",
topic_context_decay_days: 30,
audit_interval_days: 1,
next_audit_at: "2026-08-25T03:00:00Z",
cross_surface_sharing: "never",
...extra,
};
}
function snapshot(extra = {}) {
return {
schema: "hux.privacy_snapshot.v1",
api_version: "hux.v1",
identity: RAW_IDENTITY,
session_id: SCOPE.sessionId,
conversation_id: SCOPE.conversationId,
revision: 1,
no_store: false,
memory_enabled: true,
retention: retention(),
notice: notice(),
...extra,
};
}
function noStore(extra = {}) {
return snapshot({
revision: 2,
no_store: true,
memory_enabled: false,
retention: retention({scope: "session", memory_write: "deny",
topic_context_decay_days: null}),
notice: null,
...extra,
});
}
function memoryOff(extra = {}) {
return snapshot({
revision: 2,
memory_enabled: false,
retention: retention({memory_write: "deny", topic_context_decay_days: null}),
notice: null,
...extra,
});
}
function harness(responses, flags = [FOUNDATION_FLAG, PRIVACY_FLAG]) {
const calls = [];
const enabled = new Set(flags);
const foundation = {
apiVersion: "hux.v1",
identity: IDENTITY,
enabled: (flag) => enabled.has(flag),
endpoint: (path) => `/hux/v1${path}`,
};
const fetcher = async (url, init) => {
calls.push({url, init});
const response = responses.shift();
if (!response) throw new Error("unexpected request");
return {ok: response.ok ?? true, status: response.status ?? 200,
json: async () => response.body};
};
return {calls, foundation,
client: createPrivacyClient({client: foundation, fetcher})};
}
test("HUX-10 is default-off and requires the foundation", () => {
assert.equal(privacyEnabled(), false);
assert.equal(privacyEnabled([]), false);
assert.equal(privacyEnabled([FOUNDATION_FLAG]), false);
assert.equal(privacyEnabled([PRIVACY_FLAG]), false);
assert.equal(privacyEnabled([FOUNDATION_FLAG, PRIVACY_FLAG]), true);
});
test("security primitives bind opaque scope and same-origin query", () => {
assert.deepEqual(asRecord({a: 1}), {a: 1});
assert.equal(asRecord(null), null);
assert.equal(asRecord([]), null);
assert.equal(exactKeys({a: 1, b: 2}, ["a"], ["b"]), true);
assert.equal(exactKeys({a: 1, x: 2}, ["a"]), false);
assert.equal(isId("conv_alpha1234", "conv"), true);
assert.equal(isId("bad", "conv"), false);
assert.equal(isId("ses_alpha1234", "conv"), false);
assert.equal(isUtc("2026-08-24T10:00:00Z"), true);
assert.equal(isUtc("2026-99-99T10:00:00Z"), false);
assert.equal(isSafeText("safe", 1, 20), true);
assert.equal(isSafeText("bad\u0000", 1, 20), false);
assert.deepEqual(normalizeIdentity(RAW_IDENTITY), IDENTITY);
for (const raw of [null, [], {}, {...RAW_IDENTITY, extra: true},
{...RAW_IDENTITY, tenant_ref: "bad"}, {...RAW_IDENTITY, user_ref: "bad"},
{...RAW_IDENTITY, surface: "browser"}]) assert.equal(normalizeIdentity(raw), null);
assert.equal(sameIdentity(IDENTITY, IDENTITY), true);
assert.equal(sameIdentity(IDENTITY, {...IDENTITY, surface: "worker"}), false);
assert.deepEqual(normalizeScope(SCOPE), SCOPE);
for (const raw of [null, {...SCOPE, extra: true}, {...SCOPE, sessionId: "bad"},
{...SCOPE, conversationId: "bad"}]) assert.equal(normalizeScope(raw), null);
assert.equal(privacyQuery(SCOPE),
"/privacy/snapshot?session_id=ses_alpha1234&conversation_id=conv_alpha1234");
assert.throws(() => privacyQuery({...SCOPE, sessionId: "bad"}), TypeError);
});
test("notice validation drops category and source text from view state", () => {
const item = normalizeNotice(notice(), SCOPE.conversationId, "ask", 30);
assert.equal(item.text, GENERIC_NOTICE);
assert.equal("topic" in item, false);
assert.deepEqual(item.controls, notice().controls);
const restricted = normalizeNotice(notice({topic: "credentials"}),
SCOPE.conversationId, "deny", 1);
assert.equal(restricted.text, GENERIC_NOTICE);
const invalid = [null, {...notice(), extra: true}, {...notice(), schema: "old"},
{...notice(), topic: "politics"}, {...notice(), conversation_id: "conv_other1234"},
{...notice(), text: ""}, {...notice(), text: "bad\u0000"},
{...notice(), shown_at: "today"}, {...notice(), controls: null},
{...notice(), controls: []}, {...notice(), controls: [...notice().controls, "dismiss"]},
{...notice(), controls: ["unknown"]}];
invalid.forEach((raw) => assert.equal(
normalizeNotice(raw, SCOPE.conversationId, "ask", 30), null));
assert.equal(normalizeNotice(notice(), SCOPE.conversationId, "deny", 30), null);
assert.equal(normalizeNotice(notice(), SCOPE.conversationId, "ask", 1), null);
});
test("snapshot exposes generic retention while enforcing policy semantics", () => {
const item = normalizeSnapshot(snapshot(), IDENTITY, SCOPE);
assert.equal(item.notice.text, GENERIC_NOTICE);
assert.equal(item.retention.topicContextDecayDays, 30);
const privateItem = normalizeSnapshot(noStore(), IDENTITY, SCOPE);
assert.equal(privateItem.noStore, true);
assert.equal(privateItem.retention.scope, "session");
assert.equal(normalizeSnapshot(memoryOff(), IDENTITY, SCOPE).memoryEnabled, false);
const restricted = normalizeSnapshot(snapshot({memory_enabled: false,
retention: retention({memory_write: "deny", topic_context_decay_days: 1}),
notice: notice({topic: "credentials"})}), IDENTITY, SCOPE);
assert.equal(restricted.notice.text, GENERIC_NOTICE);
const changedTopic = normalizeSnapshot(snapshot({notice: null}), IDENTITY, SCOPE);
assert.equal(changedTopic.notice, null);
});
test("snapshot rejects malformed scope, retention, no-store and notice state", () => {
const invalid = [null, {...snapshot(), extra: true}, {...snapshot(), schema: "old"},
{...snapshot(), api_version: "hux.v0"},
{...snapshot(), identity: {...RAW_IDENTITY, surface: "worker"}},
{...snapshot(), session_id: "ses_other1234"},
{...snapshot(), conversation_id: "conv_other1234"},
{...snapshot(), revision: 0}, {...snapshot(), no_store: "no"},
{...snapshot(), memory_enabled: "yes"}, {...snapshot(), retention: null},
{...snapshot(), retention: {...retention(), extra: true}},
{...snapshot(), retention: retention({scope: "forever"})},
{...snapshot(), retention: retention({memory_write: "automatic"})},
{...snapshot(), retention: retention({topic_context_decay_days: 0})},
{...snapshot(), retention: retention({audit_interval_days: 31})},
{...snapshot(), retention: retention({next_audit_at: "tomorrow"})},
{...snapshot(), retention: retention({cross_surface_sharing: "everyone"})},
{...snapshot(), no_store: true},
{...noStore(), retention: retention({scope: "session", memory_write: "deny",
topic_context_decay_days: 1})},
{...noStore(), memory_enabled: true},
{...snapshot(), memory_enabled: false},
{...snapshot(), notice: notice({topic: "credentials"})},
];
invalid.forEach((raw) => assert.equal(normalizeSnapshot(raw, IDENTITY, SCOPE), null));
});
test("view helpers suppress repeats and expose only effective controls", () => {
const item = normalizeSnapshot(snapshot(), IDENTITY, SCOPE);
assert.equal(withoutNotice(item).notice, null);
const changed = withoutNotice({...item, notice: null});
assert.equal(changed.notice, null);
assert.deepEqual(privacyControls(item), [
"enable_no_store", "disable_memory_here", "forget_this_conversation",
"switch_to_private", "dismiss",
]);
assert.deepEqual(privacyControls(normalizeSnapshot(noStore(), IDENTITY, SCOPE)),
["disable_no_store"]);
});
test("client loads scoped no-cache state and does not reiterate notices", async () => {
const current = harness([{body: snapshot()}, {body: snapshot()},
{body: snapshot({revision: 2, notice: null})},
{body: snapshot({revision: 3, notice: notice({topic: "finance",
shown_at: "2026-08-24T11:00:00Z"})})}]);
assert.equal(current.client.enabled(), true);
const first = await current.client.load(SCOPE);
assert.ok(first.notice);
assert.equal((await current.client.load(SCOPE)).notice, null);
assert.equal((await current.client.load(SCOPE)).notice, null);
assert.ok((await current.client.load(SCOPE)).notice);
assert.equal(current.calls[0].url,
"/hux/v1/privacy/snapshot?session_id=ses_alpha1234&conversation_id=conv_alpha1234");
assert.equal(current.calls[0].init.method, "GET");
assert.equal(current.calls[0].init.cache, "no-store");
assert.equal(current.calls[0].init.credentials, "same-origin");
});
test("client applies each explicit control with optimistic scope binding", async () => {
const cases = [
["enable_no_store", noStore()],
["switch_to_private", noStore()],
["forget_this_conversation", noStore()],
["disable_memory_here", memoryOff()],
["dismiss", snapshot({revision: 2, notice: null})],
];
for (const [action, result] of cases) {
const current = harness([{body: snapshot()}, {body: result}]);
const before = await current.client.load(SCOPE);
const after = await current.client.control(before, action);
assert.equal(after.revision, 2);
assert.equal(current.calls[1].url, "/hux/v1/privacy/controls");
assert.deepEqual(JSON.parse(current.calls[1].init.body), {
schema: "hux.privacy_control_request.v1", session_id: SCOPE.sessionId,
conversation_id: SCOPE.conversationId, expected_revision: 1, action,
});
await assert.rejects(current.client.control(before, action), /not bound/);
}
const current = harness([{body: noStore({revision: 1})},
{body: snapshot({revision: 2, notice: null})}]);
const before = await current.client.load(SCOPE);
assert.equal((await current.client.control(before, "disable_no_store")).noStore, false);
});
test("client fails closed for disabled, invalid, stale and cross-scope data", async () => {
const disabled = harness([], [FOUNDATION_FLAG]);
assert.equal(disabled.client.enabled(), false);
await assert.rejects(disabled.client.load(SCOPE), /not enabled/);
disabled.foundation.apiVersion = "hux.v0";
await assert.rejects(disabled.client.load(SCOPE), /not enabled/);
const invalidScope = harness([]);
await assert.rejects(invalidScope.client.load({...SCOPE, sessionId: "bad"}), /scope/);
await assert.rejects(invalidScope.client.control({}, "enable_no_store"), /not bound/);
for (const response of [{ok: false, status: 409}, {ok: false, status: 503}]) {
const current = harness([response]);
await assert.rejects(current.client.load(SCOPE), PrivacyContractError);
}
const malformed = harness([{body: {}}]);
await assert.rejects(malformed.client.load(SCOPE), /crossed/);
const wrongEffect = harness([{body: snapshot()}, {body: snapshot({revision: 2})}]);
const item = await wrongEffect.client.load(SCOPE);
await assert.rejects(wrongEffect.client.control(item, "enable_no_store"), /verified/);
const stale = harness([{body: snapshot()}, {body: noStore({revision: 1})}]);
await assert.rejects(stale.client.control(await stale.client.load(SCOPE),
"enable_no_store"), /verified/);
assert.throws(() => createPrivacyClient({client: null, fetcher: async () => ({})}), /requires/);
assert.throws(() => createPrivacyClient({client: {...invalidScope.foundation,
identity: {...IDENTITY, userRef: "bad"}}, fetcher: async () => ({})}), /identity/);
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = undefined;
assert.throws(() => createPrivacyClient({client: invalidScope.foundation}), /requires/);
} finally { globalThis.fetch = originalFetch; }
});