diff --git a/app/index.html b/app/index.html
index ae64cc2..d92e165 100644
--- a/app/index.html
+++ b/app/index.html
@@ -199,13 +199,13 @@
-
Stage 05Atomistic simulation
-
diff --git a/app/js/app.js b/app/js/app.js
index ab56752..ed40d3c 100644
--- a/app/js/app.js
+++ b/app/js/app.js
@@ -92,7 +92,9 @@
support: { label: "Atomistic support", unit: "/100", domain: [0, 100], basis: "No values available: target module is not wired." },
occupancy: { label: "Pose occupancy", unit: "%", domain: [0, 100], basis: "No values available: target module is not wired." },
convergence: { label: "Convergence", unit: "%", domain: [0, 100], basis: "No values available: target module is not wired." },
- tractability_fit: { label: "Branch tractability fit", unit: "/100 representative", domain: [0, 100], basis: "Representative branch-context fit; not an atomistic metric or native module output." }
+ tractability_fit: { label: "Representative branch fit", unit: "/100 representative", domain: [0, 100], basis: "Representative branch-context fit for demo comparison; not a native tractability output." },
+ precedent: { label: "Retrieved precedent", unit: "native categorical", categorical: true, basis: "Native retrieved-precedent verdict and observations; no scalar score is inferred." },
+ computed: { label: "Computed pocket evidence", unit: "native categorical", categorical: true, basis: "Native computed tractability observations; reported independently from retrieved precedent and never converted to a score." }
}
};
@@ -254,6 +256,31 @@
.replace(/\bsimulated\b/gi, "modeled");
}
+ // Reason codes remain available in the inspector's audit sections. Node cards
+ // need a concise outcome, not an internal enum that reads like scientific copy.
+ function publicReasonSummary(node) {
+ var reason = node && typeof node.reason === "string" ? node.reason.trim() : "";
+ if (!reason) return null;
+ var code = reason.split(" · ")[0];
+ var known = {
+ PINNED_ARTIFACT_REVALIDATED: "Recorded producer artifact revalidated; scientific result unchanged.",
+ MODULE_CONFIGURED_CACHED: "Cached producer artifact used for this run.",
+ RUNTIME_UNAVAILABLE: "Live runtime unavailable; labeled fallback shown.",
+ MODULE_NOT_WIRED: "No producer artifact was created for this stage.",
+ NOT_AMENABLE: "No tractability result; the stated mechanism is not amenable.",
+ MOCK_ECONOMICS_FAILURE: "Economics record unavailable; value remains missing.",
+ MOCK_FORECAST_FAILURE: "Recruitability record unavailable; sibling branches continued."
+ };
+ if (known[code]) return known[code];
+ if (/^[A-Z][A-Z0-9_]*$/.test(code)) {
+ if (node.outputOrigin === "CACHED") return "Cached producer artifact shown; see audit details.";
+ if (node.outputOrigin === "DEMO_FALLBACK") return "Labeled fallback artifact shown; see audit details.";
+ if (node.execution === "COMPLETE") return "Producer completed; see audit details.";
+ return "Terminal producer outcome; see audit details.";
+ }
+ return judgeFacingText(reason);
+ }
+
function announce(message) {
elements.live.textContent = "";
window.requestAnimationFrame(function () {
@@ -687,7 +714,7 @@
function stageTerminal(index) {
var stageState = state.stageStates[index];
- return stageState === "complete" || stageState === "warning" || stageState === "failed";
+ return stageState === "complete" || stageState === "replay" || stageState === "warning" || stageState === "failed";
}
function retireUnusedCapacity() {
@@ -838,8 +865,14 @@
node.resultBasis = httpMode ? (hasSimulationPayload ? "BACKEND-REPORTED" : "MISSING") : (program.notAmenable ? "NO RESULT" : "NOT WIRED");
node.runtime = httpMode ? "BACKEND SNAPSHOT" : "NOT WIRED";
node.outputOrigin = httpMode ? (hasSimulationPayload ? "UNREPORTED" : "NOT_RUN") : "NOT_RUN";
- node.metrics = program.displayMetricBasis === "REPRESENTATIVE_DEMO_SCENARIO_V1"
- ? { tractability_fit: program.metrics.tractability_fit }
+ node.metrics = httpMode
+ ? {
+ tractability_fit: program.displayMetricBasis === "REPRESENTATIVE_DEMO_SCENARIO_V1"
+ ? program.metrics.tractability_fit
+ : null,
+ precedent: null,
+ computed: null
+ }
: { support: null, occupancy: null, convergence: null };
node.uncertainty = httpMode
? (program.tractabilityUncertainty || "No scalar atomistic metric is imputed; inspect the native tractability interpretation.")
@@ -888,8 +921,93 @@
return METRICS[node.stage][metricKey];
}
+ function simulationMetricView(payload, key) {
+ if (key !== "precedent" && key !== "computed") return null;
+ var artifact = payload && typeof payload === "object"
+ ? (payload.output && typeof payload.output === "object" ? payload.output : payload)
+ : {};
+ var verdict = typeof artifact.verdict === "string" ? artifact.verdict : null;
+ var verdictBasis = typeof artifact.verdict_basis === "string" ? artifact.verdict_basis : null;
+ var verdictLabel = verdict ? verdict.replace(/_/g, " ") : null;
+
+ if (key === "precedent") {
+ var precedent = artifact.target_precedent && typeof artifact.target_precedent === "object"
+ ? artifact.target_precedent
+ : {};
+ var precedentFacts = [];
+ if (typeof precedent.best_potency_nm === "number" && Number.isFinite(precedent.best_potency_nm)) {
+ precedentFacts.push("Best measured potency " + precedent.best_potency_nm + " nM");
+ }
+ if (typeof precedent.approved_small_molecules_count === "number" && Number.isFinite(precedent.approved_small_molecules_count)) {
+ precedentFacts.push(precedent.approved_small_molecules_count + " approved small molecule" + (precedent.approved_small_molecules_count === 1 ? "" : "s"));
+ }
+ if (Array.isArray(precedent.clinical_stage_small_molecules)) {
+ precedentFacts.push(precedent.clinical_stage_small_molecules.length + " clinical-stage small molecule" + (precedent.clinical_stage_small_molecules.length === 1 ? "" : "s"));
+ }
+ var precedentBearing = verdictBasis === "retrieved_precedent";
+ var supports = Boolean(verdict && /tractable/i.test(verdict) && !/(?:not|non)[_ -]?tractable|untractable/i.test(verdict));
+ var opposes = Boolean(verdict && /(?:not|non)[_ -]?tractable|untractable/i.test(verdict));
+ var precedentReported = precedentBearing || precedentFacts.length > 0;
+ return {
+ kind: "categorical",
+ display: precedentReported
+ ? (supports ? "Supports tractability · retrieved" : (opposes ? "Does not support · retrieved" : "Retrieved precedent · reported"))
+ : "Retrieved precedent · not reported",
+ detail: precedentFacts.length
+ ? precedentFacts.join(" · ") + "."
+ : (precedentBearing && verdictLabel ? "Native verdict: " + verdictLabel + "." : "The native artifact did not report retrieved-precedent observations."),
+ placement: precedentReported ? (supports ? "supported" : (opposes ? "not-supported" : "reported")) : "missing",
+ sourcePaths: ["verdict_basis", "verdict", "target_precedent"],
+ scalar: null
+ };
+ }
+
+ var computed = artifact.tractability && typeof artifact.tractability === "object"
+ ? artifact.tractability
+ : {};
+ var rank = computed.site_pocket_rank && typeof computed.site_pocket_rank === "object"
+ ? computed.site_pocket_rank
+ : {};
+ var volume = computed.pocket_volume_a3 && typeof computed.pocket_volume_a3 === "object"
+ ? computed.pocket_volume_a3
+ : {};
+ var computedFacts = [];
+ if (typeof rank.fpocket === "number" && Number.isFinite(rank.fpocket)) {
+ computedFacts.push("fPocket rank " + rank.fpocket + (typeof rank.n_pockets === "number" ? " of " + rank.n_pockets : ""));
+ }
+ if (typeof rank.prank === "number" && Number.isFinite(rank.prank)) computedFacts.push("PRANK rank " + rank.prank);
+ if (typeof volume.primary_d1_6_a3 === "number" && Number.isFinite(volume.primary_d1_6_a3)) {
+ computedFacts.push("Site volume " + volume.primary_d1_6_a3 + " ų");
+ }
+ if (typeof computed.cryptic_pocket_risk === "string" && computed.cryptic_pocket_risk) {
+ computedFacts.push("Cryptic-pocket risk " + computed.cryptic_pocket_risk.replace(/_/g, " "));
+ }
+ var computedReported = Object.keys(computed).length > 0 || computedFacts.length > 0;
+ var axisConflict = artifact.axis_conflict !== null && artifact.axis_conflict !== undefined && artifact.axis_conflict !== false;
+ var computedCarriesVerdict = verdictBasis === "computed_tractability";
+ return {
+ kind: "categorical",
+ display: computedReported
+ ? (axisConflict ? "Computed evidence · axis conflict" : (computedCarriesVerdict ? "Computed evidence · informs verdict" : "Computed evidence · context only"))
+ : "Computed evidence · not reported",
+ detail: computedFacts.length
+ ? computedFacts.join(" · ") + ". " + (computedCarriesVerdict ? "This axis informs the native verdict." : "This axis does not carry the native verdict.")
+ : "The native artifact did not report computed pocket observations.",
+ placement: computedReported ? (axisConflict ? "conflict" : (computedCarriesVerdict ? "verdict-bearing" : "reported")) : "missing",
+ sourcePaths: ["tractability", "axis_conflict", "verdict_basis"],
+ scalar: null
+ };
+ }
+
+ function activeSimulationView(node) {
+ if (!node || node.stage !== "simulation") return null;
+ return simulationMetricView(node.metadata && node.metadata.stationPayload, state.metrics.simulation);
+ }
+
function metricValue(node) {
if (!METRICS[node.stage]) return null;
+ var categoricalView = activeSimulationView(node);
+ if (categoricalView) return categoricalView;
return Object.prototype.hasOwnProperty.call(node.metrics, state.metrics[node.stage])
? node.metrics[state.metrics[node.stage]]
: null;
@@ -900,6 +1018,7 @@
var definition = metricDefinition(node);
if (!definition) return "Fixed root";
var value = metricValue(node);
+ if (value && typeof value === "object" && value.kind === "categorical") return value.display;
if (value === null || typeof value !== "number" || Number.isNaN(value)) {
return node.kind === "pending" ? "Pending · shelf" : "Missing · shelf";
}
@@ -919,6 +1038,19 @@
if (node.kind === "scaffold" || node.kind === "pending") return bandTop + GRAPH_GEOMETRY.shelfOffset;
var definition = metricDefinition(node);
var value = metricValue(node);
+ if (value && typeof value === "object" && value.kind === "categorical") {
+ if (value.placement === "missing") return bandTop + GRAPH_GEOMETRY.shelfOffset;
+ var categoricalPlacement = {
+ "not-supported": 0.18,
+ conflict: 0.42,
+ reported: 0.62,
+ "verdict-bearing": 0.82,
+ supported: 0.88
+ };
+ var displayPosition = categoricalPlacement[value.placement];
+ if (typeof displayPosition !== "number") return bandTop + GRAPH_GEOMETRY.shelfOffset;
+ return bandTop + GRAPH_GEOMETRY.plotOffset + displayPosition * GRAPH_GEOMETRY.plotTravel;
+ }
if (!definition || value === null || typeof value !== "number" || Number.isNaN(value)) return bandTop + GRAPH_GEOMETRY.shelfOffset;
var normalized = (value - definition.domain[0]) / (definition.domain[1] - definition.domain[0]);
normalized = Math.max(0, Math.min(1, normalized));
@@ -964,6 +1096,23 @@
return preview;
}
+ function nodeCardDetail(node) {
+ if (node.kind === "scaffold") {
+ return node.metadata.retired
+ ? "Unused capacity · no record created."
+ : "Requested capacity · awaiting an identity.";
+ }
+ if (node.kind === "pending") return "Identity bound · stage output pending.";
+ var reasonSummary = publicReasonSummary(node);
+ if (reasonSummary) return reasonSummary;
+ if (BOOT.mode === "http" && node.metadata.stationPayload) {
+ return node.stage === "simulation"
+ ? "Native tractability dossier · choose an evidence view."
+ : "Native producer artifact · interpretation and provenance attached.";
+ }
+ return node.metadata.summary || node.uncertainty || "No additional public summary supplied.";
+ }
+
function renderGraph() {
if (!state.snapshot) return;
var selection = state.selectedId ? lineageOf(state.selectedId) : new Set();
@@ -1008,11 +1157,7 @@
? '
' + escapeHTML(node.execution) + '' + escapeHTML(node.resultBasis) + '' + escapeHTML(node.outputOrigin || "UNREPORTED") + '' + escapeHTML(node.runtime) + "
"
: '
' + escapeHTML(node.execution) + '' + escapeHTML(node.resultBasis) + '' + escapeHTML(node.runtime) + "
";
}
- var nodeDetail = node.kind === "scaffold"
- ? (node.metadata.retired ? "Unused requested capacity · no scientific record created." : "Requested capacity · identity has not been created.")
- : node.kind === "pending"
- ? "Candidate identity exists · selected stage output is pending."
- : (node.reason || node.metadata.summary || node.uncertainty || "No additional public summary supplied.");
+ var nodeDetail = nodeCardDetail(node);
nodeElement.innerHTML = '
' + escapeHTML(node.kind === "scaffold" ? "requested capacity" : node.stage) + '' + escapeHTML(node.label) + '' + escapeHTML(formatMetric(node)) + '' + escapeHTML(nodeDetail) + "" + badges;
elements.graphNodes.appendChild(nodeElement);
});
@@ -1213,11 +1358,18 @@
});
var qualifiers = Array.isArray(node.metadata.qualifiers) ? node.metadata.qualifiers : [];
var origin = node.metadata.outputOrigin || node.outputOrigin || "UNREPORTED";
+ var categoricalView = activeSimulationView(node);
var liveDefinition = definition
- ? definition.label + " · " + definition.unit + " · display domain " + definition.domain[0] + "–" + definition.domain[1] + "."
+ ? definition.label + " · " + definition.unit + (definition.domain ? " · display domain " + definition.domain[0] + "–" + definition.domain[1] : "") + ". " + definition.basis
: "Backend-provided run record.";
- if (node.stage === "simulation") liveDefinition = "Native tractability dossier; no scalar atomistic metric is imputed.";
- if (node.metadata.displayMetricBasis === "REPRESENTATIVE_DEMO_SCENARIO_V1") {
+ if (node.stage === "simulation" && categoricalView) {
+ liveDefinition = definition.label + " · categorical native view; no scalar score is inferred. " + categoricalView.detail;
+ } else if (node.stage === "simulation") {
+ liveDefinition = state.metrics.simulation === "tractability_fit"
+ ? "Representative branch-context fit for demo comparison; not a native tractability output."
+ : "Native tractability dossier; no scalar atomistic metric is imputed.";
+ }
+ if (node.metadata.displayMetricBasis === "REPRESENTATIVE_DEMO_SCENARIO_V1" && state.metrics[node.stage] === "tractability_fit") {
liveDefinition = (definition ? definition.label + " · " + definition.unit + ". " : "") + "Representative branch value for demo comparison; not a native module output. The native artifact remains attached unchanged.";
}
var interpretability = node.metadata.stationPayload && (
@@ -1365,7 +1517,7 @@
// Every metric key the UI tests with === null; wire omissions must become
// null (missing shelf), never undefined, or dominates()/renderParetoPlot misbehave.
- var WIRE_METRIC_KEYS = ["boldness", "evidence", "plausibility", "rnpv", "positive", "impact", "recruit", "duration", "screens", "risk", "support", "occupancy", "convergence", "tractability_fit"];
+ var WIRE_METRIC_KEYS = ["boldness", "evidence", "plausibility", "rnpv", "positive", "impact", "recruit", "duration", "screens", "risk", "support", "occupancy", "convergence", "tractability_fit", "precedent", "computed"];
function finiteNumber(value) {
return typeof value === "number" && Number.isFinite(value) ? value : null;
@@ -1492,7 +1644,9 @@
support: null,
occupancy: null,
convergence: null,
- tractability_fit: null
+ tractability_fit: null,
+ precedent: null,
+ computed: null
};
var program = {
id: branch.branch_id || "scientific-branch-" + (index + 1),
@@ -1646,6 +1800,27 @@
document.getElementById("module-dialog-summary").textContent = "This scientific run exposes exact per-branch producer artifacts, refs, hashes, origins, terminal reasons, and the server Highlander result.";
}
+ function conciseStageNote(stage, status) {
+ if (status === "RUNNING") return "Running";
+ if (status === "FAILED") return "Terminal · gaps";
+ if (
+ status === "COMPLETE_WITH_WARNINGS" &&
+ stage &&
+ stage.output_origin === "CACHED" &&
+ stage.reason_code === "PINNED_ARTIFACT_REVALIDATED"
+ ) return "Complete · cached replay";
+ if (status === "COMPLETE_WITH_WARNINGS") return "Complete · warnings";
+ if (status === "COMPLETE") {
+ var origin = stage && typeof stage.output_origin === "string" ? stage.output_origin : "";
+ if (origin === "DETERMINISTIC_REPLAY") return "Complete · replay";
+ if (origin === "CACHED") return "Complete · cached";
+ if (origin === "DEMO_FALLBACK") return "Complete · fallback";
+ if (origin === "LIVE") return "Complete · live";
+ return "Complete";
+ }
+ return "Queued";
+ }
+
function ingestSnapshot(ws) {
var hadPrograms = Boolean(state.runData && state.runData.programs && state.runData.programs.length);
var scientific = isScientificSnapshot(ws);
@@ -1675,12 +1850,16 @@
if (index === -1) return;
var truth = normalizeStageTruth(stage, { execution: "QUEUED" });
var status = truth.presentationStatus;
+ var cachedReplay =
+ status === "COMPLETE_WITH_WARNINGS" &&
+ stage.output_origin === "CACHED" &&
+ stage.reason_code === "PINNED_ARTIFACT_REVALIDATED";
state.stageStates[index] =
status === "RUNNING" ? "running" :
status === "COMPLETE" ? "complete" :
- status === "COMPLETE_WITH_WARNINGS" ? "warning" :
+ status === "COMPLETE_WITH_WARNINGS" ? (cachedReplay ? "replay" : "warning") :
status === "FAILED" ? "failed" : "queued";
- state.stageNotes[index] = stage.note || status.toLowerCase();
+ state.stageNotes[index] = conciseStageNote(stage, status);
if (status === "RUNNING") markStagePending(mappedStageId);
if (status === "COMPLETE" || status === "COMPLETE_WITH_WARNINGS" || status === "FAILED") bindStage(mappedStageId);
});
@@ -1842,8 +2021,8 @@
return;
}
if (nonterminal > 0) {
- elements.readinessState.textContent = "BLOCKED · " + nonterminal + " nonterminal stage" + (nonterminal === 1 ? "" : "s");
- elements.packetCounts.innerHTML = '
0 complete0 partial' + programCount + ' running' + nonterminal + " nonterminal stages";
+ elements.readinessState.textContent = "WAITING · " + nonterminal + " stage" + (nonterminal === 1 ? "" : "s");
+ elements.packetCounts.innerHTML = '
0 complete0 partial' + programCount + ' running' + nonterminal + " waiting";
elements.gapConfirm.classList.remove("visible");
elements.launchHighlander.disabled = true;
elements.launchHighlander.textContent = launchName + " · blocked";
@@ -1854,14 +2033,14 @@
: 0;
var blockedCount = state.scientificSnapshot ? programCount - completeCount : 0;
elements.readinessState.textContent = state.scientificSnapshot
- ? "SERVER HIGHLANDER READY · terminal producer packets"
- : "READY WITH TERMINAL GAPS · no nonterminal records";
- elements.packetCounts.innerHTML = '
' + completeCount + ' complete' + (state.scientificSnapshot ? 0 : programCount) + ' partial' + blockedCount + ' blocked0 nonterminal';
+ ? "READY · terminal packets"
+ : "READY · terminal gaps";
+ elements.packetCounts.innerHTML = '
' + completeCount + ' complete' + (state.scientificSnapshot ? 0 : programCount) + ' partial' + blockedCount + ' blocked0 waiting';
elements.gapConfirm.classList.add("visible");
elements.launchHighlander.disabled = !elements.gapConfirmInput.checked;
elements.launchHighlander.textContent = elements.gapConfirmInput.checked
? launchName + " →"
- : (state.scientificSnapshot ? "Acknowledge terminal packets to run" : (BOOT.mode === "http" ? "Acknowledge gaps to continue" : "Acknowledge gaps to launch"));
+ : (state.scientificSnapshot ? "Acknowledge packets" : (BOOT.mode === "http" ? "Acknowledge gaps" : "Acknowledge gaps to launch"));
}
function resetDemo() {
@@ -2662,6 +2841,7 @@
document.querySelectorAll('[data-metric-stage="' + stage + '"]').forEach(function (candidate) {
candidate.setAttribute("aria-pressed", candidate === button ? "true" : "false");
});
+ if (stage === "simulation" && BOOT.mode === "http") updateSimulationAxis(value);
renderGraph();
var xStable = state.nodes.every(function (node) { return priorX.get(node.id) === node.x; });
announce(METRICS[stage][value].label + " selected. Presentation changed; stored records and x lanes " + (xStable ? "remain unchanged." : "changed unexpectedly."));
@@ -2733,6 +2913,47 @@
});
}
+ function updateSimulationAxis(metricKey) {
+ var rail = document.querySelector('.rail-band[data-stage="simulation"]');
+ var low = rail.querySelector(".axis-low");
+ var high = rail.querySelector(".axis-high");
+ var source = document.getElementById("simulation-axis-source");
+ if (metricKey === "precedent") {
+ low.textContent = "not established · top";
+ high.textContent = "supported · bottom";
+ source.innerHTML = "native verdict
categorical";
+ return;
+ }
+ if (metricKey === "computed") {
+ low.textContent = "not reported · top";
+ high.textContent = "reported · bottom";
+ source.innerHTML = "native pocket evidence
categorical";
+ return;
+ }
+ low.textContent = "low · top";
+ high.textContent = "high · bottom";
+ source.innerHTML = "0–100
representative";
+ }
+
+ function configureHttpTractabilityControls(defaultMetric) {
+ var controls = [
+ { key: "tractability_fit", label: "Representative branch fit" },
+ { key: "precedent", label: "Retrieved precedent" },
+ { key: "computed", label: "Computed pocket evidence" }
+ ];
+ state.metrics.simulation = defaultMetric;
+ document.querySelectorAll('[data-metric-stage="simulation"]').forEach(function (button, index) {
+ var control = controls[index];
+ if (!control) return;
+ button.dataset.metricValue = control.key;
+ button.textContent = control.label;
+ button.hidden = false;
+ button.title = METRICS.simulation[control.key].basis;
+ button.setAttribute("aria-pressed", control.key === defaultMetric ? "true" : "false");
+ });
+ updateSimulationAxis(defaultMetric);
+ }
+
function applyBootMode() {
if (BOOT.mode !== "http") return;
@@ -2741,19 +2962,7 @@
setupModeChip.classList.remove("mock");
elements.runButton.textContent = "Run local exploration →";
document.querySelector('.rail-band[data-stage="simulation"] h2').textContent = "Target tractability";
- state.metrics.simulation = "tractability_fit";
- document.querySelectorAll('[data-metric-stage="simulation"]').forEach(function (button) {
- if (button.dataset.metricValue === "support") {
- button.dataset.metricValue = "tractability_fit";
- button.textContent = "Branch tractability fit";
- button.hidden = false;
- button.setAttribute("aria-pressed", "true");
- } else {
- button.hidden = true;
- button.setAttribute("aria-pressed", "false");
- }
- });
- document.getElementById("simulation-axis-source").innerHTML = "0–100
representative";
+ configureHttpTractabilityControls("tractability_fit");
document.getElementById("gap-confirm-copy").textContent = "I acknowledge terminal packet gaps, cached outputs, and labeled fallbacks. Continue to the advisory client-side comparison.";
document.getElementById("restart-demo").textContent = "Refresh snapshot now";
elements.freshnessButton.style.display = "none"; // freshness is real in http mode
@@ -2779,19 +2988,7 @@
elements.runButton.textContent = "Run scientific branch pipeline →";
elements.maxHypotheses.value = "1";
elements.maxHypotheses.disabled = true;
- state.metrics.simulation = "support";
- document.querySelectorAll('[data-metric-stage="simulation"]').forEach(function (button) {
- if (button.dataset.metricValue === "tractability_fit") {
- button.dataset.metricValue = "support";
- button.textContent = "Native dossier (categorical)";
- button.hidden = false;
- button.setAttribute("aria-pressed", "true");
- } else {
- button.hidden = true;
- button.setAttribute("aria-pressed", "false");
- }
- });
- document.getElementById("simulation-axis-source").innerHTML = "categorical
native artifact";
+ configureHttpTractabilityControls("precedent");
document.getElementById("highlander-mode-description").textContent = "Server-native producer packet comparison";
document.getElementById("highlander-mode-chip").textContent = "SERVER HIGHLANDER";
document.getElementById("highlander-server-chip").textContent = "AWAITING PACKETS";
diff --git a/app/styles.css b/app/styles.css
index 37a1cf4..fb47634 100644
--- a/app/styles.css
+++ b/app/styles.css
@@ -541,7 +541,7 @@
/* Screen 2 */
.graph-screen {
- --graph-header: 126px;
+ --graph-header: 160px;
--graph-footer: 84px;
background: #ebe6d8;
overflow: hidden;
@@ -558,10 +558,11 @@
border-bottom: 1px solid var(--line);
}
- .run-identity h1 { margin: 0 0 5px; font-size: 25px; line-height: 1.1; letter-spacing: -.035em; }
- .run-identity p { margin: 0; color: var(--muted); font-size: 10px; }
+ .run-identity { min-width: 0; }
+ .run-identity h1 { margin: 0 0 5px; font-size: 25px; line-height: 1.1; letter-spacing: -.035em; overflow-wrap: anywhere; }
+ .run-identity p { margin: 0; color: var(--muted); font-size: 10px; overflow-wrap: anywhere; }
- .progress-strip { display: grid; grid-template-columns: repeat(5, minmax(88px, 1fr)); gap: 5px; }
+ .progress-strip { min-width: 0; display: grid; grid-template-columns: repeat(5, minmax(88px, 1fr)); gap: 5px; }
.progress-step {
min-width: 0;
@@ -572,10 +573,11 @@
}
.progress-step .stage-num { color: #7a827c; font-size: 9px; font-weight: 900; }
- .progress-step strong { display: block; overflow: hidden; text-overflow: ellipsis; font-size: 10px; white-space: nowrap; }
- .progress-step .stage-state { color: #646c66; font-size: 9px; font-weight: 800; }
+ .progress-step strong { display: block; font-size: 10px; overflow-wrap: anywhere; }
+ .progress-step .stage-state { display: block; margin-top: 2px; color: #646c66; font-size: 9px; font-weight: 800; line-height: 1.3; overflow-wrap: anywhere; }
.progress-step[data-state="running"] { background: var(--blue-soft); border-color: #7cb0bb; }
.progress-step[data-state="complete"] { background: var(--green-soft); border-color: #88caa1; }
+ .progress-step[data-state="replay"] { background: var(--green-soft); border-color: #88caa1; box-shadow: inset 4px 0 0 var(--amber); }
.progress-step[data-state="warning"] { background: var(--amber-soft); border-color: #d8aa58; }
.progress-step[data-state="failed"] { background: var(--red-soft); border-color: #cf8d84; }
@@ -824,15 +826,17 @@
border-top: 1px solid #3a5046;
}
- .readiness-title { display: flex; align-items: baseline; gap: 9px; }
+ .graph-footer > * { min-width: 0; }
+ .readiness-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 9px; }
.readiness-title strong { font-size: 13px; }
.readiness-title span { color: #9eb0a7; font-size: 9px; }
- .packet-counts { display: flex; gap: 5px; margin-top: 6px; }
+ .packet-counts { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.packet-count { padding: 3px 6px; color: #dce6e1; background: #273b32; border-radius: 5px; font-size: 8px; font-weight: 850; }
.gap-confirm { display: none; color: #ffd998; font-size: 9px; }
.gap-confirm.visible { display: flex; align-items: start; gap: 7px; }
.gap-confirm input { margin-top: 2px; }
+ .gap-confirm span { overflow-wrap: anywhere; }
.launch-button { min-width: 192px; min-height: 48px; }
@@ -862,7 +866,7 @@
.inspector-head {
display: flex;
- align-items: center;
+ align-items: flex-start;
justify-content: space-between;
gap: 10px;
padding: 13px 14px;
@@ -870,14 +874,15 @@
background: var(--dark);
}
- .inspector-title { min-width: 0; }
- .inspector-title h2 { margin: 0 0 2px; overflow: hidden; font-size: 16px; text-overflow: ellipsis; white-space: nowrap; }
- .inspector-title p { margin: 0; color: #a3b2ab; font-size: 8px; }
- .inspector-head-actions { display: flex; gap: 5px; }
+ .inspector-title { min-width: 0; flex: 1 1 auto; }
+ .inspector-title h2 { margin: 0 0 2px; font-size: 16px; line-height: 1.2; overflow-wrap: anywhere; }
+ .inspector-title h2:focus { outline: 0; padding-left: 8px; box-shadow: inset 3px 0 0 var(--green); }
+ .inspector-title p { margin: 0; color: #a3b2ab; font-size: 8px; overflow-wrap: anywhere; }
+ .inspector-head-actions { display: flex; flex: 0 0 auto; gap: 5px; }
.icon-button { width: 29px; height: 29px; padding: 0; color: #edf2ef; background: #2e4239; border: 1px solid #53665e; border-radius: 7px; font-weight: 950; }
.collapsed-identity { display: none; color: #cbd7d1; }
- .inspector-body { overflow: auto; padding: 14px; }
+ .inspector-body { min-width: 0; overflow-x: hidden; overflow-y: auto; padding: 14px; }
.state-warning {
margin-bottom: 10px;
@@ -890,19 +895,19 @@
font-weight: 750;
}
- .inspector-status-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px; margin-bottom: 12px; }
+ .inspector-status-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px; margin-bottom: 12px; }
.status-card { min-width: 0; padding: 7px; background: #efede4; border-radius: 7px; }
.status-card span { display: block; color: #777e78; font-size: 7px; font-weight: 900; letter-spacing: .06em; text-transform: uppercase; }
- .status-card strong { display: block; overflow: hidden; font-size: 9px; text-overflow: ellipsis; }
+ .status-card strong { display: block; font-size: 9px; overflow-wrap: anywhere; }
.primary-result { margin-bottom: 12px; padding: 12px; background: var(--green-soft); border: 1px solid #9bcfb0; border-radius: 9px; }
.primary-result span { display: block; color: var(--green-dark); font-size: 8px; font-weight: 950; text-transform: uppercase; }
- .primary-result strong { display: block; margin-top: 3px; font-size: 20px; }
- .primary-result p { margin: 4px 0 0; font-size: 8px; }
+ .primary-result strong { display: block; margin-top: 3px; font-size: 20px; line-height: 1.15; overflow-wrap: anywhere; }
+ .primary-result p { margin: 4px 0 0; font-size: 8px; overflow-wrap: anywhere; }
.inspector-section { margin: 10px 0; border-top: 1px solid #ddd8ca; }
- .inspector-section summary { padding: 10px 0 6px; cursor: pointer; font-size: 10px; font-weight: 900; }
- .inspector-section p, .inspector-section li { color: #4f5953; font-size: 9px; }
+ .inspector-section summary { padding: 10px 0 6px; cursor: pointer; font-size: 10px; font-weight: 900; overflow-wrap: anywhere; }
+ .inspector-section p, .inspector-section li { color: #4f5953; font-size: 9px; overflow-wrap: anywhere; }
.inspector-section ul { margin: 4px 0 8px; padding-left: 17px; }
.native-artifact {
max-height: 340px;
@@ -917,9 +922,10 @@
white-space: pre-wrap;
word-break: break-word;
}
- .interpretability-view { margin: 10px 0; padding: 10px; background: #eef4e9; border: 1px solid #b7c7ad; border-radius: 8px; }
- .interpretability-view > section h4 { margin: 0 0 4px; font-size: 13px; }
- .interpretability-view > section p { margin: 4px 0; }
+ .interpretability-view { min-width: 0; margin: 10px 0; padding: 10px; background: #eef4e9; border: 1px solid #b7c7ad; border-radius: 8px; }
+ .interpretability-view > section { min-width: 0; }
+ .interpretability-view > section h4 { margin: 0 0 4px; font-size: 13px; overflow-wrap: anywhere; }
+ .interpretability-view > section p { margin: 4px 0; overflow-wrap: anywhere; }
.evidence-artifact { padding: 9px; background: #f0eee5; border-radius: 8px; }
.evidence-bars { display: grid; gap: 5px; margin: 8px 0; }
@@ -928,9 +934,11 @@
.evidence-bar span { display: block; height: 100%; background: var(--green-dark); }
@media (min-width: 1800px) {
+ .graph-screen.inspector-open { --inspector-reserved: calc(var(--inspector) + 34px); }
+ .graph-screen.inspector-open:has(.inspector.collapsed) { --inspector-reserved: calc(var(--inspector-collapsed) + 34px); }
.graph-screen.inspector-open .run-header,
- .graph-screen.inspector-open .graph-scroller,
- .graph-screen.inspector-open .graph-footer { padding-right: calc(var(--inspector) + 34px); }
+ .graph-screen.inspector-open .graph-scroller { margin-right: var(--inspector-reserved); }
+ .graph-screen.inspector-open .graph-footer { right: var(--inspector-reserved); }
.inspector { top: 10px; right: 10px; bottom: 10px; border-radius: 10px; }
}
diff --git a/tests/frontend-live-contract.test.mjs b/tests/frontend-live-contract.test.mjs
index 3a1a9c0..e316be2 100644
--- a/tests/frontend-live-contract.test.mjs
+++ b/tests/frontend-live-contract.test.mjs
@@ -41,19 +41,100 @@ function ingestScientificSnapshot(snapshot = scientificSnapshot) {
test("stage metrics are exposed as visible button groups instead of selects", () => {
assert.doesNotMatch(appHtml, /