Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c9a8ae9
test(site): validate time machine publication journey
loopx-agent Oct 16, 2026
acc16b4
test(site): await record focus before keyboard navigation
loopx-agent Oct 16, 2026
bcb67a8
test(todos): align consumers with administrative lease semantics
loopx-agent Oct 16, 2026
e35b3bf
test(chat): keep research history navigation on its frontend
loopx-agent Oct 16, 2026
6435cb4
fix(benchmark): reuse canonical digest owner for trajectory evidence
loopx-agent Oct 16, 2026
96a01cf
fix(recovery): retain the verified backup checkpoint in audit readback
loopx-agent Oct 16, 2026
ee81fd5
Merge remote-tracking branch 'origin/main' into codex/trajectory-dige…
loopx-agent Oct 16, 2026
560fc66
fix(benchmark): preserve UTF-8 trajectory evidence across locales
loopx-agent Oct 16, 2026
381fbca
Merge remote-tracking branch 'origin/main' into codex/ci-administrati…
loopx-agent Oct 17, 2026
e3f9452
test(ci): retire managed runtimes with their disposable fixtures
loopx-agent Oct 17, 2026
60ee7b3
test(ci): keep malformed locator bytes outside teardown authority
loopx-agent Oct 17, 2026
951653b
Merge commit 'fdf03ec6112aedf497b7ab1d6e5bc4db19c2ae5f' into codex/ci…
loopx-agent Oct 17, 2026
700f236
Merge qualified source #6691 into composed CI proposal
loopx-agent Oct 17, 2026
e579b4b
Merge qualified source #6693 into composed CI proposal
loopx-agent Oct 17, 2026
8c7f609
Merge qualified source #6690 into composed CI proposal
loopx-agent Oct 17, 2026
3b7357b
Merge qualified source #6688 into composed CI proposal
loopx-agent Oct 17, 2026
bbe5e84
Merge qualified source #6684 into composed CI proposal
loopx-agent Oct 17, 2026
62f59aa
Merge existing CI fixture repairs onto current main
loopx-agent Oct 17, 2026
8e23ee3
test: repair refusal and subprocess qualification oracles
loopx-agent Oct 17, 2026
d55bc69
Merge current main for CI fixture qualification
loopx-agent Oct 17, 2026
1258cfb
Merge current consumer fixes into composed CI proposal
loopx-agent Oct 17, 2026
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
66 changes: 64 additions & 2 deletions examples/dashboard-frontstage-browser-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,66 @@ async function assertAnchorInView(page, id) {
});
}

