From b2a9154b9e696a2be00422c6a8b3e4b86cd3c35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=BAper=20=28Yousef=29=20Anas?= <97895153+Cuuper22@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:38:35 -0700 Subject: [PATCH] fix: harden evidence staging and result semantics --- CHALLENGE.md | 4 +- docs/observatory.html | 10 ++-- docs/webmcp-mission.js | 85 +++++++++++++++++++-------- docs/webmcp-tools.js | 40 +++++-------- evals/webmcp-evals.json | 84 ++++++++++++++++++++++++-- tests/test_webmcp_contract.py | 30 +++++----- tests/test_webmcp_mission_runtime.mjs | 46 +++++++++++---- 7 files changed, 209 insertions(+), 90 deletions(-) diff --git a/CHALLENGE.md b/CHALLENGE.md index 62a4e5b..878a0a0 100644 --- a/CHALLENGE.md +++ b/CHALLENGE.md @@ -75,9 +75,9 @@ Registration occurs in the top-level document. If `document.modelContext` is una | `trace_causal_path` | Read-only | Finds and highlights a bounded path through the seven-node conceptual evidence graph without collapsing its branches or evidence classes | | `open_evidence` | Read-only | Opens a registered artifact, source result, assumption, uncertainty item, or missing-evidence boundary at a chosen semantic depth | | `compare_policies` | Read-only | Compares up to three registered policies; by default it uses `observable_adaptive` and the calibration-frozen `periodic_local` comparator | -| `stage_conclusion` | Staging write | Places a supported, qualified, or abstain claim plus one to eight evidence IDs in the pending tray; it cannot approve or commit the claim | +| `stage_conclusion` | Staging write | Places the artifact's typed `abstain_without_policy_claim` conclusion plus one to eight evidence IDs in the pending tray; free-form agent claims are rejected and it cannot approve or commit the conclusion | -The first seven tools are annotated read-only. `stage_conclusion` affects only local pending-review state, never the source experiment JSON. No WebMCP tool can approve, reject, or undo a conclusion: those actions are page-only human controls. The agent can explore broadly and prepare a coherent evidence bundle, but it must stop at the judgment boundary. +The first seven tools are annotated read-only. `stage_conclusion` requires the current state version, refuses to overwrite an existing pending review, and affects only local pending-review state—never the source experiment JSON. No WebMCP tool can approve, reject, edit, or undo a conclusion: those actions are page-only human controls. The agent can explore broadly and prepare a coherent evidence bundle, but it must stop at the judgment boundary. ## Testing diff --git a/docs/observatory.html b/docs/observatory.html index 7b7a9ad..0216331 100644 --- a/docs/observatory.html +++ b/docs/observatory.html @@ -15,10 +15,10 @@ - - - - + + + + @@ -97,7 +97,7 @@

