atlas-iac/testing/tests/test_hermes_hux_ui_onboarding.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

414 lines
19 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
createOnboardingClient,
OnboardingContractError,
} from "../../dockerfiles/hermes-webui-hux/onboarding/client.ts";
import {
FOUNDATION_FLAG,
FRIENDLY_MODES_FLAG,
ONBOARDING_FLAG,
PRIVACY_FLAG,
PROJECTS_FLAG,
buildActionIntent,
normalizeDecisionState,
normalizeEvaluation,
normalizeState,
normalizeSuggestion,
onboardingEnabled,
} from "../../dockerfiles/hermes-webui-hux/onboarding/model.ts";
import {
asRecord,
exactKeys,
isId,
isSafeText,
isUtc,
normalizeIdentity,
normalizeScope,
normalizeTrigger,
sameIdentity,
sameTrigger,
scopedSuggestionPath,
} from "../../dockerfiles/hermes-webui-hux/onboarding/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",
trigger: {surface: "chat", context: "after_approval"},
});
const FLAGS = [
FOUNDATION_FLAG, PROJECTS_FLAG, FRIENDLY_MODES_FLAG, ONBOARDING_FLAG,
PRIVACY_FLAG,
];
function suggestion(extra = {}) {
return {
schema: "hux.suggestion.v1",
id: "sug_alpha1234",
kind: "workflow",
trigger: SCOPE.trigger,
title: "Run this every week?",
body: "Hermes can prepare the same report each week after you confirm it.",
action: {type: "start_workflow", payload: {intent: "schedule_weekly"}},
priority: 35,
suppression: {
dismissable: true,
max_shows: 2,
cooldown_seconds: 86400,
never_again_supported: true,
},
...extra,
};
}
function state(extra = {}) {
return {
schema: "hux.suggestion_state.v1",
owner: IDENTITY.userRef,
suggestion_id: "sug_alpha1234",
shows: 1,
last_shown_at: "2026-08-24T10:00:00Z",
never_again: false,
...extra,
};
}
function evaluation(extra = {}) {
return {
schema: "hux.suggestion_evaluation.v1",
api_version: "hux.v1",
identity: RAW_IDENTITY,
session_id: SCOPE.sessionId,
conversation_id: SCOPE.conversationId,
trigger: SCOPE.trigger,
privacy: {
schema: "hux.onboarding_privacy.v1",
no_store: false,
memory_suggestions_allowed: true,
},
claim_id: "clm_alpha1234",
issued_at: "2026-08-24T10:00:01Z",
suggestion: suggestion(),
state: state(),
...extra,
};
}
function decisionEnvelope(item, decision = "dismiss", extra = {}) {
const timestamp = "2026-08-24T10:01:00Z";
return {
schema: "hux.suggestion_decision.v1",
api_version: "hux.v1",
identity: RAW_IDENTITY,
session_id: SCOPE.sessionId,
conversation_id: SCOPE.conversationId,
claim_id: "clm_alpha1234",
suggestion_id: "sug_alpha1234",
state: {
...state(),
...(decision === "acted" ? {acted_at: timestamp} : {dismissed_at: timestamp}),
never_again: decision === "never_again",
},
...extra,
...(item ? {state: item} : {}),
};
}
function harness(responses, flags = FLAGS) {
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: createOnboardingClient({client: foundation, fetcher})};
}
test("HUX-09 is default-off and requires all four dependencies", () => {
assert.equal(onboardingEnabled(), false);
assert.equal(onboardingEnabled([]), false);
for (const flag of FLAGS.slice(0, 4)) {
assert.equal(onboardingEnabled(FLAGS.slice(0, 4).filter((item) => item !== flag)), false);
}
assert.equal(onboardingEnabled(FLAGS.slice(0, 4)), true);
});
test("security primitives bind exact identity, session and trigger", () => {
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("sug_alpha1234", "sug"), true);
assert.equal(isId("bad", "sug"), false);
assert.equal(isId("clm_alpha1234", "sug"), false);
assert.equal(isUtc("2026-08-24T10:00:00Z"), true);
assert.equal(isUtc("2026-99-99T10:00:00Z"), false);
assert.equal(isSafeText("hello", 1, 10), true);
assert.equal(isSafeText("bad\u0000", 1, 10), 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(normalizeTrigger(SCOPE.trigger), SCOPE.trigger);
for (const raw of [null, {}, {...SCOPE.trigger, extra: true},
{...SCOPE.trigger, surface: "browser"}, {...SCOPE.trigger, context: "often"}])
assert.equal(normalizeTrigger(raw), null);
assert.equal(sameTrigger(SCOPE.trigger, SCOPE.trigger), true);
assert.equal(sameTrigger(SCOPE.trigger, {...SCOPE.trigger, context: "idle"}), false);
assert.deepEqual(normalizeScope(SCOPE, IDENTITY), SCOPE);
for (const raw of [{...SCOPE, extra: true}, {...SCOPE, sessionId: "bad"},
{...SCOPE, conversationId: "bad"},
{...SCOPE, trigger: {...SCOPE.trigger, surface: "worker"}}])
assert.equal(normalizeScope(raw, IDENTITY), null);
assert.equal(scopedSuggestionPath("/onboarding/suggestions/", "sug_alpha1234"),
"/onboarding/suggestions/sug_alpha1234/decisions");
for (const [base, id] of [["//evil", "sug_alpha1234"], ["/x?y", "sug_alpha1234"],
["/../x", "sug_alpha1234"], ["/safe", "bad"]])
assert.throws(() => scopedSuggestionPath(base, id), TypeError);
});
test("suggestion schema normalizes all four contextual actions", () => {
const workflow = normalizeSuggestion(suggestion(), SCOPE.trigger);
assert.equal(workflow.action.payload.intent, "schedule_weekly");
assert.equal(workflow.suppression.cooldownSeconds, 86400);
const save = normalizeSuggestion(suggestion({
action: {type: "start_workflow", payload: {intent: "save_reusable"}},
}), SCOPE.trigger);
assert.equal(save.action.payload.intent, "save_reusable");
const project = normalizeSuggestion(suggestion({kind: "project",
action: {type: "create_project", payload: {prefill_title: "Launch plan"}}}), SCOPE.trigger);
assert.equal(project.action.payload.prefill_title, "Launch plan");
const memory = normalizeSuggestion(suggestion({kind: "feature",
action: {type: "open_memory", payload: {view: "suggestions"}}}), SCOPE.trigger);
assert.equal(memory.action.type, "open_memory");
const mode = normalizeSuggestion(suggestion({kind: "feature",
action: {type: "open_mode", payload: {mode: "research"}}}), SCOPE.trigger);
assert.equal(mode.action.payload.mode, "research");
const artifacts = normalizeSuggestion(suggestion({kind: "feature",
action: {type: "open_artifacts", payload: {view: "current"}}}), SCOPE.trigger);
assert.equal(artifacts.action.type, "open_artifacts");
const tip = normalizeSuggestion(suggestion({kind: "tip", action: undefined}), SCOPE.trigger);
assert.equal(tip.action.type, "none");
assert.deepEqual(normalizeSuggestion(suggestion({kind: "project",
action: {type: "create_project"}}), SCOPE.trigger).action.payload, {});
assert.deepEqual(normalizeSuggestion(suggestion({kind: "feature",
action: {type: "open_mode"}}), SCOPE.trigger).action.payload, {});
assert.deepEqual(normalizeSuggestion(suggestion({kind: "feature",
action: {type: "open_memory"}}), SCOPE.trigger).action.payload, {});
assert.deepEqual(normalizeSuggestion(suggestion({kind: "feature",
action: {type: "open_artifacts"}}), SCOPE.trigger).action.payload, {});
});
test("malformed, mismatched, or pushy suggestions fail closed", () => {
const invalid = [
null, {...suggestion(), extra: true}, {...suggestion(), schema: "hux.suggestion.v0"},
{...suggestion(), id: "bad"}, {...suggestion(), kind: "ad"},
{...suggestion(), title: ""}, {...suggestion(), title: "x".repeat(81)},
{...suggestion(), body: "bad\u0000"}, {...suggestion(), priority: 101},
{...suggestion(), trigger: {...SCOPE.trigger, context: "idle"}},
{...suggestion(), suppression: null},
{...suggestion(), suppression: {...suggestion().suppression, dismissable: false}},
{...suggestion(), suppression: {...suggestion().suppression, max_shows: 6}},
{...suggestion(), suppression: {...suggestion().suppression, cooldown_seconds: 30}},
{...suggestion(), suppression: {...suggestion().suppression, never_again_supported: false}},
{...suggestion(), action: null},
{...suggestion(), action: {type: "unknown"}},
{...suggestion(), action: {type: "start_workflow", payload: {intent: "daily"}}},
{...suggestion(), action: {type: "start_workflow", payload: {intent: "schedule_weekly", extra: 1}}},
{...suggestion(), action: {type: "create_project", payload: {prefill_title: ""}}},
{...suggestion(), action: {type: "create_project", payload: {prefill_title: "x".repeat(121)}}},
{...suggestion(), action: {type: "create_project", payload: {extra: true}}},
{...suggestion(), action: {type: "open_mode", payload: {mode: "turbo"}}},
{...suggestion(), action: {type: "open_mode", payload: {extra: true}}},
{...suggestion(), action: {type: "open_memory", payload: {view: "all"}}},
{...suggestion(), action: {type: "open_memory", payload: {extra: true}}},
{...suggestion(), action: {type: "open_artifacts", payload: {view: "all"}}},
{...suggestion(), action: {type: "open_artifacts", payload: {extra: true}}},
{...suggestion(), action: {type: "none", payload: {surprise: true}}},
{...suggestion(), kind: "project"},
];
invalid.forEach((raw) => assert.equal(normalizeSuggestion(raw, SCOPE.trigger), null));
});
test("eligible state is owner-bound and inside the server rate limit", () => {
const item = normalizeSuggestion(suggestion(), SCOPE.trigger);
assert.equal(normalizeState(state(), item, IDENTITY.userRef).shows, 1);
const complete = normalizeState(state({dismissed_at: "2026-08-24T10:01:00Z",
acted_at: "2026-08-24T10:02:00Z"}), item, IDENTITY.userRef);
assert.equal(complete.dismissedAt, "2026-08-24T10:01:00Z");
assert.equal(complete.actedAt, "2026-08-24T10:02:00Z");
const invalid = [null, {...state(), extra: true}, {...state(), schema: "old"},
{...state(), owner: "usr_aaaaaaaaaaaaaaaa"}, {...state(), suggestion_id: "sug_other1234"},
{...state(), shows: 0}, {...state(), shows: 3}, {...state(), last_shown_at: "today"},
{...state(), never_again: true}, {...state(), dismissed_at: "today"},
{...state(), acted_at: "today"}];
invalid.forEach((raw) => assert.equal(normalizeState(raw, item, IDENTITY.userRef), null));
});
test("evaluation binds identity, session, trigger, privacy and one claim", () => {
const item = normalizeEvaluation(evaluation(), IDENTITY, SCOPE, true);
assert.equal(item.claimId, "clm_alpha1234");
assert.equal(item.state.owner, IDENTITY.userRef);
assert.equal(normalizeEvaluation(evaluation({privacy: {
schema: "hux.onboarding_privacy.v1", no_store: true,
memory_suggestions_allowed: false}, claim_id: null, suggestion: null, state: null}),
IDENTITY, SCOPE, true), null);
assert.equal(normalizeEvaluation(evaluation({suggestion: null, claim_id: null, state: null}),
IDENTITY, SCOPE, true), null);
const memoryRaw = evaluation({suggestion: suggestion({kind: "feature",
action: {type: "open_memory", payload: {view: "suggestions"}}})});
assert.equal(normalizeEvaluation(memoryRaw, IDENTITY, SCOPE, false), null);
assert.equal(normalizeEvaluation({...memoryRaw, privacy: {...memoryRaw.privacy,
memory_suggestions_allowed: false}}, IDENTITY, SCOPE, true), null);
assert.ok(normalizeEvaluation(memoryRaw, IDENTITY, SCOPE, true));
const invalid = [null, {...evaluation(), extra: true}, {...evaluation(), schema: "old"},
{...evaluation(), api_version: "hux.v0"},
{...evaluation(), identity: {...RAW_IDENTITY, surface: "worker"}},
{...evaluation(), session_id: "ses_other1234"},
{...evaluation(), conversation_id: "conv_other1234"},
{...evaluation(), trigger: {...SCOPE.trigger, context: "idle"}},
{...evaluation(), privacy: null},
{...evaluation(), privacy: {...evaluation().privacy, extra: true}},
{...evaluation(), privacy: {...evaluation().privacy, no_store: "no"}},
{...evaluation(), claim_id: "bad"}, {...evaluation(), issued_at: "today"},
{...evaluation(), state: {...state(), owner: "usr_aaaaaaaaaaaaaaaa"}}];
invalid.forEach((raw) => assert.equal(normalizeEvaluation(raw, IDENTITY, SCOPE, true), null));
});
test("actions bridge only validated HUX-03/HUX-06 interfaces", () => {
const make = (change) => normalizeEvaluation(evaluation({
suggestion: suggestion(change),
}), IDENTITY, SCOPE, true);
assert.deepEqual(buildActionIntent(make({})),
{type: "start_workflow", intent: "schedule_weekly"});
assert.deepEqual(buildActionIntent(make({kind: "project", action: {
type: "create_project", payload: {prefill_title: "Launch"}}})),
{type: "create_project", conversationId: SCOPE.conversationId, prefillTitle: "Launch"});
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
type: "open_mode", payload: {mode: "fast"}}})), {type: "open_mode", mode: "fast"});
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
type: "open_mode"}})), {type: "open_mode"});
assert.deepEqual(buildActionIntent(make({kind: "project", action: {
type: "create_project"}})),
{type: "create_project", conversationId: SCOPE.conversationId});
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
type: "open_memory"}})), {type: "open_memory", view: "suggestions"});
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
type: "open_artifacts"}})), {type: "open_artifacts", view: "current"});
assert.equal(buildActionIntent(make({kind: "tip", action: {type: "none"}})), null);
});
test("decision states preserve shows and prove the chosen outcome", () => {
const item = normalizeEvaluation(evaluation(), IDENTITY, SCOPE, true);
for (const decision of ["dismiss", "never_again", "acted"]) {
const raw = decisionEnvelope(null, decision).state;
const normalized = normalizeDecisionState(raw, item, decision);
assert.ok(normalized);
assert.equal(normalized.neverAgain, decision === "never_again");
}
for (const raw of [null, {...state(), extra: true}, {...state(), shows: 2},
{...state(), last_shown_at: "2026-08-24T11:00:00Z"},
{...state(), never_again: "yes"}, {...state(), dismissed_at: "today"},
{...state(), acted_at: "today"}, {...state(), acted_at: "2026-08-24T10:01:00Z"}])
assert.equal(normalizeDecisionState(raw, item, "dismiss"), null);
assert.ok(normalizeDecisionState({...state(), dismissed_at: "2026-08-24T10:01:00Z",
acted_at: "2026-08-24T10:02:00Z"}, item, "dismiss"));
});
test("client evaluates exact triggers with no-store same-origin requests", async () => {
const current = harness([{body: evaluation()}]);
assert.equal(current.client.enabled(), true);
const item = await current.client.evaluate(SCOPE);
assert.equal(item.suggestion.title, "Run this every week?");
assert.equal(current.calls[0].url, "/hux/v1/onboarding/evaluate");
assert.equal(current.calls[0].init.method, "POST");
assert.equal(current.calls[0].init.cache, "no-store");
assert.equal(current.calls[0].init.credentials, "same-origin");
assert.deepEqual(JSON.parse(current.calls[0].init.body), {
schema: "hux.suggestion_evaluation_request.v1", session_id: SCOPE.sessionId,
conversation_id: SCOPE.conversationId, trigger: SCOPE.trigger,
});
const replay = harness([{body: evaluation()}, {body: evaluation()}]);
assert.ok(await replay.client.evaluate(SCOPE));
assert.equal(await replay.client.evaluate(SCOPE), null);
const empty = harness([{status: 204}, {status: 404}]);
assert.equal(await empty.client.evaluate(SCOPE), null);
assert.equal(await empty.client.evaluate(SCOPE), null);
});
test("client persists explicit decisions without performing suggested work", async () => {
for (const decision of ["dismiss", "never_again", "acted"]) {
const current = harness([{body: evaluation()}, {body: decisionEnvelope(null, decision)}]);
const item = await current.client.evaluate(SCOPE);
const after = await current.client.decide(item, decision);
assert.equal(after.neverAgain, decision === "never_again");
assert.equal(current.calls[1].url,
"/hux/v1/onboarding/suggestions/sug_alpha1234/decisions");
assert.deepEqual(JSON.parse(current.calls[1].init.body), {
schema: "hux.suggestion_decision_request.v1", session_id: SCOPE.sessionId,
conversation_id: SCOPE.conversationId, claim_id: item.claimId,
suggestion_id: item.suggestion.id, expected_shows: 1, decision,
});
await assert.rejects(current.client.decide(item, decision), /not bound/);
}
});
test("client rejects disabled, malformed, cross-scope, stale, and unissued data", async () => {
const disabled = harness([], [FOUNDATION_FLAG, PROJECTS_FLAG]);
assert.equal(disabled.client.enabled(), false);
await assert.rejects(disabled.client.evaluate(SCOPE), /not enabled/);
disabled.foundation.apiVersion = "hux.v0";
await assert.rejects(disabled.client.evaluate(SCOPE), /not enabled/);
const invalidScope = harness([]);
await assert.rejects(invalidScope.client.evaluate({...SCOPE, sessionId: "bad"}), /outside/);
await assert.rejects(invalidScope.client.decide({claimId: "clm_fake1234"}, "dismiss"), /not bound/);
await assert.rejects(invalidScope.client.decide({}, "delete"), /not bound/);
for (const response of [{ok: false, status: 409}, {ok: false, status: 503}]) {
const current = harness([response]);
await assert.rejects(current.client.evaluate(SCOPE), OnboardingContractError);
}
const malformed = harness([{body: {}}]);
assert.equal(await malformed.client.evaluate(SCOPE), null);
const wrongDecision = harness([{body: evaluation()}, {body: decisionEnvelope(null, "dismiss", {
identity: {...RAW_IDENTITY, surface: "worker"}})}]);
const item = await wrongDecision.client.evaluate(SCOPE);
await assert.rejects(wrongDecision.client.decide(item, "dismiss"), /crossed/);
const missing = harness([{body: evaluation()}, {status: 204}]);
await assert.rejects(missing.client.decide(await missing.client.evaluate(SCOPE), "dismiss"), /crossed/);
assert.throws(() => createOnboardingClient({client: null, fetcher: async () => ({})}), /requires/);
assert.throws(() => createOnboardingClient({client: {...invalidScope.foundation,
identity: {...IDENTITY, tenantRef: "bad"}}, fetcher: async () => ({})}), /identity/);
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = undefined;
assert.throws(() => createOnboardingClient({client: invalidScope.foundation}), /requires/);
} finally { globalThis.fetch = originalFetch; }
});