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

243 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from "node:assert/strict";
import test from "node:test";
import {createResearchEndpointContract} from "../../dockerfiles/hermes-webui-hux/research/endpoints.ts";
import {
FOUNDATION_FLAG, FRIENDLY_MODES_FLAG, RESEARCH_FLAG, normalizeResearchPage, researchEnabled,
} from "../../dockerfiles/hermes-webui-hux/research/model.ts";
import {
asRecord, boundedText, isHash, isId, isUtc, normalizeIdentity, normalizeScope,
safeMessageId, safePathId, safeSourceUri, sameIdentity, sameScope,
} from "../../dockerfiles/hermes-webui-hux/research/security.ts";
const IDENTITY = {tenantRef: "tnt_0123456789abcdef", userRef: "usr_0123456789abcdef", surface: "chat"};
const RAW_IDENTITY = {tenant_ref: IDENTITY.tenantRef, user_ref: IDENTITY.userRef, surface: "chat"};
const SCOPE = {projectId: "prj_project123", conversationId: "conv_converse1"};
const RAW_SCOPE = {project_id: SCOPE.projectId, conversation_id: SCOPE.conversationId};
const HASH = `sha256:${"a".repeat(64)}`;
const FLAGS = [FOUNDATION_FLAG, FRIENDLY_MODES_FLAG, RESEARCH_FLAG];
function source(extra = {}) {
return {schema: "hux.source.v1", id: "src_primary12", kind: "web",
uri: "https://example.com/report", title: "Primary report", publisher: "Example Institute",
published_at: "2026-08-20T10:00:00Z", retrieved_at: "2026-08-24T10:00:00Z",
classification: "primary", content_hash: HASH, provenance: {surface: "chat",
actor: {type: "assistant", id: "hermes"}, recorded_at: "2026-08-24T10:00:01Z",
conversation_id: SCOPE.conversationId, run_id: "run_research"}, ...extra};
}
function passage(extra = {}) {
return {schema: "hux.passage.v1", id: "psg_passage12", source_id: "src_primary12",
text: "The exact supporting passage.\nSecond line.", hash: HASH,
locator: {page: 2, selector: "#finding", line_start: 10, line_end: 12,
char_start: 50, char_end: 91}, ...extra};
}
function citation(extra = {}) {
return {schema: "hux.citation.v1", id: "cit_citation1", message_id: "msg-44",
claim: "The report supports this claim.", passage_ids: ["psg_passage12"],
support: "supports", note: "Check the methodology.", ...extra};
}
function notebook(extra = {}) {
return {schema: "hux.research_notebook.v1", id: "nb_notebook12",
conversation_id: SCOPE.conversationId, question: "What does the evidence show?", status: "open",
source_ids: ["src_primary12"], passage_ids: ["psg_passage12"], citation_ids: ["cit_citation1"],
assumptions: ["The publication date is accurate."], unresolved_questions: ["Was it peer reviewed?"],
updated_at: "2026-08-24T10:00:02Z", ...extra};
}
function page(extra = {}) {
return {schema: "hux.research_page.v1", api_version: "hux.v1", identity: RAW_IDENTITY,
binding: RAW_SCOPE, sources: [source()], passages: [passage()], citations: [citation()],
notebooks: [notebook()], ...extra};
}
test("research stays default-off until its declared capabilities are active", () => {
assert.equal(researchEnabled(), false);
assert.equal(researchEnabled([]), false);
for (const flag of FLAGS) assert.equal(researchEnabled(FLAGS.filter((item) => item !== flag)), false);
assert.equal(researchEnabled(FLAGS), true);
});
test("security helpers enforce exact identity, scope, text, IDs, timestamps and hashes", () => {
assert.deepEqual(asRecord({a: 1}), {a: 1});
assert.equal(asRecord(null), null);
assert.equal(asRecord([]), null);
assert.equal(isId("src_primary12", "src"), true);
assert.equal(isId("psg_passage12", "src"), false);
assert.equal(isId(4), false);
assert.equal(isUtc("2026-08-24T10:00:00.123Z"), true);
assert.equal(isUtc("today"), false);
assert.equal(isUtc(4), false);
assert.equal(isHash(HASH), true);
assert.equal(isHash(`sha256:${"z".repeat(64)}`), false);
assert.equal(isHash(4), false);
assert.equal(boundedText("hello", 1, 5), true);
assert.equal(boundedText("", 1, 5), false);
assert.equal(boundedText("bad\u0000", 1, 10), false);
assert.equal(boundedText(4, 1, 5), false);
assert.deepEqual(normalizeIdentity(RAW_IDENTITY), IDENTITY);
for (const bad of [null, [], {...RAW_IDENTITY, tenant_ref: "bad"},
{...RAW_IDENTITY, user_ref: "bad"}, {...RAW_IDENTITY, surface: "browser"}]) {
assert.equal(normalizeIdentity(bad), null);
}
assert.deepEqual(normalizeScope(RAW_SCOPE), SCOPE);
assert.equal(normalizeScope(null), null);
assert.equal(normalizeScope({...RAW_SCOPE, project_id: "bad"}), null);
assert.equal(normalizeScope({...RAW_SCOPE, conversation_id: "bad"}), null);
assert.equal(sameIdentity(IDENTITY, IDENTITY), true);
assert.equal(sameIdentity(null, IDENTITY), false);
assert.equal(sameIdentity({...IDENTITY, surface: "voice"}, IDENTITY), false);
assert.equal(sameScope(SCOPE, SCOPE), true);
assert.equal(sameScope(null, SCOPE), false);
assert.equal(sameScope({...SCOPE, conversationId: "conv_other1234"}, SCOPE), false);
});
test("source URLs allow HTTPS and HUX paths but reject active or ambiguous schemes", () => {
assert.equal(safeSourceUri(undefined), null);
assert.equal(safeSourceUri(null), null);
assert.equal(safeSourceUri(""), null);
assert.equal(safeSourceUri("https://example.com/a?q=1"), "https://example.com/a?q=1");
assert.equal(safeSourceUri("/hux/v1/artifacts/art_safe1234"), "/hux/v1/artifacts/art_safe1234");
for (const unsafe of ["http://example.com", "javascript:alert(1)", "data:text/html,x",
"//example.com/x", "/hux/v1/../secret", "/hux/v1/x?q=1", "https://u:p@example.com", "not a url",
`https://example.com/${"x".repeat(2000)}`, "https://example.com/\u0000"] ) {
assert.equal(safeSourceUri(unsafe), null);
}
assert.equal(safeMessageId("msg-44"), "msg-44");
assert.throws(() => safeMessageId("bad/path"), TypeError);
assert.throws(() => safeMessageId(""), TypeError);
assert.equal(safePathId("nb_notebook12", "nb"), "nb_notebook12");
assert.throws(() => safePathId("bad", "nb"), TypeError);
});
test("valid research keeps provenance, bounded passages, support, and notebook notes", () => {
const result = normalizeResearchPage(page(), IDENTITY, SCOPE);
assert.equal(result.rejected, 0);
assert.equal(result.sources[0].classification, "primary");
assert.equal(result.sources[0].provenance.actorType, "assistant");
assert.equal(result.sources[0].provenance.runId, "run_research");
assert.equal(result.passages[0].text, "The exact supporting passage.\nSecond line.");
assert.equal(result.passages[0].locator, "page 2 · #finding · lines 1012 · characters 5091");
assert.equal(result.citations[0].support, "supports");
assert.deepEqual(result.notebooks[0].notes, ["Check the methodology."]);
assert.deepEqual(result.notebooks[0].unresolvedQuestions, ["Was it peer reviewed?"]);
});
test("envelope rejects cross-tenant, user, project and conversation data", () => {
const bad = [null, {}, page({schema: "old"}), page({api_version: "hux.v0"}),
page({identity: {...RAW_IDENTITY, tenant_ref: "tnt_ffffffffffffffff"}}),
page({identity: {...RAW_IDENTITY, user_ref: "usr_ffffffffffffffff"}}),
page({identity: {...RAW_IDENTITY, surface: "voice"}}),
page({binding: {...RAW_SCOPE, project_id: "prj_other1234"}}),
page({binding: {...RAW_SCOPE, conversation_id: "conv_other1234"}}),
page({sources: null}), page({sources: Array(501).fill(source())}),
page({passages: null}), page({passages: Array(501).fill(passage())}),
page({citations: null}), page({citations: Array(501).fill(citation())}),
page({notebooks: null}), page({notebooks: Array(101).fill(notebook())})];
for (const raw of bad) assert.equal(normalizeResearchPage(raw, IDENTITY, SCOPE), null);
});
test("invalid source metadata, provenance and URL are withheld with dependants", () => {
const badSources = [null, source({schema: "old"}), source({id: "bad"}), source({kind: "video"}),
source({title: ""}), source({publisher: "x".repeat(201)}), source({retrieved_at: "today"}),
source({classification: "official"}), source({published_at: "today"}), source({content_hash: "bad"}),
source({published_at: "2026-08-25T10:00:00Z"}),
source({uri: "javascript:alert(1)"}), source({provenance: null}),
source({provenance: {...source().provenance, surface: "browser"}}),
source({provenance: {...source().provenance, actor: null}}),
source({provenance: {...source().provenance, actor: {type: "intruder"}}}),
source({provenance: {...source().provenance, actor: {type: "assistant", id: ""}}}),
source({provenance: {...source().provenance, recorded_at: "today"}}),
source({provenance: {...source().provenance, conversation_id: "conv_other1234"}}),
source({provenance: {...source().provenance, run_id: ""}})];
for (const raw of badSources) {
const result = normalizeResearchPage(page({sources: [raw]}), IDENTITY, SCOPE);
assert.equal(result.sources.length, 0);
assert.equal(result.passages.length, 0);
assert.equal(result.citations.length, 0);
assert.equal(result.notebooks.length, 0);
}
const withoutOptional = normalizeResearchPage(page({sources: [source({uri: undefined,
publisher: undefined, published_at: undefined, content_hash: undefined, provenance: undefined})]}), IDENTITY, SCOPE);
assert.equal(withoutOptional.sources[0].uri, null);
assert.equal(withoutOptional.sources[0].provenance, null);
});
test("passage locators and citations are schema-bounded and referentially complete", () => {
const invalidPassages = [null, passage({schema: "old"}), passage({id: "bad"}),
passage({source_id: "src_missing12"}), passage({text: ""}), passage({text: "x".repeat(4001)}),
passage({hash: "bad"}), passage({locator: null}), passage({locator: {unknown: 1}}),
passage({locator: {page: 0}}), passage({locator: {selector: "x".repeat(501)}}),
passage({locator: {line_start: 0}}), passage({locator: {line_end: 0}}),
passage({locator: {char_start: -1}}), passage({locator: {char_end: -1}}),
passage({locator: {line_start: 10, line_end: 9}}), passage({locator: {char_start: 10, char_end: 9}})];
for (const raw of invalidPassages) {
assert.equal(normalizeResearchPage(page({passages: [raw]}), IDENTITY, SCOPE).passages.length, 0);
}
assert.equal(normalizeResearchPage(page({passages: [passage({locator: undefined})]}), IDENTITY, SCOPE)
.passages[0].locator, "");
assert.equal(normalizeResearchPage(page({passages: [passage({locator: {line_start: 4}})]}), IDENTITY, SCOPE)
.passages[0].locator, "lines 4");
const invalidCitations = [null, citation({schema: "old"}), citation({id: "bad"}),
citation({message_id: ""}), citation({claim: ""}), citation({passage_ids: null}),
citation({passage_ids: []}), citation({passage_ids: Array(33).fill("psg_passage12")}),
citation({passage_ids: ["psg_passage12", "psg_passage12"]}),
citation({passage_ids: ["psg_missing12"]}), citation({support: "maybe"}),
citation({note: "x".repeat(501)})];
for (const raw of invalidCitations) {
assert.equal(normalizeResearchPage(page({citations: [raw]}), IDENTITY, SCOPE).citations.length, 0);
}
const allSupport = ["supports", "partially_supports", "contradicts", "unverified"];
for (const support of allSupport) assert.equal(normalizeResearchPage(page({citations: [citation({support})]}),
IDENTITY, SCOPE).citations[0].support, support);
});
test("notebooks validate references, lists, status, conversation and duplicates", () => {
const invalid = [null, notebook({schema: "old"}), notebook({id: "bad"}),
notebook({conversation_id: "conv_other1234"}), notebook({question: ""}), notebook({status: "paused"}),
notebook({updated_at: "today"}), notebook({source_ids: null}), notebook({source_ids: ["src_missing12"]}),
notebook({source_ids: ["src_primary12", "src_primary12"]}), notebook({passage_ids: null}),
notebook({passage_ids: ["psg_missing12"]}), notebook({citation_ids: null}),
notebook({citation_ids: ["cit_missing12"]}), notebook({assumptions: null}),
notebook({assumptions: Array(65).fill("x")}), notebook({assumptions: [""]}),
notebook({unresolved_questions: null}), notebook({unresolved_questions: Array(65).fill("x")}),
notebook({unresolved_questions: [""]})];
for (const raw of invalid) {
assert.equal(normalizeResearchPage(page({notebooks: [raw]}), IDENTITY, SCOPE).notebooks.length, 0);
}
for (const status of ["open", "answered", "abandoned"]) {
assert.equal(normalizeResearchPage(page({notebooks: [notebook({status})]}), IDENTITY, SCOPE)
.notebooks[0].status, status);
}
const duplicate = normalizeResearchPage(page({sources: [source(), source()],
passages: [passage(), passage()], citations: [citation(), citation()], notebooks: [notebook(), notebook()]}),
IDENTITY, SCOPE);
assert.deepEqual([duplicate.sources.length, duplicate.passages.length, duplicate.citations.length,
duplicate.notebooks.length], [1, 1, 1, 1]);
assert.equal(duplicate.rejected, 4);
});
test("endpoint contract is scoped, inert, optimistic, and same-origin delegated", () => {
const client = {apiVersion: "hux.v1", identity: IDENTITY,
endpoint(path) { assert.match(path, /^\/projects\/prj_[^/]+\/conversations\/conv_[^/]+\/research/);
return `/hux/v1${path}`; }};
assert.equal(createResearchEndpointContract(client, SCOPE), null);
assert.equal(createResearchEndpointContract(client, SCOPE, FLAGS.slice(1)), null);
const contract = createResearchEndpointContract(client, SCOPE, FLAGS);
assert.equal(contract.page.path, "/hux/v1/projects/prj_project123/conversations/conv_converse1/research");
assert.match(contract.citationsForMessage("msg-44").path, /messages\/msg-44\/citations$/);
assert.match(contract.notebook("nb_notebook12").path, /notebooks\/nb_notebook12$/);
assert.deepEqual(contract.updateNotebook("nb_notebook12").requiredBody,
["expected_updated_at", "status", "assumptions", "unresolved_questions"]);
assert.deepEqual(contract.attachCitation("msg-44").requiredBody,
["claim", "passage_ids", "support", "note"]);
for (const call of [() => contract.citationsForMessage("bad/path"), () => contract.notebook("bad"),
() => contract.updateNotebook("bad")]) assert.throws(call, TypeError);
assert.throws(() => createResearchEndpointContract({...client, apiVersion: "hux.v2"}, SCOPE, FLAGS), TypeError);
assert.throws(() => createResearchEndpointContract({...client, identity: {...IDENTITY, tenantRef: "bad"}}, SCOPE, FLAGS), TypeError);
assert.throws(() => createResearchEndpointContract(client, {...SCOPE, projectId: "bad"}, FLAGS), TypeError);
});