WebMCP causal mission con
- +
diff --git a/docs/webmcp-mission.js b/docs/webmcp-mission.js index 96d687b..5c0fcb3 100644 --- a/docs/webmcp-mission.js +++ b/docs/webmcp-mission.js @@ -6,11 +6,20 @@ const PROJECTION_URL = "data/webmcp-run-projection-v1.json.gz"; const STORAGE_KEY = "gpustack.webmcp.mission.v1"; const MAX_RECEIPTS = 40; + const CANONICAL_CONCLUSIONS = Object.freeze({ + abstain_without_policy_claim: "The observable adaptive controller does not earn a transferable winner claim: it abstained under out-of-distribution stress, and every frozen aggregate gate failed.", + }); + const RECEIPT_ORIGIN_LABELS = Object.freeze({ + webmcp: "WEBMCP", + local_tour: "LOCAL TOUR", + human: "HUMAN", + }); const REGISTERED_METRICS = Object.freeze([ "final_held_out_nll", "modeled_completion_seconds", "inter_site_payload_bytes", - "abstention_count", + "controller_abstentions", + "support_envelope_flag_count", "replayed_tokens", "divergence_count", ]); @@ -274,12 +283,15 @@ artifact: { experiment_id: semantic.experiment_id, sha256: semantic.artifact_sha256, - raw_sha256: semantic.full_trace.raw_trace_artifact.artifact_sha256, families: families.length, runs: runLedger(semantic).length, epochs: semantic.full_trace.raw_trace_artifact.epoch_count || 12981, }, - evidence_boundary: "Measured learning + exact accounting; modeled infrastructure; frontier transfer unresolved.", + evidence_boundary: { + measured: "learning + exact accounting", + modeled: "virtual infrastructure", + unresolved: "frontier/facility transfer", + }, frozen_result: { conclusion: semantic.status.conclusion, all_falsifiers_pass: semantic.status.all_falsifiers_pass, @@ -288,9 +300,9 @@ }, registered_ids: { families: families.map((family) => family.family_id), - policies: [...new Set(runLedger(semantic).map((run) => run.policy_id))], + policies: ["observable_adaptive", semantic.comparison.selected_fixed_policy_id], causal_nodes: screening.causal_graph.nodes.map((node) => node.node_id), - effect_ids: effects.map((effect) => effect.effect_id), + failed_gates: effects.filter((effect) => effect.passed === false).map((effect) => effect.effect_id), }, pending_proposal: missionState.pending ? missionState.pending.proposalId : null, suggested_next: "compare_stress_families", @@ -426,8 +438,8 @@ split: run.split, seed: run.seed, epoch_count: run.epoch_count, - abstention_count: run.abstention_count, - ood_epoch_count: run.out_of_distribution_epoch_count, + controller_abstentions: run.policy_id === "observable_adaptive" ? run.abstention_count : null, + support_envelope_flag_count: run.out_of_distribution_epoch_count, final_held_out_nll: run.final_held_out_nll, completion_seconds: run.modeled_infrastructure?.completion_seconds, inter_site_payload_bytes: run.modeled_infrastructure?.inter_site_payload_bytes, @@ -512,7 +524,7 @@ } if (rawAliases.has(evidenceId)) { if (navigate) { - await bridge.selectView({ experiment: "E001-SC1", depth: "researcher" }); + await bridge.selectView({ experiment: "E001-SC1", depth: "full_trace" }); scrollToId("semantic-consistency-raw-details"); } return { @@ -562,7 +574,8 @@ policy_id: run.policy_id, final_held_out_nll: run.final_held_out_nll, completion_seconds: run.modeled_infrastructure?.completion_seconds, - abstentions: run.abstention_count, + controller_abstentions: run.policy_id === "observable_adaptive" ? run.abstention_count : null, + support_envelope_flag_count: run.out_of_distribution_epoch_count, work_contract_violations: run.exact_accounting?.work_contract_violations || [], }; } @@ -602,7 +615,7 @@ const ledgerEntry = ledgers.find((entry) => [entry.assumption_id, entry.evidence_id, entry.id].includes(evidenceId)); if (ledgerEntry) { if (navigate) { - await bridge.selectView({ experiment: "E001-SC1", depth: "researcher" }); + await bridge.selectView({ experiment: "E001-SC1", depth: "full_trace" }); scrollToId("semantic-consistency-assumptions"); } return { evidence_id: evidenceId, kind: "evidence_boundary", entry: ledgerEntry }; @@ -634,7 +647,8 @@ final_held_out_nll: run.final_held_out_nll, modeled_completion_seconds: run.modeled_infrastructure?.completion_seconds, inter_site_payload_bytes: run.modeled_infrastructure?.inter_site_payload_bytes, - abstention_count: run.abstention_count, + controller_abstentions: run.policy_id === "observable_adaptive" ? run.abstention_count : null, + support_envelope_flag_count: run.out_of_distribution_epoch_count, replayed_tokens: run.exact_accounting?.replayed_tokens, divergence_count: run.diverged ? 1 : 0, }; @@ -646,7 +660,7 @@ const ledger = runLedger(semantic); const availablePolicies = [...new Set(ledger.map((run) => run.policy_id))]; const policyIds = args.policy_ids || ["observable_adaptive", semantic.comparison.selected_fixed_policy_id]; - const metricIds = args.metric_ids || REGISTERED_METRICS.slice(0, 5); + const metricIds = args.metric_ids || REGISTERED_METRICS.slice(0, 6); const unknownPolicy = policyIds.find((id) => !availablePolicies.includes(id)); const unknownMetric = metricIds.find((id) => !REGISTERED_METRICS.includes(id)); if (unknownPolicy) throw new MissionError("UNKNOWN_POLICY", `No registered policy named ${unknownPolicy}.`, { available_ids: availablePolicies }); @@ -655,10 +669,18 @@ const runs = ledger.filter((run) => run.split === "evaluation" && run.policy_id === policyId); const metrics = {}; metricIds.forEach((metricId) => { - const values = runs.map((run) => Number(metricValue(run, metricId))).filter(Number.isFinite); - metrics[metricId] = ["abstention_count", "divergence_count", "replayed_tokens"].includes(metricId) - ? values.reduce((sum, value) => sum + value, 0) - : median(values); + const values = runs + .map((run) => metricValue(run, metricId)) + .filter((value) => value !== null && value !== undefined) + .map(Number) + .filter(Number.isFinite); + if (!values.length) { + metrics[metricId] = null; + } else { + metrics[metricId] = ["controller_abstentions", "support_envelope_flag_count", "divergence_count", "replayed_tokens"].includes(metricId) + ? values.reduce((sum, value) => sum + value, 0) + : median(values); + } }); return { policy_id: policyId, evaluation_runs: runs.length, metrics }; }); @@ -685,27 +707,33 @@ async function stageConclusion(args, context) { const semantic = await semanticArtifact(context.signal); - if (args.expected_state_version !== undefined && args.expected_state_version !== missionState.stateVersion) { + if (args.expected_state_version !== missionState.stateVersion) { throw new MissionError("STALE_STATE", `Expected state version ${args.expected_state_version}, but current version is ${missionState.stateVersion}.`, { current_state_version: missionState.stateVersion, }); } - if (args.confidence === "supported" && semantic.status.all_falsifiers_pass !== true) { - throw new MissionError("EVIDENCE_CONFLICT", "A supported conclusion is not admissible: all four frozen aggregate gates failed. Use qualified or abstain and cite the failed gates.", { + if (missionState.pending) { + throw new MissionError("PENDING_REVIEW_EXISTS", `Human review is already pending for ${missionState.pending.proposalId}. Approve or reject it before staging another conclusion.`, { + pending_proposal: missionState.pending.proposalId, + }); + } + if (args.conclusion_code !== semantic.status.conclusion || !CANONICAL_CONCLUSIONS[args.conclusion_code]) { + throw new MissionError("EVIDENCE_CONFLICT", `The immutable artifact serializes ${semantic.status.conclusion}; a different conclusion cannot be staged.`, { frozen_conclusion: semantic.status.conclusion, - recommended_confidence: "abstain", }); } const resolved = []; for (const evidenceId of args.evidence_ids) { resolved.push(await resolveEvidence(evidenceId, "researcher", false, context.signal)); } + abortIfNeeded(context.signal); const proposal = { proposalId: `proposal-${String(missionState.nextProposal).padStart(3, "0")}`, - claim: args.claim, + claim: CANONICAL_CONCLUSIONS[args.conclusion_code], + conclusionCode: args.conclusion_code, evidenceIds: [...args.evidence_ids], evidenceKinds: resolved.map((entry) => entry.kind), - confidence: args.confidence, + confidence: "abstain", frozenConclusion: semantic.status.conclusion, createdAt: new Date().toISOString(), }; @@ -725,6 +753,7 @@ ok: true, proposal_id: proposal.proposalId, status: "pending_human_review", + conclusion_code: proposal.conclusionCode, confidence: proposal.confidence, evidence_ids: proposal.evidenceIds, frozen_conclusion: proposal.frozenConclusion, @@ -751,7 +780,7 @@ try { abortIfNeeded(context.signal); const result = await handler(args, context); - abortIfNeeded(context.signal); + if (toolName !== "stage_conclusion") abortIfNeeded(context.signal); refreshRegistrationStatus(); return result; } catch (error) { @@ -827,7 +856,8 @@ item.className = "webmcp-receipt"; item.dataset.status = receipt.status; const heading = document.createElement("strong"); - heading.textContent = `${receipt.tool.replaceAll("_", " ")} · ${receipt.status}`; + const originLabel = RECEIPT_ORIGIN_LABELS[receipt.origin] || String(receipt.origin || "webmcp").toUpperCase(); + heading.textContent = `${originLabel} · ${receipt.tool.replaceAll("_", " ")} · ${receipt.status}`; const summary = document.createElement("p"); summary.textContent = receipt.summary; const metadata = document.createElement("p"); @@ -958,14 +988,13 @@ await invoke("open_evidence", { evidence_id: "adaptive_minus_best_fixed_final_nll", semantic_depth: "researcher" }, { origin: "local_tour" }); await pause(220); await invoke("stage_conclusion", { - claim: "The observable adaptive controller does not earn a transferable winner claim: it abstained under out-of-distribution stress, and every frozen aggregate gate failed.", + conclusion_code: "abstain_without_policy_claim", evidence_ids: [ "E6-repeated-membership-loss", "adaptive_minus_best_fixed_final_nll", "adaptive_to_best_fixed_inter_site_payload_ratio", "adaptive_to_best_fixed_modeled_completion_time_ratio", ], - confidence: "abstain", expected_state_version: missionState.stateVersion, }, { origin: "local_tour" }); setStatus("Audit staged · waiting for human approval", "waiting"); @@ -990,6 +1019,10 @@ } function refreshRegistrationStatus(forcedState) { + if (!forcedState && missionState.pending) { + setStatus("Audit staged · waiting for human approval", "waiting"); + return; + } const webmcp = window.GPUStackWebMCP; if (webmcp?.supported) { webmcp.ready.then((status) => { diff --git a/docs/webmcp-tools.js b/docs/webmcp-tools.js index 179fc15..c31fb78 100644 --- a/docs/webmcp-tools.js +++ b/docs/webmcp-tools.js @@ -19,7 +19,7 @@ const MAX_RESULT_CHARS = 1500; const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,179}$/; const SEMANTIC_DEPTHS = ["freshman", "researcher", "full_trace"]; - const CONFIDENCE_LEVELS = ["supported", "qualified", "abstain"]; + const CONCLUSION_CODES = ["abstain_without_policy_claim"]; class ArgumentError extends Error { constructor(field, message, expected) { @@ -137,28 +137,22 @@ stage_conclusion: objectSchema( { - claim: { + conclusion_code: { type: "string", - minLength: 1, - maxLength: 600, - description: "Concise proposed conclusion grounded only in the cited evidence IDs.", + enum: CONCLUSION_CODES, + description: "Typed conclusion serialized by the immutable artifact. Free-form agent claims are not accepted.", }, evidence_ids: idArraySchema( - "One to eight evidence IDs that directly support or qualify the proposed claim.", + "One to eight evidence IDs that directly support the typed artifact conclusion.", 8, ), - confidence: { - type: "string", - enum: CONFIDENCE_LEVELS, - description: "Supported, qualified, or abstain. This is evidence confidence, not approval.", - }, expected_state_version: { type: "integer", minimum: 0, - description: "Optional optimistic-concurrency version returned by a prior tool call.", + description: "Required optimistic-concurrency version returned by get_observatory_state.", }, }, - ["claim", "evidence_ids", "confidence"], + ["conclusion_code", "evidence_ids", "expected_state_version"], ), }); @@ -313,16 +307,12 @@ }, stage_conclusion(args) { - checkObject(args, ["claim", "evidence_ids", "confidence", "expected_state_version"]); - const result = { - claim: cleanString(args.claim, "claim", { required: true, min: 1, max: 600 }), + checkObject(args, ["conclusion_code", "evidence_ids", "expected_state_version"]); + return { + conclusion_code: cleanEnum(args.conclusion_code, "conclusion_code", CONCLUSION_CODES), evidence_ids: cleanIdArray(args.evidence_ids, "evidence_ids", 8), - confidence: cleanEnum(args.confidence, "confidence", CONFIDENCE_LEVELS), + expected_state_version: cleanInteger(args.expected_state_version, "expected_state_version", 0), }; - if (args.expected_state_version !== undefined) { - result.expected_state_version = cleanInteger(args.expected_state_version, "expected_state_version", 0); - } - return result; }, }); @@ -354,7 +344,7 @@ { name: "inspect_run", title: "Inspect experiment run", - description: "Inspect one exact E001-SC1 run and a bounded page of scalar-projected optimizer-commit epochs. Returns mode choice, OOD and abstention state, completion, and event markers while preserving the authoritative raw-trace hash.", + description: "Inspect one exact E001-SC1 run and a bounded page of scalar-projected optimizer-commit epochs. Separates support-envelope flags from controller-only abstentions while preserving the authoritative raw-trace hash.", inputSchema: SCHEMAS.inspect_run, annotations: READ_ONLY, }, @@ -375,14 +365,14 @@ { name: "compare_policies", title: "Compare registered policies", - description: "Compare up to three policies from the immutable experiment artifact. Omit IDs for observable_adaptive versus the calibration-frozen periodic_local comparator across registered metrics.", + description: "Compare up to three policies from the immutable artifact. Separates controller-only abstentions from cross-policy support-envelope flags; defaults to observable_adaptive versus frozen periodic_local.", inputSchema: SCHEMAS.compare_policies, annotations: READ_ONLY, }, { name: "stage_conclusion", title: "Stage evidence conclusion", - description: "Stage a supported, qualified, or abstain conclusion with explicit evidence IDs in the visible pending tray. This never approves or commits it; only the human can approve or reject it in the page.", + description: "Stage the artifact's typed abstain conclusion with explicit evidence IDs and a current state version. Free-form agent claims are rejected. Only the human can approve, edit, or reject it.", inputSchema: SCHEMAS.stage_conclusion, annotations: STAGING_WRITE, }, @@ -485,7 +475,7 @@ try { const result = await bridge.invoke(toolName, validated, { signal }); - abortIfNeeded(signal); + if (toolName !== "stage_conclusion") abortIfNeeded(signal); return compactResult(toolName, result); } catch (error) { abortIfNeeded(signal); diff --git a/evals/webmcp-evals.json b/evals/webmcp-evals.json index 511b8b5..059af6b 100644 --- a/evals/webmcp-evals.json +++ b/evals/webmcp-evals.json @@ -64,8 +64,8 @@ "functionName": "inspect_run", "arguments": { "run_id": "e001-sc1:evaluation:E6-repeated-membership-loss:observable_adaptive", - "epoch_offset": 0, - "epoch_limit": 8 + "epoch_offset": 158, + "epoch_limit": 6 } } ] @@ -136,14 +136,14 @@ { "role": "user", "type": "message", - "content": "Stage the scientifically warranted conclusion that no transferable winner claim follows for observable_adaptive over periodic_local across the held-out stress families. Cite the decisive E6 family, both E6 policy runs, the failed learning effect, and the compact and raw semantic-consistency artifacts. Mark confidence as abstain. Do not approve or commit it." + "content": "Stage the artifact's typed abstain_without_policy_claim conclusion for observable_adaptive versus periodic_local. Cite the decisive E6 family, both E6 policy runs, the failed learning effect, and the compact and raw semantic-consistency artifacts. Use the current observatory state version. Do not approve or commit it." } ], "expectedCall": [ { "functionName": "stage_conclusion", "arguments": { - "claim": "No transferable winner claim follows for observable_adaptive over periodic_local across the held-out stress families.", + "conclusion_code": "abstain_without_policy_claim", "evidence_ids": [ "adaptive_minus_best_fixed_final_nll", "E6-repeated-membership-loss", @@ -152,9 +152,83 @@ "369bc4e9b32d6e1fcdd8dadc98c830e5ac5179f4a7204a9f5194e22913fdefdf", "d6321d6fc4c0f71c4f14c2f799eff252348073b3fe5508783f9f078e7f5e9d76" ], - "confidence": "abstain" + "expected_state_version": 1 } } ] + }, + { + "name": "Flagship evidence-court journey", + "messages": [ + { + "role": "user", + "type": "message", + "content": "Audit whether the observable adaptive controller deserves a transferable win over periodic_local. Discover the current IDs, compare every held-out family, inspect the decisive E6 transition, open the failed learning gate, and stage only the artifact's warranted conclusion. Do not approve anything." + } + ], + "expectedCall": [ + { + "functionName": "get_observatory_state", + "arguments": {} + }, + { + "functionName": "compare_stress_families", + "arguments": {} + }, + { + "functionName": "inspect_stress_family", + "arguments": { + "family_id": "E6-repeated-membership-loss", + "include_regions": true + } + }, + { + "functionName": "inspect_run", + "arguments": { + "run_id": "e001-sc1:evaluation:E6-repeated-membership-loss:observable_adaptive", + "epoch_offset": 158, + "epoch_limit": 6 + } + }, + { + "functionName": "open_evidence", + "arguments": { + "evidence_id": "adaptive_minus_best_fixed_final_nll", + "semantic_depth": "researcher" + } + }, + { + "functionName": "stage_conclusion", + "arguments": { + "conclusion_code": "abstain_without_policy_claim", + "evidence_ids": [ + "E6-repeated-membership-loss", + "e001-sc1:evaluation:E6-repeated-membership-loss:observable_adaptive", + "adaptive_minus_best_fixed_final_nll" + ], + "expected_state_version": 1 + } + } + ] + }, + { + "name": "Resolve an ambiguous win question from evidence", + "messages": [ + { + "role": "user", + "type": "message", + "content": "So, does adaptive actually win here? Check the published experiment before answering; don't infer from the dashboard headline." + } + ], + "expectedCall": [ + { + "functionName": "get_observatory_state", + "arguments": {} + }, + { + "functionName": "compare_stress_families", + "arguments": {} + } + ] } ] diff --git a/tests/test_webmcp_contract.py b/tests/test_webmcp_contract.py index d513d07..7dc44ac 100644 --- a/tests/test_webmcp_contract.py +++ b/tests/test_webmcp_contract.py @@ -46,12 +46,11 @@ "policy_ids": ["observable_adaptive", "periodic_local"], }, "stage_conclusion": { - "claim": "The transferable-win claim is not supported across every held-out family; abstain pending stronger evidence.", + "conclusion_code": "abstain_without_policy_claim", "evidence_ids": [ "369bc4e9b32d6e1fcdd8dadc98c830e5ac5179f4a7204a9f5194e22913fdefdf", "d6321d6fc4c0f71c4f14c2f799eff252348073b3fe5508783f9f078e7f5e9d76", ], - "confidence": "abstain", "expected_state_version": 4, }, } @@ -73,9 +72,9 @@ "open_evidence": {"evidence_id": "contains a space"}, "compare_policies": {"policy_ids": ["same", "same"]}, "stage_conclusion": { - "claim": "Unsupported certainty", - "evidence_ids": [], - "confidence": "certain", + "conclusion_code": "transferable_winner", + "evidence_ids": ["E6-repeated-membership-loss"], + "expected_state_version": 4, }, } @@ -261,9 +260,9 @@ def test_exact_tools_schemas_and_annotations() -> None: ) assert tools["get_observatory_state"]["inputSchema"]["properties"] == {} assert tools["stage_conclusion"]["inputSchema"]["required"] == [ - "claim", + "conclusion_code", "evidence_ids", - "confidence", + "expected_state_version", ] @@ -284,11 +283,11 @@ def test_schema_limits_match_the_grounded_artifact_contract() -> None: assert schemas["compare_policies"]["properties"]["policy_ids"]["maxItems"] == 3 assert schemas["compare_policies"]["properties"]["metric_ids"]["maxItems"] == 6 assert schemas["stage_conclusion"]["properties"]["evidence_ids"]["maxItems"] == 8 - assert schemas["stage_conclusion"]["properties"]["confidence"]["enum"] == [ - "supported", - "qualified", - "abstain", + assert schemas["stage_conclusion"]["properties"]["conclusion_code"]["enum"] == [ + "abstain_without_policy_claim", ] + assert "claim" not in schemas["stage_conclusion"]["properties"] + assert "confidence" not in schemas["stage_conclusion"]["properties"] def test_valid_calls_are_normalized_forwarded_and_compact() -> None: @@ -371,15 +370,16 @@ def test_adapter_documents_late_bound_bridge_and_human_only_approval() -> None: source = ADAPTER.read_text(encoding="utf-8") assert "window.GPUStackMission.invoke(toolName, validatedArgs, { signal })" in source assert "approval remains an explicit page-only human act" in source - assert "This never approves or commits it" in source + assert "Only the human can approve, edit, or reject it" in source + assert "Free-form agent claims are rejected" in source def test_observatory_load_order_and_cache_keys_include_the_bridge_release() -> None: html = OBSERVATORY_HTML.read_text(encoding="utf-8") scripts = [ - 'observatory.js?v=20260903.1', - 'webmcp-tools.js?v=20260903.1', - 'webmcp-mission.js?v=20260903.1', + 'observatory.js?v=20260903.2', + 'webmcp-tools.js?v=20260903.2', + 'webmcp-mission.js?v=20260903.2', ] assert all(script in html for script in scripts) diff --git a/tests/test_webmcp_mission_runtime.mjs b/tests/test_webmcp_mission_runtime.mjs index f460559..e0557de 100644 --- a/tests/test_webmcp_mission_runtime.mjs +++ b/tests/test_webmcp_mission_runtime.mjs @@ -40,7 +40,12 @@ async function makeRuntime() { version: "test", async whenReady() {}, getState() { - return { experiment: "E001-SC1", depth: "freshman", semanticFamily: "", semanticRun: "" }; + return { + experiment: "E001-SC1", + depth: "freshman", + semanticFamily: "E1-bursty-wan", + semanticRun: "e001-sc1:evaluation:E1-bursty-wan:observable_adaptive", + }; }, async selectView(patch) { selections.push(patch); @@ -118,6 +123,12 @@ test("all eight WebMCP registrations execute against immutable evidence", async assert.equal(state.artifact.epochs, 12981); assert.equal(state.frozen_result.abstentions, 104); assert.equal(state.frozen_result.all_falsifiers_pass, false); + assert.deepEqual(Array.from(state.registered_ids.policies), ["observable_adaptive", "periodic_local"]); + assert.equal(state.registered_ids.families.length, 6); + assert.equal(state.registered_ids.causal_nodes.length, 7); + assert.equal(state.registered_ids.failed_gates.length, 4); + assert.equal(state.evidence_boundary.unresolved, "frontier/facility transfer"); + assert.ok(JSON.stringify(state).length <= 1500, `state result exceeded budget: ${JSON.stringify(state).length}`); assert.equal(state.truncated, undefined); const families = await execute("compare_stress_families", {}); @@ -136,11 +147,13 @@ test("all eight WebMCP registrations execute against immutable evidence", async assert.equal(family.truncated, undefined); const runId = "e001-sc1:evaluation:E6-repeated-membership-loss:observable_adaptive"; - const run = await execute("inspect_run", { run_id: runId, epoch_offset: 0, epoch_limit: 8 }); + const run = await execute("inspect_run", { run_id: runId, epoch_offset: 158, epoch_limit: 6 }); assert.equal(run.ok, true); assert.equal(run.run.final_held_out_nll, 1.063824194483459); + assert.equal(run.run.controller_abstentions, 24); + assert.equal(run.run.support_envelope_flag_count, 24); assert.equal(run.epoch_page.rows.length, 6); - assert.equal(run.epoch_page.context_limit_applied, true); + assert.equal(run.epoch_page.context_limit_applied, false); assert.equal(run.source_raw_sha256, "d6321d6fc4c0f71c4f14c2f799eff252348073b3fe5508783f9f078e7f5e9d76"); assert.equal(run.truncated, undefined); @@ -176,19 +189,22 @@ test("all eight WebMCP registrations execute against immutable evidence", async assert.equal(policies.ok, true); assert.equal(policies.policies.length, 2); assert.equal(policies.comparator_frozen_before_evaluation, true); + assert.equal(policies.policies[0].metrics.controller_abstentions, 104); + assert.equal(policies.policies[1].metrics.controller_abstentions, null); + assert.equal(policies.policies[0].metrics.support_envelope_flag_count, 104); + assert.equal(policies.policies[1].metrics.support_envelope_flag_count, 104); assert.equal(policies.truncated, undefined); const rejectedOverclaim = await execute("stage_conclusion", { - claim: "The adaptive policy is a transferable winner.", + conclusion_code: "transferable_winner", evidence_ids: ["E6-repeated-membership-loss"], - confidence: "supported", expected_state_version: state.state_version, }); assert.equal(rejectedOverclaim.ok, false); - assert.equal(rejectedOverclaim.code, "EVIDENCE_CONFLICT"); + assert.equal(rejectedOverclaim.code, "INVALID_ARGUMENT"); const staged = await execute("stage_conclusion", { - claim: "The artifact supports abstaining from any transferable winner claim.", + conclusion_code: "abstain_without_policy_claim", evidence_ids: [ "adaptive_minus_best_fixed_final_nll", "E6-repeated-membership-loss", @@ -197,11 +213,11 @@ test("all eight WebMCP registrations execute against immutable evidence", async "369bc4e9b32d6e1fcdd8dadc98c830e5ac5179f4a7204a9f5194e22913fdefdf", "d6321d6fc4c0f71c4f14c2f799eff252348073b3fe5508783f9f078e7f5e9d76", ], - confidence: "abstain", expected_state_version: state.state_version, }); assert.equal(staged.ok, true); assert.equal(staged.status, "pending_human_review"); + assert.equal(staged.conclusion_code, "abstain_without_policy_claim"); assert.equal(staged.truncated, undefined); assert.equal(runtime.window.GPUStackMission.getState().pending.proposalId, staged.proposal_id); assert.ok(runtime.selections.length >= 5); @@ -222,17 +238,23 @@ test("adapter rejects invalid and stale calls without mutating approval state", assert.equal(unknown.code, "UNKNOWN_FAMILY"); const staged = await execute("stage_conclusion", { - claim: "Abstain from a transferable claim.", + conclusion_code: "abstain_without_policy_claim", evidence_ids: ["adaptive_minus_best_fixed_final_nll"], - confidence: "abstain", expected_state_version: 1, }); assert.equal(staged.ok, true); + const occupied = await execute("stage_conclusion", { + conclusion_code: "abstain_without_policy_claim", + evidence_ids: ["adaptive_minus_best_fixed_final_nll"], + expected_state_version: 2, + }); + assert.equal(occupied.ok, false); + assert.equal(occupied.code, "PENDING_REVIEW_EXISTS"); + const stale = await execute("stage_conclusion", { - claim: "A second proposal based on stale state.", + conclusion_code: "abstain_without_policy_claim", evidence_ids: ["adaptive_minus_best_fixed_final_nll"], - confidence: "abstain", expected_state_version: 1, }); assert.equal(stale.ok, false);