async function assertTimeMachineJourney(page, lang) {
await page.goto(`${publicOrigin}/loopx/?lang=${lang}`);
const entry = page.locator('#explore .resource-card[href*="/lab/time-machine/"]');
await entry.focus();
await Promise.all([
page.waitForURL(`${publicOrigin}/loopx/lab/time-machine/${lang === "zh" ? "?lang=zh" : ""}`),
entry.press("Enter"),
]);
// Anchors come from the three public captures, independently of the UI scene copy.
const captures = [
["portfolio", 76, "finding_5b6717a721136e001cd0cd18d38ac167431bbe265445e669bac92c72d990aef2", /13\.84/, /20\.53/],
["graph", 134, "finding_b5860e437b2905fbc0b9db2bbdc3ba567783817eee67e0ecc433bf4e65c00b1a", /32%/],
["ad", 105, "finding_c652328314cf9ac2757f85dfcbdb9907bb719cf1a1e36185926640694663a9f2", /32/],
];
for (const [index, [id, count, findingId, ...metrics]] of captures.entries()) {
if (index) {
await page.locator(".tm-cases button").nth(index).click();
await page.waitForURL((url) => url.searchParams.get("case") === id);
}
const capture = JSON.parse(await readFile(resolve(exportSite, `lab/time-machine/data/${id}.json`), "utf8"));
assert.equal(capture.events.length, count);
const finding = capture.events.find((row) => row.result_id === findingId);
assert.equal(finding?.event_kind, "finding");
const choice = page.locator(".tm-choices button:not([disabled])").first();
await choice.focus();
await choice.press("Enter");
await page.locator(".tm-revealed").waitFor();
for (const metric of metrics) assert.match(await page.locator(".tm-revealed").innerText(), metric);
const title = await page.locator(".tm-revealed h3").innerText();
await page.locator(".tm-choices button").nth(1).click();
assert.equal(await page.locator(".tm-revealed h3").innerText(), title, "either choice reveals the same historical outcome");
await page.getByRole("button", { name: lang === "zh" ? "核对这条发现" : "Inspect this finding", exact: true }).click();
await page.locator(".tm-original").waitFor({ state: "visible" });
// Inspect queues a focus transfer; finish it before sending the next key.
await page.waitForFunction(() => document.querySelector(".tm-original h3") === document.activeElement);
assert.equal(await page.locator(".tm-record-body").textContent(), finding.summary, "the outcome opens its original finding");
assert.equal(await page.locator(".tm-source").getAttribute("href"), `/loopx/lab/time-machine/data/${id}.json`);
const cursor = page.locator("#tm-cursor");
assert.equal(await cursor.getAttribute("max"), String(count));
await cursor.press("End");
await page.waitForFunction((summary) => document.querySelector(".tm-record-body")?.textContent === summary, capture.events.at(-1).summary);
assert.equal(await cursor.inputValue(), String(count));
await page.locator(".tm-return").click();
assert.ok(await page.locator("#tm-question").evaluate((heading) => heading === document.activeElement));
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1), "Time Machine fits the viewport");
}
// A real missing HTTP capture must hide outcomes and disable choices; retry
// then reads the actual exported bytes through the production verifier.
await page.route("**/lab/time-machine/data/portfolio.json", (route) => route.fulfill({ status: 404, body: "Missing capture" }), { times: 1 });
await page.reload();
await page.locator(".tm-cases button").first().click();
await page.locator(".tm-error [role=alert]").waitFor();
assert.equal(await page.locator(".tm-revealed").count(), 0);
assert.equal(await page.locator(".tm-choices button:disabled").count(), 2);
await page.locator(".tm-error button").click();
await page.locator(".tm-choices button:not([disabled])").first().click();
await page.locator(".tm-revealed").waitFor();
assert.match(await page.locator(".tm-revealed").innerText(), /13\.84/);
}

