From 51b2416c4b68cf65f94ae24fdf3ce5890a5f0387 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:58:46 +0800 Subject: [PATCH 1/4] fix(chat): explain causes and expectations before delivery status Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- loopx/presentation/answer_instruction.py | 5 +++++ .../references/repair-patterns.md | 1 + tests/test_chat_project_context.py | 14 +++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/loopx/presentation/answer_instruction.py b/loopx/presentation/answer_instruction.py index 8023431f1f..2857421ac0 100644 --- a/loopx/presentation/answer_instruction.py +++ b/loopx/presentation/answer_instruction.py @@ -5,6 +5,11 @@ def conversation_answer_instruction() -> str: """Adapt detail to the requested result without cutting or judging content.""" return ( "Answer the user's actual question at a depth proportionate to the work. " + "When asked why a result occurred or whether it is expected, first explain its " + "meaning, the evidence-supported mechanism and what matches or departs from " + "expectations. Separate confirmed causes from hypotheses and unresolved triggers. " + "Measurements support that explanation; a data table, test checklist or PR status " + "does not replace it. " "Follow the user's language and stated reader preferences. Explain unfamiliar " "terms on first use; retain units, comparison direction, counterexamples and " "unknown costs or missing evidence. Separate intermediate checks from the " diff --git a/skills/loopx-self-repair/references/repair-patterns.md b/skills/loopx-self-repair/references/repair-patterns.md index ad4019a72a..4636dbef6c 100644 --- a/skills/loopx-self-repair/references/repair-patterns.md +++ b/skills/loopx-self-repair/references/repair-patterns.md @@ -5,6 +5,7 @@ teaches a reusable control-plane lesson. | Pattern | Symptoms | Evidence To Read | Likely Root | Durable Repair | | --- | --- | --- | --- | --- | +| `answer_question_replaced_by_delivery_status` | A user asks why a result occurred or whether it is expected, but receives measurements, tests or PR progress even though the reply was delivered completely. | The actual question and relevant corrections, current receiver brief, shared answer guidance, and the complete message on its original return route. | The author answered implementation progress instead of the causal question; successful transport or correct counters were mistaken for a useful answer. | Retain the correction in the existing Task and outcome, then repair the existing shared generation guidance: explain meaning, supported mechanism, expectations and the specific unknown before supporting data. Keep hypotheses separate from confirmed causes and preserve the qualified investigation owner. Verify guidance at native callers and read back the actual explanatory answer. Do not add keyword-based answer scoring, a second model rewrite, another display or a duplicate investigation; a prompt test or delivery receipt does not prove model compliance or user adoption. | | `queue_action_type_coercion` | A queued input action such as `["edit"]` is accepted but applies cancellation, or a stored operation accepts a non-string action. | Original JSON types, the typed planner and receipt decoder, real HTTP responses, canonical Turn/transcript/events/Session snapshots and native IO before/after refusal. | Membership checks stringify the action while effect selection and replay compare its original literal value. | Validate the original literal action at the existing owner before branching and when decoding pending or committed receipts. Cover arrays, nested arrays, objects, nulls, numbers, booleans and unknown strings; require rejection without state or native effects, then verify legal edit/cancel, original-operation replay and queue races. Preserve malformed historical evidence rather than coercing or rewriting receipts. | | `collaboration_return_format_projection_gap` | A complete answer that fits the result budget is rejected, or a published versioned return becomes unreadable or cannot replay through the real CLI. | Selected renderer output and Unicode count; persisted reply, compact publication/Inbox observations, original-route delivery and legacy replay at the same boundary. | Validation counted a different renderer before choosing the immutable format, or a compact codec omitted the persisted format marker and silently treated it as legacy. | Apply the unchanged budget to the actual selected format, carry its existing marker through every compact observation, and retain old bytes on replay. Validate BMP/non-BMP exact-limit and one-character-over cases, unsupported versions, real CLI publication/retry, original-audience return and File/SQLite packaged readback. Do not raise the budget, truncate the answer, rewrite historical receipts or add a second model. | | `typed_adapter_validation_scope_mismatch` | A local adapter type check passes while the same CI entry reports a large legacy dependency error set. | Exact source revisions, configured roots, cold same-environment runs, installation/import paths, and original-root plus adapter counterfactuals. | Adding runtime adapters to a kernel root list expands its import closure; an extra source path can mask that traversal locally. | Preserve the existing kernel gate and its import behavior. Make the additive adapter boundary explicit, retain imported-code debt as unqualified, and prove strict root checking with original diagnostics and core/adapter mutations in an unmasked source installation. Do not globally disable import checks, remove original roots or rewrite historical failures as passes. | diff --git a/tests/test_chat_project_context.py b/tests/test_chat_project_context.py index b4a3d83e50..b5fbb3121b 100644 --- a/tests/test_chat_project_context.py +++ b/tests/test_chat_project_context.py @@ -13,8 +13,12 @@ def test_source_reference_contract_reaches_native_project_turn_without_expanding status, opened = request("/api/chat/sessions", {"context_kind": "project", "project_ref": ref}) assert status == 201 session_id = opened["session_id"] + question = ( + "Why did the export take longer even though its transfer shrank? " + "Is that expected? Cite the source and identify what is still unknown." + ) status, accepted = request(f"/api/chat/sessions/{session_id}/turns", { - "message": "Explain this project and cite the source.", "client_turn_id": "source-reference", + "message": question, "client_turn_id": "source-reference", }) assert status == 202 assert runtime.wait_for_turn(session_id=session_id, turn_id=accepted["turn_id"], timeout_sec=10)["status"] == "completed" @@ -22,6 +26,11 @@ def test_source_reference_contract_reaches_native_project_turn_without_expanding start = next(row["params"] for row in packets if row.get("method") == "thread/start") turn = next(row["params"] for row in packets if row.get("method") == "turn/start") text = turn["input"][0]["text"] + assert question in text + assert "why a result occurred or whether it is expected" in text + assert "evidence-supported mechanism and what matches or departs from expectations" in text + assert "Separate confirmed causes from hypotheses and unresolved triggers" in text + assert "a data table, test checklist or PR status does not replace it" in text assert "repository-relative Markdown destinations are not source URLs in chat" in text assert "canonical repository and the exact revision actually read" in text assert "private, ignored, unpublished or modified local content" in text @@ -51,5 +60,8 @@ def test_steward_and_collaboration_preserve_the_same_receiver_source_boundaries( assert "plain file/section reference" in steward assert "never upload or publish content merely to make a citation clickable" in steward assert steward in MANAGER_AGENT_OBJECTIVE + assert "evidence-supported mechanism and what matches or departs from expectations" in steward + assert "Separate confirmed causes from hypotheses and unresolved triggers" in steward + assert "a data table, test checklist or PR status does not replace it" in collaboration assert "Preparation, saving, delivery, adoption, execution and acceptance are distinct" in steward assert "Preserve the original author, audience and facts" in steward From 84c50ef960e8c3b00d19722ff4fbded4617c4b59 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:58:46 +0800 Subject: [PATCH 2/4] test(chat): declare imported fixtures as explicit reexports Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- tests/test_chat_codex_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_chat_codex_history.py b/tests/test_chat_codex_history.py index 263c4cdab8..5e1ee87c3b 100644 --- a/tests/test_chat_codex_history.py +++ b/tests/test_chat_codex_history.py @@ -7,8 +7,8 @@ from loopx import chat_agent, chat_runtime from loopx.capabilities.native_chat.codex_adapter import CodexAppServerAdapter -from test_chat_manager_model_adoption import conversation, configure # noqa: F401 -from test_native_steward_private import steward # noqa: F401 +from test_chat_manager_model_adoption import conversation as conversation, configure +from test_native_steward_private import steward as steward def test_source_revocation_during_history_compaction_preserves_original_audience(steward, monkeypatch): From d48604e91b9168319cfa19ab9185792b0aa1dca9 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Fri, 6 Nov 2026 18:14:01 +0800 Subject: [PATCH 3/4] test(research): bind retained evidence checks to native identity Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../personal-workspace-browser/research-history.mjs | 5 +++-- .../research-native-reading.mjs | 12 +++++++----- .../loopx-self-repair/references/repair-patterns.md | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/personal-workspace-browser/research-history.mjs b/examples/personal-workspace-browser/research-history.mjs index a5f5edf695..21981ce0f3 100644 --- a/examples/personal-workspace-browser/research-history.mjs +++ b/examples/personal-workspace-browser/research-history.mjs @@ -1519,10 +1519,11 @@ Path(sys.argv[1]).write_text(json.dumps({'goal_id':goal,'display_name':'Syntheti await progress.locator(`.research-progress-scope[data-pack-id="${"a".repeat(64)}"]`).waitFor(); const basis = progress.locator('.research-progress-basis'); assert.equal(await basis.evaluate(e => e.open), false); - const improvement = progress.getByRole('region', {name: '已采用成果增量', exact: true}); + const improvement = progress.getByRole('region', {name: '声明的保留增量', exact: true}); assert.equal(await improvement.locator('[data-retained-improvement]').textContent(), '+4'); await improvement.getByRole('button', {name: '成果增量说明', exact: true}).focus(); await improvement.getByRole('tooltip').waitFor({state: 'visible'}); + assert.match(await improvement.getByRole('tooltip').innerText(), /不证明实际文件采用/, 'Source-declared retention does not establish independent artifact adoption'); await page.keyboard.press('Escape'); await improvement.locator('summary').click(); await improvement.getByRole('button', {name: '查看原文', exact: true}).click(); @@ -1734,7 +1735,7 @@ Path(sys.argv[1]).write_text(json.dumps({'goal_id':goal,'display_name':'Syntheti assert.equal(await nativeResults.locator('[data-native-point="retained"] strong').textContent(), '3', 'Native facts do not require an analyzer result'); assert.match(await nativeResults.innerText(), /部分窗口 · 起点未知/); assert.match(await nativeResults.locator('[data-native-point="initial"]').innerText(), /未知/); - const nativeCitation = nativeResults.getByRole('button', {name: /原生保留结果.*查看原始证据/}); + const nativeCitation = nativeResults.locator('[data-native-point="retained"]').getByRole('button'); await nativeCitation.focus(); await nativeCitation.press('Enter'); await citedOriginal.waitFor(); await citedOriginal.press('Escape'); diff --git a/examples/personal-workspace-browser/research-native-reading.mjs b/examples/personal-workspace-browser/research-native-reading.mjs index ab0168c969..b119e91d4c 100644 --- a/examples/personal-workspace-browser/research-native-reading.mjs +++ b/examples/personal-workspace-browser/research-native-reading.mjs @@ -170,18 +170,20 @@ export const researchNativeReadingScenario = { await details.locator(":scope > summary").tap(); if (c.id === "invalid-first") await page.screenshot({path: resolve(outputDir, "research-native-unknown-phone.png"), animations: "disabled"}); if (c.id === "lower-negative") { - const retained = native.getByRole("button", {name: /原生保留结果.*查看原始证据/}); + // The native point identifies the action independently of translated + // copy distinguishing source declarations from verified adoption. + const retained = native.locator('[data-native-point="retained"]').getByRole("button"); await retained.focus(); await retained.press("Enter"); const citation = page.getByRole("region", {name: "引用原文", exact: true}); await citation.waitFor(); await citation.getByText(/Synthetic observation 0: value -1/).waitFor(); assert.match(await citation.innerText(), /Synthetic observation 0: value -1/); await citation.press("Escape"); - await page.waitForFunction(() => document.activeElement?.getAttribute("aria-label")?.startsWith("原生保留结果:")); + await page.waitForFunction(() => document.activeElement?.matches('[data-native-point="retained"] button')); await page.screenshot({path: resolve(outputDir, "research-native-negative-phone.png"), animations: "disabled"}); } if (c.source) { - const retained = native.getByRole("button", {name: /原生保留结果.*查看原始证据/}); + const retained = native.locator('[data-native-point="retained"]').getByRole("button"); await retained.focus(); await retained.press("Enter"); const citation = page.getByRole("region", {name: "引用原文", exact: true}); await citation.waitFor(); @@ -230,7 +232,7 @@ export const researchNativeReadingScenario = { } } await citation.press("Escape"); - await page.waitForFunction(() => document.activeElement?.getAttribute("aria-label")?.startsWith("原生保留结果:")); + await page.waitForFunction(() => document.activeElement?.matches('[data-native-point="retained"] button')); } if (c.unit) { await page.evaluate(() => document.documentElement.style.fontSize = "200%"); @@ -252,7 +254,7 @@ export const researchNativeReadingScenario = { await selector.selectOption({label: "tool-pending-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(); + await page.getByRole("region", {name: "Native measurements", exact: true}).locator('[data-native-point="retained"]').getByRole("button").click(); const originalPanel = page.getByRole("region", {name: "Cited original", exact: true}); await originalPanel.getByText("Exit code 17", {exact: true}).waitFor(); await originalPanel.getByText("No exit code yet", {exact: true}).waitFor(); diff --git a/skills/loopx-self-repair/references/repair-patterns.md b/skills/loopx-self-repair/references/repair-patterns.md index 4636dbef6c..0537712c7b 100644 --- a/skills/loopx-self-repair/references/repair-patterns.md +++ b/skills/loopx-self-repair/references/repair-patterns.md @@ -270,6 +270,7 @@ teaches a reusable control-plane lesson. | `commit_hygiene_drift` | Broad commit includes temporary smokes, raw logs, local state, or unrelated docs. | `git status`, `git diff --stat`, `git ls-files --others --exclude-standard`, AGENTS.md. | Worktree was staged by chronology rather than reviewer logic. | Use explicit pathspecs, split commits, keep only durable smokes, and update AGENTS/skill if the failure mode recurs. | | `migration_terminal_receipt_replay_gap` | A migrated transaction succeeds once, then an idempotent legacy retry is rejected as receipt corruption or times out on a lock that the first response lost. | Versioned operation receipt, persisted state, lock owner/token, exact caller retry identity, pre-migration replay behavior, and focused native plus compatibility tests. | The new owner modeled only held/committed receipts and treated a closed no-op receipt as incomplete, or generated retry identity inside the callee after the caller's retry boundary. | Model terminal no-op receipts separately from held authority proofs, replay them without retired private tokens, and generate one stable operation id outside any transport retry while minting a new id for independent calls. Cover direct native replay and the compatibility adapter's response-loss boundary. | | `typed_kernel_import_surface_drift` | Focused tests and premerge canaries pass, but CI mypy starts reporting unrelated errors in a previously unchecked module after a small typed-kernel change. | Exact mypy entry files, the new import edge, base/head module diffs, and whether the imported module owns the new behavior or is only a type source. | A checked kernel dependency imported a broad runtime or Goal-routing owner for one enum, provider decision or filesystem helper, expanding strict mypy traversal beyond the owning contract. | Keep caller policy with its caller and shared filesystem helpers in a dependency-light runtime module. Validate behavior parity and the exact CI mypy command after changing kernel dependencies, using the same source installation and import environment; an extra PYTHONPATH can hide the imported-module failure. | +| `browser_smoke_presentation_copy_drift` | Browser checks time out finding retained outcomes after wording changes to distinguish source declarations from verified adoption. | Same immutable base and scenario, production bundle, accessible names, native point identities, exact values, citation and focus return, plus affected pipeline entrypoints. | Smoke locators encode superseded presentation copy rather than result identity; related region names were not aligned with the corrected contract. | Keep provenance-accurate UI. Bind existing result keys with role locators, preserve accessible meaning and independent numeric, source, failure and recovery oracles, then run the same workload before and after against its real owner. Do not restore overclaims, increase timeouts or delete assertions. | | `packaged_surface_completion_illusion` | A development route or API-mocked browser smoke passes while the packaged product still serves an older UI, loses local history after refresh, hides every healthy Agent, or records important contradictions only as non-failing observations. | packaged entry title and first viewport, emitted bundle identity, real loopback capabilities, service-restart history, Last-Event-ID replay, mobile navigation close behavior, and the acceptance report's untested/observation fields. | UI completion was inferred from component tests or a Vite route; the release asset, launcher environment, durable local session path, and hard browser assertions evolved separately. | Make packaged-route parity a blocking acceptance contract. Build and launch the real bundle with a controlled executable path, convert semantic observations into assertions, persist status-only and Agent-backed messages independently, test refresh plus restart recovery, and require every design criterion to be PASS or explicitly gated before claiming the surface complete. | | `dashboard_status_source_generation_gap` | Switching between local and SSH sources leaves the old Goal list visible, or the selector and route oscillate between two sources after a slow tunnel ensure or status response. | User selection order, tunnel-ensure start/finish order, status fetch start/finish order, requested and loaded URLs, projection revision, route commits, and the rendered Goal identity. | Request freshness starts only after SSH preparation, a foreground request captures its revision before advancing it, or an older route commit clears a newer pending selection. | Fence the complete selection transaction—from user intent through tunnel ensure, fetch, payload commit, and route commit—with one monotonic selection generation plus a projection revision. Let only the current generation clear pending state; reject background responses whose target is no longer the committed source. Cover delayed A/B fetches and delayed SSH-then-local selection with a focused browser smoke that asserts payload, selector, and route remain aligned. | | `docs_surface_sprawl` | Root docs become hard for contributors to navigate; research/drafts/history compete with stable contracts. | `docs/README.md`, root docs count, docs governance smoke. | Documentation lacks audience and lifecycle ownership. | Move archive/outreach/research/reference material into indexed subdirs and keep new docs linked from an index. | From ea941dd8a3cfe96fba5bb5302cb3bf0b01efed02 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Fri, 6 Nov 2026 18:24:58 +0800 Subject: [PATCH 4/4] test(research): qualify unknown retention by content and hierarchy Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../research-native-reading.mjs | 25 ++++++++++++++++--- .../references/repair-patterns.md | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/examples/personal-workspace-browser/research-native-reading.mjs b/examples/personal-workspace-browser/research-native-reading.mjs index b119e91d4c..fc17c9df2b 100644 --- a/examples/personal-workspace-browser/research-native-reading.mjs +++ b/examples/personal-workspace-browser/research-native-reading.mjs @@ -143,11 +143,30 @@ export const researchNativeReadingScenario = { assert.match(await native.locator(".research-native-scope").innerText(), new RegExp(`${c.values.filter(v => v !== null).length} / ${c.values.length}.*有效结果`)); if (c.partial) assert.match(await native.innerText(), /部分窗口 · 起点未知/); if (c.id === "invalid-first") { - const best = await native.locator('[data-native-point="best_valid"]').boundingBox(); - const retained = await native.locator('[data-native-point="retained"]').boundingBox(); - assert(best.width > retained.width + 8, "An unknown retained result must not reserve an equal primary column beside the best valid measurement"); + const measureUnknownRetention = () => native.evaluate(section => { + const retained = section.querySelector('[data-native-point="retained"]'); + const unknown = retained.querySelector(".research-native-unknown"); + const label = document.createRange(); + label.selectNodeContents(retained.querySelector("dt")); + return { + unusedWidth: retained.getBoundingClientRect().width - Math.max(label.getBoundingClientRect().width, unknown.getBoundingClientRect().width), + bestFont: parseFloat(getComputedStyle(section.querySelector('[data-native-point="best_valid"] strong')).fontSize), + unknownFont: parseFloat(getComputedStyle(unknown).fontSize), + }; + }); + const actual = await measureUnknownRetention(); await native.evaluate(e => e.scrollIntoView({block: "start"})); await page.screenshot({path: resolve(outputDir, "research-native-unknown-desktop.png"), animations: "disabled"}); + assert(actual.bestFont > actual.unknownFont, "The best valid measurement remains primary when retention is unknown"); + assert(actual.unusedWidth <= 2, `Unknown retention must not reserve an empty primary column: ${JSON.stringify(actual)}`); + const reading = native.locator(".research-native-reading"); + const originalStyle = await reading.getAttribute("style"); + try { + await reading.evaluate(e => {e.style.display = "grid"; e.style.gridTemplateColumns = "repeat(4, minmax(0, 1fr))";}); + assert((await measureUnknownRetention()).unusedWidth > 2, "The same oracle must reject equal-width empty columns"); + } finally { + await reading.evaluate((e, style) => style === null ? e.removeAttribute("style") : e.setAttribute("style", style), originalStyle); + } } await page.setViewportSize({width: 390, height: 844}); const details = native.locator(".research-native-basis"); diff --git a/skills/loopx-self-repair/references/repair-patterns.md b/skills/loopx-self-repair/references/repair-patterns.md index 0537712c7b..bc95573fe9 100644 --- a/skills/loopx-self-repair/references/repair-patterns.md +++ b/skills/loopx-self-repair/references/repair-patterns.md @@ -270,7 +270,7 @@ teaches a reusable control-plane lesson. | `commit_hygiene_drift` | Broad commit includes temporary smokes, raw logs, local state, or unrelated docs. | `git status`, `git diff --stat`, `git ls-files --others --exclude-standard`, AGENTS.md. | Worktree was staged by chronology rather than reviewer logic. | Use explicit pathspecs, split commits, keep only durable smokes, and update AGENTS/skill if the failure mode recurs. | | `migration_terminal_receipt_replay_gap` | A migrated transaction succeeds once, then an idempotent legacy retry is rejected as receipt corruption or times out on a lock that the first response lost. | Versioned operation receipt, persisted state, lock owner/token, exact caller retry identity, pre-migration replay behavior, and focused native plus compatibility tests. | The new owner modeled only held/committed receipts and treated a closed no-op receipt as incomplete, or generated retry identity inside the callee after the caller's retry boundary. | Model terminal no-op receipts separately from held authority proofs, replay them without retired private tokens, and generate one stable operation id outside any transport retry while minting a new id for independent calls. Cover direct native replay and the compatibility adapter's response-loss boundary. | | `typed_kernel_import_surface_drift` | Focused tests and premerge canaries pass, but CI mypy starts reporting unrelated errors in a previously unchecked module after a small typed-kernel change. | Exact mypy entry files, the new import edge, base/head module diffs, and whether the imported module owns the new behavior or is only a type source. | A checked kernel dependency imported a broad runtime or Goal-routing owner for one enum, provider decision or filesystem helper, expanding strict mypy traversal beyond the owning contract. | Keep caller policy with its caller and shared filesystem helpers in a dependency-light runtime module. Validate behavior parity and the exact CI mypy command after changing kernel dependencies, using the same source installation and import environment; an extra PYTHONPATH can hide the imported-module failure. | -| `browser_smoke_presentation_copy_drift` | Browser checks time out finding retained outcomes after wording changes to distinguish source declarations from verified adoption. | Same immutable base and scenario, production bundle, accessible names, native point identities, exact values, citation and focus return, plus affected pipeline entrypoints. | Smoke locators encode superseded presentation copy rather than result identity; related region names were not aligned with the corrected contract. | Keep provenance-accurate UI. Bind existing result keys with role locators, preserve accessible meaning and independent numeric, source, failure and recovery oracles, then run the same workload before and after against its real owner. Do not restore overclaims, increase timeouts or delete assertions. | +| `browser_smoke_presentation_copy_drift` | Browser checks time out after retention wording changes, or mistake a longer source label in a content-sized layout for an empty primary column. | Same immutable base and scenario, production bundle, accessible names, native point identities, exact values, citation and focus return, plus affected pipeline entrypoints. | Smoke locators freeze superseded copy or box-size relationships rather than result identity and information hierarchy. | Keep provenance-accurate UI. Bind existing result keys with role locators, preserve accessible meaning and independent numeric, source, failure and recovery oracles, then run the same workload before and after against its real owner. A replacement layout oracle needs a failing equal-column control, not a weaker threshold. Do not restore overclaims, increase timeouts or delete assertions. | | `packaged_surface_completion_illusion` | A development route or API-mocked browser smoke passes while the packaged product still serves an older UI, loses local history after refresh, hides every healthy Agent, or records important contradictions only as non-failing observations. | packaged entry title and first viewport, emitted bundle identity, real loopback capabilities, service-restart history, Last-Event-ID replay, mobile navigation close behavior, and the acceptance report's untested/observation fields. | UI completion was inferred from component tests or a Vite route; the release asset, launcher environment, durable local session path, and hard browser assertions evolved separately. | Make packaged-route parity a blocking acceptance contract. Build and launch the real bundle with a controlled executable path, convert semantic observations into assertions, persist status-only and Agent-backed messages independently, test refresh plus restart recovery, and require every design criterion to be PASS or explicitly gated before claiming the surface complete. | | `dashboard_status_source_generation_gap` | Switching between local and SSH sources leaves the old Goal list visible, or the selector and route oscillate between two sources after a slow tunnel ensure or status response. | User selection order, tunnel-ensure start/finish order, status fetch start/finish order, requested and loaded URLs, projection revision, route commits, and the rendered Goal identity. | Request freshness starts only after SSH preparation, a foreground request captures its revision before advancing it, or an older route commit clears a newer pending selection. | Fence the complete selection transaction—from user intent through tunnel ensure, fetch, payload commit, and route commit—with one monotonic selection generation plus a projection revision. Let only the current generation clear pending state; reject background responses whose target is no longer the committed source. Cover delayed A/B fetches and delayed SSH-then-local selection with a focused browser smoke that asserts payload, selector, and route remain aligned. | | `docs_surface_sprawl` | Root docs become hard for contributors to navigate; research/drafts/history compete with stable contracts. | `docs/README.md`, root docs count, docs governance smoke. | Documentation lacks audience and lifecycle ownership. | Move archive/outreach/research/reference material into indexed subdirs and keep new docs linked from an index. |