Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHALLENGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Registration occurs in the top-level document. If `document.modelContext` is una
| `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 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` 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.
The first seven tools are annotated read-only. `stage_conclusion` requires the current state version, a failed frozen gate, and an adaptive family or run with controller abstentions; it performs a final compare-and-swap and refuses to overwrite an existing pending review. It 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. A human edit is recorded explicitly as an override, and the approved claim, evidence, artifact code, timestamp, and override status remain visible until undone. The agent can explore broadly and prepare a coherent evidence bundle, but it must stop at the judgment boundary.

## Testing

Expand Down
8 changes: 4 additions & 4 deletions docs/observatory.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<link rel="stylesheet" href="styles/60-taskbar.css">
<link rel="stylesheet" href="styles/70-pixel-icons.css">
<link rel="stylesheet" href="styles/95-observatory-cuperos.css?v=20260717.2">
<link rel="stylesheet" href="styles/99-webmcp-mission.css?v=20260903.3">
<script src="observatory.js?v=20260903.3" defer></script>
<script src="webmcp-tools.js?v=20260903.3" defer></script>
<script src="webmcp-mission.js?v=20260903.3" defer></script>
<link rel="stylesheet" href="styles/99-webmcp-mission.css?v=20260903.5">
<script src="observatory.js?v=20260903.5" defer></script>
<script src="webmcp-tools.js?v=20260903.5" defer></script>
<script src="webmcp-mission.js?v=20260903.5" defer></script>
</head>
<body data-depth="freshman" data-data-state="loading" data-mission-mode="active">
<a class="skip-link" href="#observatory-main">Skip to experiment</a>
Expand Down
126 changes: 107 additions & 19 deletions docs/webmcp-mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
let screeningPromise = null;
let projectionPromise = null;
let tourRunning = false;
let tourGeneration = 0;
let editingProposal = false;
let missionState = loadMissionState();

Expand Down Expand Up @@ -95,16 +96,36 @@
if (signal && signal.aborted) throw signal.reason || new DOMException("Aborted", "AbortError");
}