let browser;
try {
for (let i = 0; ; i++) {
Expand Down Expand Up @@ -249,7 +309,7 @@ try {
await page.locator("#explore").waitFor();
assert.equal(await page.locator("html").getAttribute("lang"), lang === "zh" ? "zh-CN" : "en");
assert.equal(await page.locator('a[href*="deprecated"], a[href*="frontstage/"]').count(), 0);
const expected = ["docs/guides/personal-workspace-user-guide/", `benchmarks/swe-marathon/${lang === "zh" ? "?lang=zh" : ""}`, `benchmarks/lhtb/${lang === "zh" ? "?lang=zh" : ""}`, "benchmarks/deepswe/behavior-discovery/", "benchmarks/deepswe-sol/", `docs/showcases/index${lang === "en" ? ".en" : ""}.html`];
const expected = ["docs/guides/personal-workspace-user-guide/", `benchmarks/swe-marathon/${lang === "zh" ? "?lang=zh" : ""}`, `benchmarks/lhtb/${lang === "zh" ? "?lang=zh" : ""}`, "benchmarks/deepswe/behavior-discovery/", "benchmarks/deepswe-sol/", `lab/time-machine/${lang === "zh" ? "?lang=zh" : ""}`];
assert.deepEqual(await page.locator("#explore .resource-card").evaluateAll((links) => links.map((a) => a.getAttribute("href"))), expected.map((path) => `/loopx/${path}`));
if (width === 390) {
await page.getByRole("button", { name: "Open navigation" }).click();
Expand Down Expand Up @@ -279,12 +339,14 @@ try {
await page.goto(`${publicOrigin}/loopx/${expected[i]}`);
await page.locator("h1").first().waitFor();
}
await assertTimeMachineJourney(page, lang);
console.log(`Time Machine discovery, three outcomes, original records and retry: ${width}px, ${lang}: ok`);
}
}
if (process.env.LOOPX_PUBLIC_SITE_DIR) {
// Check the assembled publication, including the separately built books.
const checked = new Set();
for (const path of ["", "?lang=zh", "benchmarks/swe-marathon/", "benchmarks/lhtb/", "benchmarks/lhtb/?lang=zh", "benchmarks/deepswe/behavior-discovery/", "benchmarks/deepswe-sol/", "docs/showcases/index.html", "docs/showcases/index.en.html", "docs/guides/personal-workspace-user-guide/", "docs/book/", "docs/book/en/", "blog/", "blog/zh/"]) {
for (const path of ["", "?lang=zh", "benchmarks/swe-marathon/", "benchmarks/lhtb/", "benchmarks/lhtb/?lang=zh", "benchmarks/deepswe/behavior-discovery/", "benchmarks/deepswe-sol/", "lab/time-machine/", "lab/time-machine/?lang=zh", "docs/showcases/index.html", "docs/showcases/index.en.html", "docs/guides/personal-workspace-user-guide/", "docs/book/", "docs/book/en/", "blog/", "blog/zh/"]) {
await page.goto(`${publicOrigin}/loopx/${path}`);
await page.locator("h1").first().waitFor();
const links = await page.locator("a[href]").evaluateAll((links) => links.map((link) => link.href));
Expand Down
6 changes: 4 additions & 2 deletions examples/personal-workspace-browser/research-history.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export const researchHistoryScenario = {
let unavailable = false;
try {
context = await openWorkspacePage(browser, url, {collectCoverage, beforeGoto: async (_, page) => {
await page.route("**/status.json*", async route => {
// The document URL also contains ?statusUrl=/status.json. Only proxy
// the status endpoint; navigation must reach the frontend server.
await page.route(url => url.pathname === "/status.json", async route => {
const response = await fetchWorkspaceBackend(route, owner.base);
assert.equal(response.status(), 200);
await route.fulfill({response});
Expand Down Expand Up @@ -155,7 +157,7 @@ Path(sys.argv[1]).write_text(json.dumps({'goal_id':goal,'display_name':'Syntheti
const realOwner=owner; owner=collisionOwner;
await realOwner.close();
await page.setViewportSize({width:1280,height:900});
await page.goto(new URL(`/chat/?goalId=${goal}&view=overview&statusUrl=%2Fstatus.json`,url).href,{waitUntil:'networkidle'});
await page.goto(new URL(`?goalId=${goal}&view=overview&statusUrl=%2Fstatus.json`,url).href,{waitUntil:'networkidle'});
await history.getByRole('status').filter({hasText:'11 / 11'}).waitFor();
const target = await history.locator(".research-timeline-marker").first().evaluate(button => {
const box = button.getBoundingClientRect(), dot = getComputedStyle(button, "::after");
Expand Down
29 changes: 19 additions & 10 deletions loopx/capabilities/benchmark_toolkit/trajectory_assessment.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ def _result_proof(directory: Path, prepared: dict[str, Any]) -> dict[str, str]:

def _write(path: Path, value: Any) -> None:
temporary = path.with_suffix(".tmp")
temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n")
temporary.write_text(
json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
)
temporary.replace(path)


Expand All @@ -43,14 +45,14 @@ def prepare(
return effect_runtime_result(
"benchmark.trajectory.prepare",
{
"input": json.loads(input_path.read_text()),
"input": json.loads(input_path.read_text(encoding="utf-8")),
"config": {"model": model, "effort": effort, "service_tier": service_tier},
},
)


def read_job(directory: Path) -> dict[str, Any]:
receipt = json.loads((directory / "receipt.json").read_text())
receipt = json.loads((directory / "receipt.json").read_text(encoding="utf-8"))
if receipt.get("schema_version") != "trajectory_assessment_job_v1":
raise ValueError("Unsupported trajectory assessment receipt")
return receipt
Expand Down Expand Up @@ -106,7 +108,9 @@ def execute(
{
**prepared,
"output": json.loads(
(directory / "model-output.json").read_text()
(directory / "model-output.json").read_text(
encoding="utf-8"
)
),
},
)
Expand Down Expand Up @@ -138,7 +142,7 @@ def execute(
(directory / "cancel.request").unlink(missing_ok=True)
_write(directory / "input.json", prepared["input"])
_write(directory / "output-schema.json", prepared["output_schema"])
(directory / "prompt.txt").write_text(prepared["prompt"])
(directory / "prompt.txt").write_text(prepared["prompt"], encoding="utf-8")
config = prepared["config"]
command = [
codex,
Expand Down Expand Up @@ -186,9 +190,9 @@ def execute(
}
_write(directory / "receipt.json", receipt)
with (
(directory / "prompt.txt").open() as prompt,
(directory / "events.jsonl").open("w") as events,
(directory / "stderr.txt").open("w") as errors,
(directory / "prompt.txt").open(encoding="utf-8") as prompt,
(directory / "events.jsonl").open("w", encoding="utf-8") as events,
(directory / "stderr.txt").open("w", encoding="utf-8") as errors,
):
process = subprocess.Popen(
command,
Expand All @@ -197,6 +201,7 @@ def execute(
stdout=events,
stderr=errors,
text=True,
encoding="utf-8",
)
receipt["process_id"] = process.pid
_write(directory / "receipt.json", receipt)
Expand All @@ -220,15 +225,19 @@ def execute(
raise ValueError(
f"Codex exited {process.returncode}; inspect stderr.txt and events.jsonl"
)
output = json.loads((directory / "model-output.json").read_text())
output = json.loads(
(directory / "model-output.json").read_text(encoding="utf-8")
)
result = effect_runtime_result(
"benchmark.trajectory.qualify", {**prepared, "output": output}
)
_write(directory / "assessment.json", result)
receipt.update(
status="succeeded", ok=True, **_result_proof(directory, prepared)
)
for line in (directory / "events.jsonl").read_text().splitlines():
for line in (
(directory / "events.jsonl").read_text(encoding="utf-8").splitlines()
):
try:
event = json.loads(line)
except json.JSONDecodeError:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import hashlib
import json
from pathlib import Path, PurePosixPath
import re

from ...control_plane.content_digest import BARE_SHA256_PATTERN
from ...control_plane.effect_runtime import effect_runtime_result
from ...file_lock import exclusive_file_lock
from .trajectory_assessment import _digest, _write, execute, read_job, request_cancel
Expand All @@ -28,7 +28,7 @@ def __init__(self, reason_code: str):


def _opaque(value: str) -> str:
if not isinstance(value, str) or not re.fullmatch(r"[0-9a-f]{64}", value):
if not isinstance(value, str) or not BARE_SHA256_PATTERN.fullmatch(value):
raise PackUnavailable("invalid_identifier")
return value

Expand Down Expand Up @@ -88,7 +88,7 @@ def _index(owner: Path) -> dict:
path = owner / "packs.json"
if not path.exists():
return {"default_pack_id": None, "pack_ids": []}
value = json.loads(path.read_text())
value = json.loads(path.read_text(encoding="utf-8"))
for pack_id in value["pack_ids"]:
_opaque(pack_id)
if value["default_pack_id"] not in value["pack_ids"]:
Expand All @@ -107,7 +107,7 @@ def _resolve(
path = owner / "packs" / f"{pack_id}.json"
if path.is_symlink():
raise PackUnavailable("invalid_registration")
record = json.loads(path.read_text())
record = json.loads(path.read_text(encoding="utf-8"))
if (
record["owner_goal_id"] != owner_goal_id
or _pack_id(owner_goal_id, record) != pack_id
Expand Down Expand Up @@ -159,7 +159,7 @@ def register_pack(
prepared = effect_runtime_result(
"benchmark.trajectory.prepare",
{
"input": json.loads(input_path.read_text()),
"input": json.loads(input_path.read_text(encoding="utf-8")),
"config": config or DEFAULT_CONFIG,
},
)
Expand All @@ -180,7 +180,7 @@ def register_pack(
index = _index(owner)
path = owner / "packs" / f"{pack_id}.json"
if path.exists():
if json.loads(path.read_text()) != record:
if json.loads(path.read_text(encoding="utf-8")) != record:
raise PackUnavailable("registration_conflict")
else:
_write(path, record)
Expand Down Expand Up @@ -286,7 +286,9 @@ def inspect_pack(
{
**prepared,
"output": json.loads(
(directory / "model-output.json").read_text()
(directory / "model-output.json").read_text(
encoding="utf-8"
)
),
},
)
Expand Down
3 changes: 2 additions & 1 deletion loopx/control_plane/capabilities/trajectory_assessment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** Benchmark-toolkit's observer sidecar; never a solver or native-score authority. */
import {createHash} from 'node:crypto';
import {BARE_SHA256_PATTERN} from '../content_digest.ts';
import {EffectRuntimeRequestError} from '../effect_runtime_errors.ts';

export const TRAJECTORY_METHOD = 'research-episodes-v2';
Expand Down Expand Up @@ -38,7 +39,7 @@ export function parseTrajectoryInput(raw: unknown): TrajectoryInput {
const sources = list(x.sources, 'sources').map(rawSource => {
const r = object(rawSource, 'source');
const hash = str(r.artifact_sha256, 'artifact_sha256');
if (!/^[a-f0-9]{64}$/.test(hash)) throw new EffectRuntimeRequestError('artifact_sha256 must be a sha256 hex digest');
if (!BARE_SHA256_PATTERN.test(hash)) throw new EffectRuntimeRequestError('artifact_sha256 must be a sha256 hex digest');
return {id: str(r.id, 'source.id'), artifact: str(r.artifact, 'artifact'), artifact_sha256: hash,
first_line: integer(r.first_line, 'first_line', 1), text: str(r.text, 'source.text')};
});
Expand Down
1 change: 1 addition & 0 deletions loopx/control_plane/coordination/authority_backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export async function inspectAuthorityBackup(dataRoot: string, row: JsonObject):
if (head === null) throw new Error("authority capture has no head");
const collection = canonicalTodoCollection(head, goalId, true);
return {provider: archive.summary.source_provider, head_digest: archive.summary.projection_sha256,
provider_revision: archive.summary.source_provider_revision, cursor: archive.summary.commits,
history_commits: Number(archive.summary.commits), todo_count: collection.projection.todo_ids.length,
historical_lease_count: collection.leaseIndex?.lease_todo_ids.length ?? 0,
capture_started_at: row.started_at, capture_completed_at: row.completed_at};
Expand Down
1 change: 1 addition & 0 deletions tests/architecture/test_content_digest_single_owner.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
"loopx.capabilities.benchmark_toolkit.integrity",
"loopx.capabilities.benchmark_toolkit.runtime_continuity",
"loopx.capabilities.benchmark_toolkit.study_projection",
"loopx.capabilities.benchmark_toolkit.trajectory_assessment_store",
"loopx.capabilities.content_ops.item_lifecycle",
"loopx.capabilities.deep_research.runtime",
"loopx.capabilities.issue_fix.outcome_projection",
Expand Down
Loading
Loading