open-standards/wai/quantum-studio/src/answer-view.js
Transaction Science 2526b6ac83 wai-quantum: answers drawn as they arrive in the Studio, and a correctness record kept only when asked
The Studio has an answer pane. It reads an answer in the view layer as it
streams from the assistant, draws each statement when it arrives, runs the
engine call each one is bound to, and seals the answer with a receipt that
is checked again by re-running. A stored answer carries its receipt and is
verified when it is read back.

The correctness record is off by default. When it is turned on, it keeps
one record per answer in the browser: the codes, the verdict level, the
repairs, whether the answer was accepted, and the receipt id. Nothing is
sent anywhere, and the records leave only when exported. The failures
export as an eval corpus in the conformance format. A test shows that
nothing is recorded while it is off.

The engine adds a noise argument to @Sample and @Check, keeps root-listed
names that have not arrived in the snapshot order, and gives a model the
layer's rules with a worked example. The desktop app answers the same
calls. macOS can read a running process's energy, for the model harness.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 08:42:54 -04:00

216 lines
12 KiB
JavaScript

// An answer in the answer view layer (wai/extensions/quantum-answers.md), drawn as
// it arrives: the root's layout as soon as its line does, each view once its
// statement is ready, its numbers from its engine call carried out here, a program's
// circuit gate by gate while its lines stream in, and every problem with its code.
//
// The text goes through the engine's own reader (engine.answer('snapshot'), then
// 'parse' once it has ended), so what is drawn is what the layer says; a view
// whose call the engine refuses shows the refusal, for that view alone. A seal
// is checked here, by the engine, and the answer can be kept with its receipt.
import { el, sameness } from './dom.js';
import { circuitSvg } from './circuit-svg.js';
import { verdictLine } from './verdict-view.js';
import { chips, fidelityChip } from './critique-view.js';
import { frontierSvg, compact as count, duration } from './estimate-view.js';
import { blochSphere } from './state-views.js';
import { METHOD_NAMES, countsBlock, expectationsBlock, amplitudesBlock } from './result-parts.js';
const note = (text, attrs = {}) => el('p', { class: 'qs-note', ...attrs, text });
// A view's card: its name and view, then what it shows.
function card(name, what) {
const body = el('div', { class: 'qs-av-body' }, note('waiting for its statement…'));
const node = el('div', { class: 'qs-av-card', 'data-name': name, 'data-state': 'waiting' },
el('div', { class: 'qs-k' }, el('span', { text: name }), el('span', { class: 'qs-av-what', text: what ? ` · ${what}` : '' })), body);
return { node, body };
}
// The program a fence has opened and not yet closed, as far as it has come.
function openProgram(text) {
const lines = text.split('\n');
let open = null;
for (let i = 0; i < lines.length - 1; i++) {
const t = lines[i].trim();
if (open && t === '```') open = null;
else if (open) open.lines.push(lines[i]);
else {
const m = /^```(?:qasm|openqasm)\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/.exec(t);
if (m) open = { name: m[1], lines: [] };
}
}
return open && open.lines.length ? { name: open.name, qasm: open.lines.join('\n') + '\n' } : null;
}
// What a statement's value shows, by its view.
function draw(view, v, engine) {
if (!v || v.ok === false) {
return [el('p', { class: 'qs-warn', text: v?.error || 'the engine gave no value' }), v?.code ? note(`${v.code} · ${v.outcome}: ${v.meaning || ''}`, { class: 'qs-note qs-av-code' }) : null];
}
const x = v.value;
switch (view) {
case 'Text': return [el('p', { class: 'qs-why', text: x.text ?? '' })];
case 'Circuit':
case 'program': {
if (!x.layout) return [note(x.why || 'not drawn')];
const { svg } = circuitSvg(x.layout);
return [el('div', { class: 'qs-av-circuit' }, svg), x.description ? el('details', {}, el('summary', { text: 'The circuit in words' }), el('p', { class: 'qs-why', text: x.description })) : null];
}
case 'Histogram': return [note(`${METHOD_NAMES[x.method] || x.method}: ${x.why || ''}`), countsBlock(x.result)];
case 'Expectation': return [expectationsBlock(x.result)];
case 'Amplitudes': return [amplitudesBlock(x.result)];
case 'Bloch': {
const b = x.bloch || {};
return [el('div', { class: 'qs-av-bloch' }, blochSphere(b, 120), el('p', { class: 'qs-why', text: `qubit ${b.qubit}: (${Number(b.x).toFixed(4)}, ${Number(b.y).toFixed(4)}, ${Number(b.z).toFixed(4)}), length ${Number(b.length).toFixed(4)}, purity ${Number(b.purity).toFixed(4)}` }))];
}
case 'Verdict': return [verdictLine(x, { sealed: false })];
case 'Equivalence': {
const s = sameness(x);
return [el('span', { class: `qs-badge ${s.cls}`, text: s.text }), s.why ? el('span', { class: 'qs-why', text: ` ${s.why}` }) : null];
}
case 'Critique': return [el('div', { class: 'qs-chips' }, ...chips(x), fidelityChip(x)), el('ul', { class: 'qs-smells' }, ...(x.smells || []).map((s) => el('li', { class: 'qs-smell' }, el('span', { class: 'qs-smell-text', text: s.message }))))];
case 'Estimate': {
const e = x.estimate || {};
const pts = x.frontier || [];
return [el('p', { class: 'qs-why', text: `${count(e.physical_qubits)} physical qubits, ${e.factories} factories, ${duration(e.runtime_s)} at code distance ${e.distance}` }), pts.length ? el('div', { class: 'qs-frontier-figure' }, frontierSvg(pts, Math.max(0, pts.findIndex((p) => p.headline)), () => {})) : null];
}
case 'Compile': {
const c = x.certificate || {};
return [el('p', { class: 'qs-why', text: `${c.verdict}: ${c.output?.counts?.gates} gates, ${c.output?.counts?.t_count} T, within ${c.epsilon?.bound} of the program (budget ${c.epsilon?.budget})` })];
}
case 'Plan': return [el('p', {}, el('b', { text: METHOD_NAMES[x.method] || x.method || '' }), el('span', { class: 'qs-why', text: ` ${x.why || ''}` }))];
case 'Receipt': {
const check = el('span', { class: 'qs-badge', 'data-receipt': 'checking', text: 'checking here…' });
engine.verify(x).then((r) => {
check.dataset.receipt = r.valid ? 'valid' : 'invalid';
check.className = `qs-badge ${r.valid ? 'ok' : 'bad'}`;
check.textContent = r.valid ? `✓ seals ${(x.request?.sealed || []).join(', ')}, checked here` : '✗ does not verify';
}).catch((e) => { check.dataset.receipt = 'unchecked'; check.textContent = `not checked: ${e.message}`; });
return [check, note(`signed by ${(x.result?.receipt?.key || '').slice(0, 16)}…; the receipt shows that this key sealed these values for this answer, not who holds the key.`)];
}
default: return [note(JSON.stringify(x).slice(0, 400))];
}
}
// A renderer: `feed(text)` as the answer arrives, `end()` once it has ended. `engine`
// answers answer(...), layout(...) and verify(...); `onEnded(summary)` hears the
// finished answer: its parse, each view's value, and the seal's.
export function answerRenderer({ engine, onEnded = () => {} }) {
const shell = el('div', { class: 'qs-av-shell', id: 'qs-av-shell' });
const live = el('div', { class: 'qs-av-live', id: 'qs-av-live' });
const diags = el('ul', { class: 'qs-av-diags', id: 'qs-av-diags' });
const node = el('div', { class: 'qs-av', id: 'qs-av' }, shell, live, diags);
let text = '';
let cards = new Map();
let drawn = new Set();
let values = new Map();
let queue = Promise.resolve();
let lines = 0;
const cardFor = (name, what) => {
if (!cards.has(name)) {
const c = card(name, what);
cards.set(name, c);
shell.append(c.node);
}
return cards.get(name);
};
async function refresh(complete) {
const a = await engine.answer(complete ? 'parse' : 'snapshot', { text });
if (a.error) { diags.replaceChildren(el('li', { class: 'qs-warn', text: a.error })); return a; }
node.dataset.root = a.root ?? '';
const by = new Map(a.statements.map((s) => [s.name, s]));
// The root's layout first: a card for each view it lists, waiting until it is ready.
for (const name of a.order) {
const s = by.get(name);
const c = cardFor(name, s?.view ?? (s?.kind === 'program' ? 'program' : null));
if (s?.view) c.node.querySelector('.qs-av-what').textContent = ` · ${s.view}`;
}
// Each view and program that is ready, drawn once, its call carried out here.
for (const name of a.order) {
const s = by.get(name);
if (!s || drawn.has(name)) continue;
const c = cardFor(name);
if (s.status === 'dropped') { c.node.dataset.state = 'dropped'; c.body.replaceChildren(note('left out: see the problems below')); drawn.add(name); continue; }
if (s.status !== 'ready') { c.node.dataset.state = 'pending'; c.body.replaceChildren(note(`waiting for ${s.deps.filter((d) => by.get(d)?.status !== 'ready').join(', ') || 'what it names'}…`)); continue; }
drawn.add(name);
c.node.dataset.state = 'running';
c.body.replaceChildren(note('running on the engine here…'));
const view = s.kind === 'program' ? 'program' : s.view;
const v = s.kind === 'program'
? await engine.layout(s.qasm).then((l) => (l.error ? { ok: false, error: l.error } : { ok: true, value: { layout: l } }), (e) => ({ ok: false, error: e.message }))
: await engine.answer('value', { text, name, complete });
values.set(name, v);
c.body.replaceChildren(...draw(view, v, engine).filter(Boolean));
c.node.dataset.state = v.ok === false ? 'refused' : 'drawn';
c.node.dataset.view = view;
}
// A program still arriving: its circuit, gate by gate.
const open = complete ? null : openProgram(text);
if (open) {
const l = await engine.layout(open.qasm).catch(() => null);
if (l && !l.error) live.replaceChildren(el('div', { class: 'qs-k', text: `${open.name}, arriving` }), circuitSvg(l).svg);
} else live.replaceChildren();
// The problems: each with its line and code; the errors are what a model is sent back.
diags.replaceChildren(...a.diagnostics.map((d) => el('li', { class: `qs-av-diag ${d.severity}`, 'data-code': d.code, 'data-severity': d.severity },
el('span', { class: 'qs-badge' + (d.severity === 'error' ? ' bad' : ''), text: d.code }), ` line ${d.line}: ${d.message}`)));
return a;
}
return {
node,
// The next piece of the answer; the view redraws as each line completes.
feed(piece) {
text += piece;
const now = text.split('\n').length - 1;
if (now === lines) return queue;
lines = now;
queue = queue.then(() => refresh(false)).catch(() => {});
return queue;
},
// The answer has ended: read whole, everything left drawn or left out.
end() {
queue = queue.then(async () => {
const a = await refresh(true);
for (const [name, c] of cards) if (!drawn.has(name)) { c.node.dataset.state = 'dropped'; c.body.replaceChildren(note('never arrived')); }
node.dataset.done = 'true';
onEnded({ answer: a, text, values });
return a;
});
return queue;
},
reset() {
text = ''; lines = 0; cards = new Map(); drawn = new Set(); values = new Map();
shell.replaceChildren(); live.replaceChildren(); diags.replaceChildren();
delete node.dataset.done;
},
text: () => text,
};
}
// What a finished answer gives a correctness record: its codes (every
// diagnostic's, and each refused view's), the first verdict it shows, its first
// program, the intent of its first check, and its seal's digest.
export function answerRecord({ answer, values }) {
const codes = [...answer.diagnostics.map((d) => d.code)];
for (const v of values.values()) for (const c of v?.codes || []) if (!codes.includes(c)) codes.push(c);
const stmts = answer.statements || [];
const verdictOf = stmts.find((s) => s.view === 'Verdict' && values.get(s.name)?.ok);
const program = stmts.find((s) => s.kind === 'program' && s.reads);
const seal = stmts.find((s) => s.view === 'Receipt' && values.get(s.name)?.ok);
const fields = { codes, program: program?.qasm, receipt: seal ? values.get(seal.name).value?.result?.receipt?.digest : undefined };
if (verdictOf) {
const v = values.get(verdictOf.name).value;
fields.verdict = { level: v.level, holds: v.holds };
// The intent its check held the program to, as a job's intent.
const call = verdictOf.expr?.args?.[0];
const intent = call?.call === 'Check' ? call.args?.[1] : null;
if (intent?.object) {
const entries = intent.object;
const ref = (e) => stmts.find((s) => s.name === e?.ref)?.qasm;
if (entries.length === 1 && ['equivalent', 'prepares'].includes(entries[0][0]) && entries[0][1].ref) fields.intent = { [entries[0][0]]: ref(entries[0][1]) };
else fields.intent = { distribution: Object.fromEntries(entries.map(([k, e]) => [k, e.value])) };
} else if (intent?.ref) fields.intent = { equivalent: stmts.find((s) => s.name === intent.ref)?.qasm };
}
return fields;
}