async function fetchArtifact(url, signal) {
function waitForShared(promise, signal) {
abortIfNeeded(signal);
const response = await fetch(url, { cache: "no-store", headers: { Accept: "application/json" }, signal });
if (!signal || typeof signal.addEventListener !== "function") return promise;
return new Promise((resolve, reject) => {
const onAbort = () => {
signal.removeEventListener?.("abort", onAbort);
reject(signal.reason || new DOMException("Aborted", "AbortError"));
};
signal.addEventListener("abort", onAbort, { once: true });
promise.then(
(value) => {
signal.removeEventListener?.("abort", onAbort);
resolve(value);
},
(error) => {
signal.removeEventListener?.("abort", onAbort);
reject(error);
},
);
});
}

async function fetchArtifact(url) {
const response = await fetch(url, { cache: "no-store", headers: { Accept: "application/json" } });
if (!response.ok) throw new MissionError("ARTIFACT_UNAVAILABLE", `${url} returned HTTP ${response.status}.`);
return response.json();
}

async function fetchGzipArtifact(url, signal) {
abortIfNeeded(signal);
const response = await fetch(url, { cache: "no-store", headers: { Accept: "application/gzip" }, signal });
async function fetchGzipArtifact(url) {
const response = await fetch(url, { cache: "no-store", headers: { Accept: "application/gzip" } });
if (!response.ok) throw new MissionError("ARTIFACT_UNAVAILABLE", `${url} returned HTTP ${response.status}.`);
if (typeof DecompressionStream !== "function") {
throw new MissionError("DECOMPRESSION_UNAVAILABLE", "This browser cannot open the bounded gzip projection.");
Expand All @@ -119,7 +140,7 @@
}

function semanticArtifact(signal) {
semanticPromise ||= fetchArtifact(SEMANTIC_URL, signal).then((value) => {
semanticPromise ||= fetchArtifact(SEMANTIC_URL).then((value) => {
if (value?.schema !== "gpu-stack.causal-observatory.e001-semantic-consistency.v1") {
throw new MissionError("ARTIFACT_INVALID", "The E001-SC1 compact artifact has an unsupported schema.");
}
Expand All @@ -128,11 +149,11 @@
semanticPromise = null;
throw error;
});
return semanticPromise;
return waitForShared(semanticPromise, signal);
}

function screeningArtifact(signal) {
screeningPromise ||= fetchArtifact(SCREENING_URL, signal).then((value) => {
screeningPromise ||= fetchArtifact(SCREENING_URL).then((value) => {
if (value?.schema !== "gpu-stack.causal-observatory.e001.v1") {
throw new MissionError("ARTIFACT_INVALID", "The E001 screening artifact has an unsupported schema.");
}
Expand All @@ -141,11 +162,11 @@
screeningPromise = null;
throw error;
});
return screeningPromise;
return waitForShared(screeningPromise, signal);
}

function runProjection(signal) {
projectionPromise ||= fetchGzipArtifact(PROJECTION_URL, signal).then((value) => {
projectionPromise ||= fetchGzipArtifact(PROJECTION_URL).then((value) => {
if (value?.schema !== "gpustack.webmcp-run-projection.v1" || !Array.isArray(value.epoch_columns)) {
throw new MissionError("PROJECTION_INVALID", "The bounded epoch projection has an unsupported schema.");
}
Expand All @@ -154,7 +175,7 @@
projectionPromise = null;
throw error;
});
return projectionPromise;
return waitForShared(projectionPromise, signal);
}

async function observatory() {
Expand Down Expand Up @@ -268,6 +289,7 @@
const effects = semantic.researcher.paired_effects || [];
const families = familyList(semantic);
const view = bridge.getState();
const latestApproved = missionState.approved[missionState.approved.length - 1] || null;
const receipt = addReceipt("get_observatory_state", "complete", "Read the immutable audit state and registered evidence IDs.", {
evidenceIds: [semantic.artifact_sha256],
origin: context.origin,
Expand Down Expand Up @@ -304,7 +326,11 @@
causal_nodes: screening.causal_graph.nodes.map((node) => node.node_id),
failed_gates: effects.filter((effect) => effect.passed === false).map((effect) => effect.effect_id),
},
pending_proposal: missionState.pending ? missionState.pending.proposalId : null,
review: {
pending: missionState.pending ? missionState.pending.proposalId : null,
latest_approved: latestApproved ? latestApproved.proposalId : null,
human_override: latestApproved ? Boolean(latestApproved.humanOverride) : false,
},
suggested_next: "compare_stress_families",
}, receipt);
}
Expand Down Expand Up @@ -416,6 +442,10 @@
throw new MissionError("HASH_MISMATCH", "The bounded projection is not bound to this compact artifact's raw trace.");
}
const page = projectedEpochRows(projection, run.run_id, args.epoch_offset, args.epoch_limit);
if (run.policy_id !== "observable_adaptive") {
const flagColumn = page.columns.indexOf("abstained");
if (flagColumn >= 0) page.columns[flagColumn] = "support_envelope_flag";
}
const bridge = await observatory();
await bridge.selectView({
experiment: "E001-SC1",
Expand Down Expand Up @@ -682,7 +712,10 @@
: median(values);
}
});
return { policy_id: policyId, evaluation_runs: runs.length, metrics };
const role = policyId === "observable_adaptive"
? "candidate"
: (policyId === semantic.comparison.selected_fixed_policy_id ? "calibration_frozen_comparator" : "registered_reference");
return { policy_id: policyId, role, evaluation_runs: runs.length, metrics };
});
const bridge = await observatory();
await bridge.selectView({ experiment: "E001-SC1", depth: "researcher" });
Expand All @@ -695,7 +728,10 @@
return resultWithReceipt({
ok: true,
evaluation_split_only: true,
comparator_frozen_before_evaluation: semantic.comparison.selection.frozen_before_evaluation,
comparator_contract: {
policy_id: semantic.comparison.selected_fixed_policy_id,
frozen_before_evaluation: semantic.comparison.selection.frozen_before_evaluation,
},
policies: rows,
frozen_gates: semantic.researcher.paired_effects.map((effect) => ({
effect_id: effect.effect_id,
Expand Down Expand Up @@ -726,11 +762,33 @@
for (const evidenceId of args.evidence_ids) {
resolved.push(await resolveEvidence(evidenceId, "researcher", false, context.signal));
}
const hasFailedGate = resolved.some((entry) => entry.kind === "frozen_gate" && entry.passed === false);
const hasAdaptiveAbstention = resolved.some((entry) => (
(entry.kind === "held_out_family" && Number(entry.abstentions) > 0)
|| (entry.kind === "run" && entry.policy_id === "observable_adaptive" && Number(entry.controller_abstentions) > 0)
));
if (!hasFailedGate || !hasAdaptiveAbstention) {
throw new MissionError("EVIDENCE_INSUFFICIENT", "The typed abstain conclusion requires at least one failed frozen gate and one adaptive family or run with controller abstentions.", {
required_evidence: ["failed_frozen_gate", "adaptive_abstention_family_or_run"],
});
}
abortIfNeeded(context.signal);
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 (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,
});
}
const proposal = {
proposalId: `proposal-${String(missionState.nextProposal).padStart(3, "0")}`,
claim: CANONICAL_CONCLUSIONS[args.conclusion_code],
originalClaim: CANONICAL_CONCLUSIONS[args.conclusion_code],
conclusionCode: args.conclusion_code,
humanOverride: false,
evidenceIds: [...args.evidence_ids],
evidenceKinds: resolved.map((entry) => entry.kind),
confidence: "abstain",
Expand Down Expand Up @@ -807,9 +865,29 @@
function renderPending() {
if (!dom.pending) return;
const proposal = missionState.pending;
const latestApproved = missionState.approved[missionState.approved.length - 1] || null;
dom.pending.replaceChildren();
if (!proposal) {
dom.pending.innerHTML = EMPTY_PENDING_HTML;
if (!latestApproved) {
dom.pending.innerHTML = EMPTY_PENDING_HTML;
} else {
const card = document.createElement("article");
card.className = "mission-change";
card.dataset.kind = "approved-conclusion";
card.dataset.changeId = latestApproved.proposalId;
const title = document.createElement("h3");
title.textContent = `${latestApproved.proposalId} · human recorded${latestApproved.humanOverride ? " · override" : ""}`;
const claim = document.createElement("p");
claim.textContent = latestApproved.claim;
const evidence = document.createElement("p");
const evidenceLabel = document.createElement("strong");
evidenceLabel.textContent = "Evidence: ";
evidence.append(evidenceLabel, document.createTextNode(latestApproved.evidenceIds.join(" · ")));
const boundary = document.createElement("p");
boundary.textContent = `Artifact code: ${latestApproved.conclusionCode}. Human override: ${latestApproved.humanOverride ? "yes" : "no"}. Recorded ${new Date(latestApproved.approvedAt).toLocaleString()}.`;
card.append(title, claim, evidence, boundary);
dom.pending.append(card);
}
} else {
const card = document.createElement("article");
card.className = "mission-change";
Expand All @@ -833,7 +911,7 @@
evidenceLabel.textContent = "Evidence: ";
evidence.append(evidenceLabel, document.createTextNode(proposal.evidenceIds.join(" · ")));
const boundary = document.createElement("p");
boundary.textContent = `Frozen result: ${proposal.frozenConclusion}. Approval has not occurred.`;
boundary.textContent = `Frozen result: ${proposal.frozenConclusion}. Human override: ${proposal.humanOverride ? "yes" : "no"}. Approval has not occurred.`;
card.append(title, claim, evidence, boundary);
dom.pending.append(card);
}
Expand Down Expand Up @@ -895,7 +973,7 @@
missionState.stateVersion += 1;
editingProposal = false;
persist();
addReceipt("human_approve", "approved", `Human recorded ${approved.proposalId}.`, {
addReceipt("human_approve", "approved", `Human recorded ${approved.proposalId}: ${approved.claim}`, {
evidenceIds: approved.evidenceIds,
delta: `Recorded conclusion → ${approved.proposalId}`,
origin: "human",
Expand Down Expand Up @@ -934,6 +1012,7 @@
return;
}
missionState.pending.claim = claim.slice(0, 600);
missionState.pending.humanOverride = missionState.pending.claim !== missionState.pending.originalClaim;
missionState.stateVersion += 1;
editingProposal = false;
persist();
Expand All @@ -958,9 +1037,12 @@
}

async function resetMission() {
tourGeneration += 1;
const nextStateVersion = missionState.stateVersion + 1;
missionState = initialMissionState();
missionState.stateVersion = nextStateVersion;
editingProposal = false;
try { window.sessionStorage.removeItem(STORAGE_KEY); } catch (_error) { /* no-op */ }
persist();
renderMission();
clearHighlights();
const bridge = await observatory();
Expand All @@ -974,19 +1056,25 @@

async function runTour() {
if (tourRunning) return;
const generation = ++tourGeneration;
tourRunning = true;
dom.tour.disabled = true;
try {
await invoke("get_observatory_state", {}, { origin: "local_tour" });
await pause(220);
if (generation !== tourGeneration) return;
await invoke("compare_stress_families", {}, { origin: "local_tour" });
await pause(220);
if (generation !== tourGeneration) return;
await invoke("inspect_stress_family", { family_id: "E6-repeated-membership-loss", include_regions: false }, { origin: "local_tour" });
await pause(220);
if (generation !== tourGeneration) return;
await invoke("trace_causal_path", { from_node: "site_availability", to_node: "time_to_target", max_nodes: 7 }, { origin: "local_tour" });
await pause(220);
if (generation !== tourGeneration) return;
await invoke("open_evidence", { evidence_id: "adaptive_minus_best_fixed_final_nll", semantic_depth: "researcher" }, { origin: "local_tour" });
await pause(220);
if (generation !== tourGeneration) return;
await invoke("stage_conclusion", {
conclusion_code: "abstain_without_policy_claim",
evidence_ids: [
Expand Down Expand Up @@ -1019,7 +1107,7 @@
}

function refreshRegistrationStatus(forcedState) {
if (!forcedState && missionState.pending) {
if (missionState.pending) {
setStatus("Audit staged · waiting for human approval", "waiting");
return;
}
Expand Down
2 changes: 1 addition & 1 deletion evals/webmcp-evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
{
"role": "user",
"type": "message",
"content": "Inspect the first eight projected epochs of the observable_adaptive run in the E6 held-out family. Keep the result bounded and preserve its link to the authoritative trace."
"content": "Inspect a six-row transition slice starting at epoch 158 of the observable_adaptive run in the E6 held-out family. Keep the result bounded and preserve its link to the authoritative trace."
}
],
"expectedCall": [
Expand Down
6 changes: 3 additions & 3 deletions tests/test_webmcp_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@ def test_adapter_documents_late_bound_bridge_and_human_only_approval() -> None:
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.3',
'webmcp-tools.js?v=20260903.3',
'webmcp-mission.js?v=20260903.3',
'observatory.js?v=20260903.5',
'webmcp-tools.js?v=20260903.5',
'webmcp-mission.js?v=20260903.5',
]

assert all(script in html for script in scripts)
Expand Down
Loading
Loading