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
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
.analysis-source { border: 1px solid var(--pw-line); border-radius: 8px; padding: 16px; margin-block: 16px; }
.analysis-source p { font-size: var(--text-caption); color: var(--pw-muted); }
.analysis-source pre { font-family: var(--font-mono); font-size: var(--text-body); line-height: 1.7; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 360px; overflow: auto; }
.analysis-source-command + .analysis-source-command { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--pw-line); }
.analysis-source-command-meta { display: flex; flex-wrap: wrap; gap: 4px 16px; color: var(--pw-muted); }
.analysis-source pre.analysis-source-output { font-size: var(--text-lead); line-height: var(--leading-prose); max-height: none; overflow: visible; }
.analysis-source p.analysis-source-empty { font-size: var(--text-lead); color: var(--pw-text); }
.analysis-source-content details > summary { min-height: 44px; padding-block: 10px; cursor: pointer; }
.analysis-source-raw { margin-top: 12px; border-top: 1px solid var(--pw-line); }
.analysis-method { border-block-start: 1px solid var(--pw-line); margin-top: 16px; padding-top: 12px; color: var(--pw-muted); font-size: var(--text-lead); }
.analysis-method > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; color: var(--pw-text); }
.analysis-method dl { display: grid; grid-template-columns: 100px minmax(0, 1fr); gap: 8px 16px; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,39 @@ export function ResearchReportSummary({analysis: a}: {analysis: Controller}) {
onExpand={origin => {a.trigger.current = origin; a.show();}} onCitation={a.readCitation}/></div> : null;
}

function CitationContent({citation, zh}: {citation: Citation; zh: boolean}) {
// A reading projection of the native completed-command envelope, not a
// source verifier. Unknown or mixed formats retain the complete literal.
const commands: {output: string; argv: string[]; exitCode: number; status: string}[] = [];
try {
for (const line of citation.text.split("\n")) {
const event: {type?: unknown; payload?: {type?: unknown; item?: Record<string, unknown>}} | null = JSON.parse(line);
const item = event?.payload?.item;
if (event?.type !== "event_msg" || event.payload?.type !== "item_completed"
|| item?.type !== "CommandExecution" || typeof item.aggregated_output !== "string"
|| !Array.isArray(item.command) || !item.command.every((arg: unknown) => typeof arg === "string")
|| typeof item.exit_code !== "number" || !Number.isSafeInteger(item.exit_code) || typeof item.status !== "string") {
return <pre>{citation.text}</pre>;
}
commands.push({output: item.aggregated_output, argv: item.command, exitCode: item.exit_code, status: item.status});
}
} catch {
return <pre>{citation.text}</pre>;
}
return <div className="analysis-source-content">
{commands.map((command, index) => <div className="analysis-source-command" key={index}>
<div className="analysis-source-command-meta">
{commands.length > 1 ? <span>{zh ? "行" : "Line"} {citation.first_line + index}</span> : null}
<span>{zh ? "退出码" : "Exit code"} {command.exitCode}</span><span>{command.status}</span>
</div>
{command.output ? <pre className="analysis-source-output">{command.output}</pre>
: <p className="analysis-source-empty">{zh ? "输出为空" : "Empty output"}</p>}
<details><summary>{zh ? "命令参数" : "Command arguments"}</summary><pre>{JSON.stringify(command.argv, null, 2)}</pre></details>
</div>)}
<details className="analysis-source-raw"><summary>{zh ? "完整原始记录" : "Complete raw record"}</summary><pre>{citation.text}</pre></details>
</div>;
}

export function ResearchCitationPanel({analysis: a}: {analysis: Controller}) {
if (!a.citation && !a.citationBusy && !a.citationError) return null;
const zh = a.zh;
Expand All @@ -298,7 +331,8 @@ export function ResearchCitationPanel({analysis: a}: {analysis: Controller}) {
<header><h4>{zh ? "引用原文" : "Cited original"}</h4><button type="button" onClick={a.closeCitation} aria-label={zh ? "关闭原文" : "Close source"}><X size={14} aria-hidden/></button></header>
{a.citationBusy ? <p role="status">{zh ? "正在读取引用原文…" : "Reading cited source…"}</p> : null}
{a.citationError ? <p className="analysis-error" role="alert">{a.citationError}</p> : null}
{a.citation ? <><p>{a.citation.artifact} · {zh ? "行" : "lines"} {a.citation.first_line}–{a.citation.last_line}</p><pre>{a.citation.text}</pre></> : null}
{a.citation ? <><p>{a.citation.artifact} · {zh ? "行" : "lines"} {a.citation.first_line}–{a.citation.last_line}</p>
<CitationContent key={`${a.pack?.analysis_key}:${a.citation.source_id}:${a.citation.first_line}:${a.citation.last_line}`} citation={a.citation} zh={zh}/></> : null}
</section>;
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 69 additions & 2 deletions examples/personal-workspace-browser/research-native-reading.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export const researchNativeReadingScenario = {
expected: [0.0000001234567890123456, 0.0000001234567890123456, 0.0000001123456789012345, 0.0000001123456789012345], display: ["1.235E-7", "1.235E-7", "1.123E-7", "1.123E-7"], gain: "-1.111E-8"},
{id: "large", values: [123456789.12345679], higher: true, retained: 0,
expected: [123456789.12345679, 123456789.12345679, 123456789.12345679, 123456789.12345679], display: ["1.235E8", "1.235E8", "1.235E8", "1.235E8"], gain: "0"},
...["command", "mixed", "damaged", "empty-output", "unsafe-exit"].map(source => ({id: `${source}-source`, source,
values: [2, 1], higher: false, retained: 0, expected: [1, 2, 1, 2]})),
];
const root = await mkdtemp(resolve(tmpdir(), "loopx-native-reading-"));
let owner, page;
Expand All @@ -48,14 +50,30 @@ export const researchNativeReadingScenario = {
}));
if (c.gain) native.comparison = {baseline_result_id: "result-0",
retention_citation: {source_id: "facts", first_line: c.retained + 1, last_line: c.retained + 1}};
const text = c.values.map((v, i) => `Synthetic observation ${i}: value ${v ?? "unknown"}; valid ${v !== null}.`).join("\n") || "No measurements captured.";
let text = c.values.map((v, i) => `Synthetic observation ${i}: value ${v ?? "unknown"}; valid ${v !== null}.`).join("\n") || "No measurements captured.";
if (c.source) {
const command = (output, exitCode) => JSON.stringify({type: "event_msg", ordinal: exitCode + 1,
payload: {type: "item_completed", item: {type: "CommandExecution", status: "completed", exit_code: exitCode,
command: ["python", "-c", "print('synthetic output')"], aggregated_output: output}}});
const output = JSON.stringify({cases: 8, improved: 3, regressed: 2, unchanged: 3, complete_cost: null}, null, 2);
const longFailure = "Validation failed\n0 accepted updates\n<script>window.sourceExecuted=true</script>\n"
+ Array.from({length: 32}, (_, i) => `Synthetic case ${i + 1}: negative result retained; 完整成本仍未知。`).join("\n");
text = [command(c.source === "empty-output" ? "" : output, 0), c.source === "mixed"
? JSON.stringify({type: "unknown_event", text: "Unrecognized source MUST_REMAIN_LITERAL"})
: c.source === "damaged" ? "{malformed MUST_REMAIN_LITERAL"
: command(c.source === "empty-output" ? "" : longFailure, c.source === "unsafe-exit" ? Number.MAX_SAFE_INTEGER + 1 : 17)].join("\n");
native.samples[0].citation.last_line = 2;
}
a.artifacts["facts.txt"] = text;
a.artifacts["native.json"] = JSON.stringify(native);
Object.assign(a.input.sources[0], {text, artifact_sha256: createHash("sha256").update(text).digest("hex")});
return a;
});
const path = resolve(root, "cases.json");
await writeFile(path, JSON.stringify(fixture));
// Same public synthetic data for reproducible before/after inspection.
await writeFile(resolve(outputDir, "research-citation-reader-fixture.json"), JSON.stringify({...fixture,
analyses: fixture.analyses.filter(a => a.title === "command-source")}));
owner = await startHistoryOwner(path);
page = await browser.newPage({viewport: {width: 1280, height: 900}, hasTouch: true});
const errors = [];
Expand Down Expand Up @@ -115,6 +133,47 @@ export const researchNativeReadingScenario = {
await page.waitForFunction(() => document.activeElement?.getAttribute("aria-label")?.startsWith("原生保留结果:"));
await page.screenshot({path: resolve(outputDir, "research-native-negative-phone.png"), animations: "disabled"});
}
if (c.source) {
const retained = native.getByRole("button", {name: /原生保留结果.*查看原始证据/});
await retained.focus(); await retained.press("Enter");
const citation = page.getByRole("region", {name: "引用原文", exact: true});
await citation.waitFor();
const literal = fixture.analyses.find(a => a.title === c.id).artifacts["facts.txt"];
if (["mixed", "damaged", "unsafe-exit"].includes(c.source)) {
await citation.locator("pre").waitFor();
assert.equal(await citation.locator("pre").textContent(), literal, "Never discard unsupported or malformed neighboring evidence");
assert.equal(await citation.locator("details").count(), 0);
} else {
await citation.locator(".analysis-source-content").waitFor();
assert.equal(await citation.locator(".analysis-source-command").count(), 2);
await citation.getByText("行 2", {exact: true}).waitFor();
await citation.getByText("退出码 17", {exact: true}).waitFor();
const raw = citation.locator(".analysis-source-raw");
assert.equal(await raw.getAttribute("open"), null);
await raw.locator("summary").tap();
assert.equal(await raw.locator("pre").textContent(), literal, "Raw evidence remains byte-for-byte available");
await raw.locator("summary").tap();
if (c.source === "empty-output") assert.equal(await citation.getByText("输出为空", {exact: true}).count(), 2);
else {
const records = literal.split("\n").map(line => JSON.parse(line).payload.item);
for (const [i, record] of records.entries()) {
assert.equal(await citation.locator(".analysis-source-output").nth(i).textContent(), record.aggregated_output);
}
assert.equal(await citation.locator("script").count(), 0);
assert.equal(await page.evaluate(() => window.sourceExecuted), undefined);
for (const width of [1280, 390]) {
await page.setViewportSize({width, height: 900});
await citation.evaluate(e => e.scrollIntoView({block: "start"}));
assert(await citation.evaluate(e => e.scrollWidth <= e.clientWidth + 1));
assert.equal(await citation.locator(".analysis-source-output").first().evaluate(e => parseFloat(getComputedStyle(e).fontSize)), 16);
assert(await citation.locator(".analysis-source-output").last().evaluate(e => e.clientHeight > 360), "Full long output uses page reading, not an inner clipped wall");
await page.screenshot({path: resolve(outputDir, `research-citation-output-${width}.png`), animations: "disabled"});
}
}
}
await citation.press("Escape");
await page.waitForFunction(() => document.activeElement?.getAttribute("aria-label")?.startsWith("原生保留结果:"));
}
if (c.unit) {
await page.evaluate(() => document.documentElement.style.fontSize = "200%");
assert(await native.evaluate(e => e.scrollWidth <= e.clientWidth + 1), "Long unit remains readable at 200% text");
Expand All @@ -132,12 +191,20 @@ export const researchNativeReadingScenario = {
await page.getByRole("button", {name: "About native measurements", exact: true}).focus();
await page.getByRole("tooltip").waitFor({state: "visible"});
await page.keyboard.press("Escape");
await selector.selectOption({label: "command-source"});
const selected = await selector.inputValue();
await page.locator(`.research-progress-scope[data-pack-id="${selected}"]`).waitFor();
await page.getByRole("region", {name: "Native measurements", exact: true}).getByRole("button", {name: /Native retained result.*Read original evidence/}).click();
const originalPanel = page.getByRole("region", {name: "Cited original", exact: true});
await originalPanel.getByText("Exit code 17", {exact: true}).waitFor();
await originalPanel.getByText("Complete raw record", {exact: true}).waitFor();
await originalPanel.press("Escape");
const readback = await (await page.request.get(`${owner.base}/fixture/readback`)).json();
assert.equal(readback.source_unchanged, true);
assert.equal(readback.mutation_attempts, 0);
assert.deepEqual(errors, []);
return {coverageEntries: collectCoverage ? await page.coverage.stopJSCoverage() : [],
note: "real-owner native result roles: signed/zero/invalid/missing/partial, compact decimal/tiny/large values with full-precision details, exact citations, readable desktop/390/200%, Chinese and English; no mutation"};
note: "real-owner native roles and citations; literal command output, failure/empty/long text, raw preservation, unknown/malformed fallback, exact focus return, desktop/390 and Chinese/English; no mutation"};
} finally {
await page?.close();
await owner?.close();
Expand Down
Loading