Merge current master and preserve Claude default behavior
Co-Authored-By: Paperclip <noreply@paperclip.ing>
No files matched your search
@@ -189,6 +189,26 @@ For Paperclip-assigned work, attach important proof through
|
||||
the evidence directory or issue, not in the map. The generated
|
||||
`maps/generated/coverage.json` lists page references, not live test health.
|
||||
|
||||
### Preserve failures through acceptance
|
||||
|
||||
A successful retry establishes that a later attempt succeeded. Retain earlier
|
||||
material failures in the verification record, including the failed requirement,
|
||||
relevant logs, and what changed before the retry. Use a focused comparison or
|
||||
reproduction to determine whether the change caused the failure. When useful,
|
||||
check the unchanged baseline under comparable conditions. If the cause remains
|
||||
unknown, state that uncertainty; do not infer an infrastructure fault from a
|
||||
passing retry alone.
|
||||
|
||||
Report recovery and resolution separately. A later passing journey can show
|
||||
recovery without explaining the earlier failure. Do not describe acceptance as
|
||||
complete while a relevant requirement remains unverified. Record the remaining
|
||||
gap and the next discriminating check instead of repeating retries until green.
|
||||
|
||||
Apply this to failures that bear on the changed behavior or requested acceptance
|
||||
criteria. Independently established infrastructure failures can be tracked
|
||||
separately, with supporting evidence and any checks they prevented still marked
|
||||
blocked or not run. This does not require investigating every unrelated failure.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Close the browser context you created (the smoke does this in `finally`). Leave
|
||||
|
||||
@@ -29,7 +29,7 @@ test("routing exports both placement and stack scope to callers", () => {
|
||||
assert.match(jobs, /^ {6}full_ci: \$\{\{ steps\.scope\.outputs\.full_ci \}\}$/m);
|
||||
});
|
||||
|
||||
const githubStub = "#!/usr/bin/env bash\nset -Eeuo pipefail\n\nendpoint=\"${*: -1}\"\ncase \"$endpoint\" in\n */pulls/*)\n jq -cn \\\n --argjson author_id \"${TEST_PR_AUTHOR_ID:-34892728}\" \\\n --argjson repository_id \"${TEST_REPOSITORY_ID:-1170821064}\" \\\n --arg base_ref \"${TEST_BASE_REF:-master}\" \\\n --arg base_sha \"${TEST_PR_BASE_SHA:-$EVENT_BASE_SHA}\" \\\n --arg head_sha \"${TEST_PR_HEAD_SHA:-1111111111111111111111111111111111111111}\" \\\n --arg merge_sha \"${TEST_PR_MERGE_SHA:-4444444444444444444444444444444444444444}\" '{\n state: \"open\",\n user: {id: $author_id},\n base: {repo: {id: $repository_id}, ref: $base_ref, sha: $base_sha},\n head: {sha: $head_sha},\n merge_commit_sha: $merge_sha\n }'\n ;;\n */git/ref/heads/*)\n jq -cn \\\n --arg sha \"${TEST_LIVE_BASE_REF_SHA:-$EVENT_BASE_SHA}\" '{object: {sha: $sha}}'\n ;;\n */compare/*)\n jq -cn \\\n --arg merge_base_sha \"${TEST_BASE_COMPARISON_MERGE_BASE_SHA:-$EVENT_BASE_SHA}\" \\\n --arg status \"${TEST_BASE_COMPARISON_STATUS:-ahead}\" '{\n merge_base_commit: {sha: $merge_base_sha},\n status: $status\n }'\n ;;\n */git/commits/*)\n requested_sha=\"${endpoint##*/}\"\n live_merge_sha=\"${TEST_PR_MERGE_SHA:-4444444444444444444444444444444444444444}\"\n synthetic_base_merge_sha=\"${TEST_SYNTHETIC_BASE_MERGE_SHA:-}\"\n if [[ \"$requested_sha\" == \"$EVENT_MERGE_SHA\" ]]; then\n tree_sha=\"${TEST_EVENT_MERGE_TREE_SHA:-5555555555555555555555555555555555555555}\"\n base_sha=\"${TEST_EVENT_MERGE_BASE_SHA:-$EVENT_BASE_SHA}\"\n head_sha=\"${TEST_EVENT_MERGE_HEAD_SHA:-$EVENT_HEAD_SHA}\"\n elif [[ \"$requested_sha\" == \"$live_merge_sha\" ]]; then\n tree_sha=\"${TEST_LIVE_MERGE_TREE_SHA:-5555555555555555555555555555555555555555}\"\n base_sha=\"${TEST_LIVE_MERGE_BASE_SHA:-$EVENT_BASE_SHA}\"\n head_sha=\"${TEST_LIVE_MERGE_HEAD_SHA:-$EVENT_HEAD_SHA}\"\n elif [[ -n \"$synthetic_base_merge_sha\" && \"$requested_sha\" == \"$synthetic_base_merge_sha\" ]]; then\n tree_sha=\"${TEST_SYNTHETIC_BASE_MERGE_TREE_SHA:-8888888888888888888888888888888888888888}\"\n base_sha=\"${TEST_SYNTHETIC_MASTER_PARENT_SHA:-9999999999999999999999999999999999999999}\"\n head_sha=\"${TEST_SYNTHETIC_BASE_REF_PARENT_SHA:-${TEST_LIVE_BASE_REF_SHA:-$EVENT_BASE_SHA}}\"\n else\n echo \"unexpected merge commit: $requested_sha\" >&2\n exit 1\n fi\n jq -cn \\\n --arg sha \"$requested_sha\" \\\n --arg tree_sha \"$tree_sha\" \\\n --arg base_sha \"$base_sha\" \\\n --arg head_sha \"$head_sha\" '{\n sha: $sha,\n tree: {sha: $tree_sha},\n parents: [{sha: $base_sha}, {sha: $head_sha}]\n }'\n ;;\n */actions/runs/*)\n jq -cn \\\n --argjson actor_id \"${TEST_TRIGGERING_ACTOR_ID:-34892728}\" \\\n --argjson repository_id \"${TEST_REPOSITORY_ID:-1170821064}\" \\\n --arg event_name \"${TEST_EVENT_NAME:-pull_request}\" '{\n triggering_actor: {id: $actor_id},\n repository: {id: $repository_id},\n event: $event_name\n }'\n ;;\n *)\n echo \"unexpected gh endpoint: $endpoint\" >&2\n exit 1\n ;;\nesac";
|
||||
const githubStub = "#!/usr/bin/env bash\nset -Eeuo pipefail\n\nendpoint=\"${*: -1}\"\ncase \"$endpoint\" in\n */pulls/*)\n jq -cn \\\n --argjson author_id \"${TEST_PR_AUTHOR_ID:-34892728}\" \\\n --argjson repository_id \"${TEST_REPOSITORY_ID:-1170821064}\" \\\n --arg base_ref \"${TEST_BASE_REF:-master}\" \\\n --arg base_sha \"${TEST_PR_BASE_SHA:-$EVENT_BASE_SHA}\" \\\n --arg head_sha \"${TEST_PR_HEAD_SHA:-1111111111111111111111111111111111111111}\" \\\n --arg merge_sha \"${TEST_PR_MERGE_SHA:-4444444444444444444444444444444444444444}\" '{\n state: \"open\",\n user: {id: $author_id},\n base: {repo: {id: $repository_id}, ref: $base_ref, sha: $base_sha},\n head: {sha: $head_sha},\n merge_commit_sha: $merge_sha\n }'\n ;;\n */git/ref/heads/*)\n jq -cn \\\n --arg sha \"${TEST_LIVE_BASE_REF_SHA:-$EVENT_BASE_SHA}\" '{object: {sha: $sha}}'\n ;;\n */compare/*)\n [[ -z \"${TEST_COMPARE_FAIL:-}\" ]] || exit 1\n default_files='[{\"filename\":\"server/src/index.ts\"}]'\n jq -cn \\\n --arg merge_base_sha \"${TEST_BASE_COMPARISON_MERGE_BASE_SHA:-$EVENT_BASE_SHA}\" \\\n --arg status \"${TEST_BASE_COMPARISON_STATUS:-ahead}\" \\\n --argjson files \"${TEST_COMPARE_FILES:-$default_files}\" '{\n merge_base_commit: {sha: $merge_base_sha},\n status: $status,\n files: $files\n }'\n ;;\n */git/commits/*)\n requested_sha=\"${endpoint##*/}\"\n live_merge_sha=\"${TEST_PR_MERGE_SHA:-4444444444444444444444444444444444444444}\"\n synthetic_base_merge_sha=\"${TEST_SYNTHETIC_BASE_MERGE_SHA:-}\"\n if [[ \"$requested_sha\" == \"$EVENT_MERGE_SHA\" ]]; then\n tree_sha=\"${TEST_EVENT_MERGE_TREE_SHA:-5555555555555555555555555555555555555555}\"\n base_sha=\"${TEST_EVENT_MERGE_BASE_SHA:-$EVENT_BASE_SHA}\"\n head_sha=\"${TEST_EVENT_MERGE_HEAD_SHA:-$EVENT_HEAD_SHA}\"\n elif [[ \"$requested_sha\" == \"$live_merge_sha\" ]]; then\n tree_sha=\"${TEST_LIVE_MERGE_TREE_SHA:-5555555555555555555555555555555555555555}\"\n base_sha=\"${TEST_LIVE_MERGE_BASE_SHA:-$EVENT_BASE_SHA}\"\n head_sha=\"${TEST_LIVE_MERGE_HEAD_SHA:-$EVENT_HEAD_SHA}\"\n elif [[ -n \"$synthetic_base_merge_sha\" && \"$requested_sha\" == \"$synthetic_base_merge_sha\" ]]; then\n tree_sha=\"${TEST_SYNTHETIC_BASE_MERGE_TREE_SHA:-8888888888888888888888888888888888888888}\"\n base_sha=\"${TEST_SYNTHETIC_MASTER_PARENT_SHA:-9999999999999999999999999999999999999999}\"\n head_sha=\"${TEST_SYNTHETIC_BASE_REF_PARENT_SHA:-${TEST_LIVE_BASE_REF_SHA:-$EVENT_BASE_SHA}}\"\n else\n echo \"unexpected merge commit: $requested_sha\" >&2\n exit 1\n fi\n jq -cn \\\n --arg sha \"$requested_sha\" \\\n --arg tree_sha \"$tree_sha\" \\\n --arg base_sha \"$base_sha\" \\\n --arg head_sha \"$head_sha\" '{\n sha: $sha,\n tree: {sha: $tree_sha},\n parents: [{sha: $base_sha}, {sha: $head_sha}]\n }'\n ;;\n */actions/runs/*)\n jq -cn \\\n --argjson actor_id \"${TEST_TRIGGERING_ACTOR_ID:-34892728}\" \\\n --argjson repository_id \"${TEST_REPOSITORY_ID:-1170821064}\" \\\n --arg event_name \"${TEST_EVENT_NAME:-pull_request}\" '{\n triggering_actor: {id: $actor_id},\n repository: {id: $repository_id},\n event: $event_name\n }'\n ;;\n *)\n echo \"unexpected gh endpoint: $endpoint\" >&2\n exit 1\n ;;\nesac";
|
||||
|
||||
function stepScript(name) {
|
||||
const start = workflow.indexOf(` - name: ${name}\n`);
|
||||
@@ -137,12 +137,82 @@ const scopeCases = [
|
||||
|
||||
for (const [name, stack, base, expected] of scopeCases) {
|
||||
test(`scope decision: ${name}`, () => {
|
||||
assert.equal(executeStep("Select stacked PR CI scope", {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: JSON.stringify(stack), PR_BASE_REF: base,
|
||||
}).full_ci, expected);
|
||||
});
|
||||
}
|
||||
|
||||
const files = (...paths) => JSON.stringify(paths.map((filename) => ({ filename })));
|
||||
const manyReleaseNotes = JSON.stringify(Array.from({ length: 300 }, (_, index) => ({
|
||||
filename: `releases/beta/v2026.1012.${index}-beta.0.md`,
|
||||
})));
|
||||
|
||||
const pathScopeCases = [
|
||||
["stable release notes", { TEST_COMPARE_FILES: files("releases/v2026.1015.0.md") }, "false"],
|
||||
["beta release notes draft", { TEST_COMPARE_FILES: files("releases/beta/v2026.1012.1-beta.0.md") }, "false"],
|
||||
["stable and beta notes", { TEST_COMPARE_FILES: files("releases/v2026.1015.0.md", "releases/beta/v2026.1012.1-beta.0.md") }, "false"],
|
||||
["removed release notes", { TEST_COMPARE_FILES: JSON.stringify([{ filename: "releases/beta/v2026.1006.0-beta.0.md", status: "removed" }]) }, "false"],
|
||||
["release notes and code", { TEST_COMPARE_FILES: files("releases/v2026.1015.0.md", "server/src/index.ts") }, "true"],
|
||||
["code renamed into releases", { TEST_COMPARE_FILES: JSON.stringify([{ filename: "releases/v2026.1015.0.md", previous_filename: "scripts/release.sh" }]) }, "true"],
|
||||
["non-markdown file in releases", { TEST_COMPARE_FILES: files("releases/beta/v2026.1012.1-beta.0.json") }, "true"],
|
||||
["nested releases directory", { TEST_COMPARE_FILES: files("releases/beta/drafts/v2026.1012.1-beta.0.md") }, "true"],
|
||||
["markdown outside releases", { TEST_COMPARE_FILES: files("doc/RELEASING.md") }, "true"],
|
||||
["file entry without a name", { TEST_COMPARE_FILES: JSON.stringify([{ status: "modified" }]) }, "true"],
|
||||
["empty diff", { TEST_COMPARE_FILES: "[]" }, "true"],
|
||||
["possibly truncated file list", { TEST_COMPARE_FILES: manyReleaseNotes }, "true"],
|
||||
["compare lookup failure", { TEST_COMPARE_FAIL: "1", TEST_COMPARE_FILES: files("releases/v2026.1015.0.md") }, "true"],
|
||||
["non-PR event", { EVENT_NAME: "workflow_dispatch", TEST_COMPARE_FILES: files("releases/v2026.1015.0.md") }, "true"],
|
||||
["malformed head SHA", { EVENT_HEAD_SHA: "invalid", TEST_COMPARE_FILES: files("releases/v2026.1015.0.md") }, "true"],
|
||||
];
|
||||
|
||||
for (const [name, env, expected] of pathScopeCases) {
|
||||
test(`path scope decision: ${name}`, () => {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: "null", PR_BASE_REF: "master", ...env,
|
||||
}).full_ci, expected);
|
||||
});
|
||||
}
|
||||
|
||||
test("path scope keeps full CI for the top PR in a stack that touches code", () => {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: JSON.stringify({ position: 11, size: 11, base: { ref: "master" } }),
|
||||
PR_BASE_REF: "stack-10",
|
||||
}).full_ci, "true");
|
||||
});
|
||||
|
||||
test("path scope keeps full CI for release notes on top of code-changing stacked PRs", () => {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: JSON.stringify({ position: 11, size: 11, base: { ref: "master" } }),
|
||||
PR_BASE_REF: "stack-10",
|
||||
TEST_COMPARE_FILES: files("releases/v2026.1015.0.md"),
|
||||
}).full_ci, "true");
|
||||
});
|
||||
|
||||
test("path scope keeps full CI for release notes with malformed stack metadata", () => {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: JSON.stringify({ position: 0, size: 2, base: { ref: "master" } }),
|
||||
PR_BASE_REF: "master",
|
||||
TEST_COMPARE_FILES: files("releases/v2026.1015.0.md"),
|
||||
}).full_ci, "true");
|
||||
});
|
||||
|
||||
test("path scope skips full CI for the lowest PR in a stack that only touches release notes", () => {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: JSON.stringify({ position: 1, size: 3, base: { ref: "master" } }),
|
||||
PR_BASE_REF: "master",
|
||||
TEST_COMPARE_FILES: files("releases/v2026.1015.0.md"),
|
||||
}).full_ci, "false");
|
||||
});
|
||||
|
||||
test("path scope skips full CI for a single-PR stack that only touches release notes", () => {
|
||||
assert.equal(executeStep("Select PR CI scope", {
|
||||
STACK_JSON: JSON.stringify({ position: 1, size: 1, base: { ref: "master" } }),
|
||||
PR_BASE_REF: "master",
|
||||
TEST_COMPARE_FILES: files("releases/v2026.1015.0.md"),
|
||||
}).full_ci, "false");
|
||||
});
|
||||
|
||||
|
||||
function routingPlacement(vars = {}, github = {}) {
|
||||
const source = "[34892728]";
|
||||
|
||||
@@ -215,10 +215,14 @@ jobs:
|
||||
echo "runner=$aws_runner" >> "$GITHUB_OUTPUT"
|
||||
echo '::notice title=AWS CI routing::Using an ephemeral RunsOn Fleet runner'
|
||||
|
||||
- name: Select stacked PR CI scope
|
||||
- name: Select PR CI scope
|
||||
id: scope
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
EVENT_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
STACK_JSON: ${{ toJSON(github.event.pull_request.stack) }}
|
||||
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||
run: |
|
||||
@@ -226,17 +230,53 @@ jobs:
|
||||
|
||||
full_ci='true'
|
||||
reason='ordinary pull request'
|
||||
# True when the PR's own diff is everything it adds to the stack's
|
||||
# base branch: no stack, or the lowest unmerged PR in one. Above
|
||||
# that, the diff omits lower PRs' code that this run must still test.
|
||||
diff_covers_stack='true'
|
||||
|
||||
# Release notes PRs touch only releases/v*.md or releases/beta/v*.md,
|
||||
# which no build, test, or e2e lane reads. The diff is pinned to the
|
||||
# event's base and head SHAs, so a later push cannot change the
|
||||
# decision for this run. Any lookup failure keeps full CI.
|
||||
release_notes_only() {
|
||||
[[ "${EVENT_NAME:-}" == 'pull_request' ]] || return 1
|
||||
[[ "${EVENT_BASE_SHA:-}" =~ ^[0-9a-f]{40}$ ]] || return 1
|
||||
[[ "${EVENT_HEAD_SHA:-}" =~ ^[0-9a-f]{40}$ ]] || return 1
|
||||
|
||||
local comparison
|
||||
comparison="$(gh api \
|
||||
-H 'Accept: application/vnd.github+json' \
|
||||
-H 'X-GitHub-Api-Version: 2022-11-28' \
|
||||
"/repos/paperclipai/paperclip/compare/$EVENT_BASE_SHA...$EVENT_HEAD_SHA" 2>/dev/null)" \
|
||||
|| return 1
|
||||
|
||||
# The compare API lists at most 300 files, so a full page may be
|
||||
# truncated. Renames count both the old and the new path.
|
||||
jq -e '
|
||||
(.files | type == "array") and
|
||||
(.files | length > 0 and length < 300) and
|
||||
all(.files[];
|
||||
(.filename | type == "string") and
|
||||
([.filename, .previous_filename] | map(select(. != null)) |
|
||||
all(.[]; type == "string" and test("^releases/(beta/)?v[^/]+\\.md$"))))
|
||||
' <<< "$comparison" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
if jq -e 'type == "object"' <<< "$STACK_JSON" >/dev/null 2>&1; then
|
||||
stack_position="$(jq -r '.position // empty' <<< "$STACK_JSON")"
|
||||
stack_size="$(jq -r '.size // empty' <<< "$STACK_JSON")"
|
||||
stack_base_ref="$(jq -r '.base.ref // empty' <<< "$STACK_JSON")"
|
||||
if [[ -z "$stack_base_ref" || "$stack_base_ref" != "$PR_BASE_REF" ]]; then
|
||||
diff_covers_stack='false'
|
||||
fi
|
||||
|
||||
if [[ ! "$stack_position" =~ ^[1-9][0-9]*$ ]] ||
|
||||
[[ ! "$stack_size" =~ ^[1-9][0-9]*$ ]] ||
|
||||
(( stack_position > stack_size )) ||
|
||||
[[ -z "$stack_base_ref" ]]; then
|
||||
reason='malformed stack metadata; defaulting to full CI'
|
||||
diff_covers_stack='false'
|
||||
elif (( stack_position == stack_size )); then
|
||||
reason='top pull request in stack'
|
||||
elif [[ "$stack_base_ref" == "$PR_BASE_REF" ]]; then
|
||||
@@ -247,6 +287,12 @@ jobs:
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$full_ci" == 'true' && "$diff_covers_stack" == 'true' ]] &&
|
||||
release_notes_only; then
|
||||
full_ci='false'
|
||||
reason='release notes only'
|
||||
fi
|
||||
|
||||
echo "full_ci=$full_ci" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice title=Stacked PR CI scope::$reason; full_ci=$full_ci"
|
||||
echo "::notice title=PR CI scope::$reason; full_ci=$full_ci"
|
||||
|
||||
@@ -16,6 +16,17 @@
|
||||
"path": "rules/product-harness-responsibility.mdc",
|
||||
"description": "Product requirements and review criteria for harness responsibility, minimal user-facing internals, truthful progress, and human decisions."
|
||||
},
|
||||
{
|
||||
"path": "rules/runtime-dependency-boundaries.mdc",
|
||||
"description": "Dependency cost, responsiveness, and verification requirements for changed server, adapter, and runner execution paths.",
|
||||
"scope": [
|
||||
"server/src/**",
|
||||
"packages/adapter-utils/src/**",
|
||||
"packages/adapters/*/src/**",
|
||||
"packages/paperclip-runner/src/**",
|
||||
"packages/paperclip-runner/runner/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "rules/chat-channel-platform-boundaries.mdc",
|
||||
"description": "Architecture requirements and review criteria for separating chat channel platform implementations from shared orchestration.",
|
||||
|
||||
@@ -33,12 +33,6 @@ function fixture(): { filePath: string; info: PaperclipRuntimeInfo } {
|
||||
}
|
||||
|
||||
describe("runtime info", () => {
|
||||
it("writes and reads the selected runtime endpoint", () => {
|
||||
const { filePath, info } = fixture();
|
||||
writeRuntimeInfo(info, filePath);
|
||||
expect(readRuntimeInfo("default", filePath)).toEqual(info);
|
||||
});
|
||||
|
||||
it("does not remove runtime info owned by a replacement process", () => {
|
||||
const { filePath, info } = fixture();
|
||||
writeRuntimeInfo(info, filePath);
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { bytesToPortableFileEntry, isBlobStorePath, readZipArchive } from "../commands/client/zip.js";
|
||||
import { createStoredZipArchive } from "./helpers/zip.js";
|
||||
|
||||
describe("isBlobStorePath", () => {
|
||||
it("matches blobs/ entries at the archive root and under a package root", () => {
|
||||
expect(isBlobStorePath("blobs/4f2d1c9a")).toBe(true);
|
||||
expect(isBlobStorePath("paperclip-demo/blobs/4f2d1c9a")).toBe(true);
|
||||
expect(isBlobStorePath("tasks/pap-1/TASK.md")).toBe(false);
|
||||
expect(isBlobStorePath("blobs/nested/file")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("bytesToPortableFileEntry", () => {
|
||||
it("keeps blobs/ entries as base64 octet streams regardless of extension", () => {
|
||||
const bytes = new Uint8Array([0x00, 0x01, 0x80, 0xfe, 0xff]);
|
||||
expect(bytesToPortableFileEntry("blobs/4f2d1c9a", bytes)).toEqual({
|
||||
encoding: "base64",
|
||||
data: Buffer.from(bytes).toString("base64"),
|
||||
contentType: "application/octet-stream",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to base64 when bytes are not valid UTF-8", () => {
|
||||
const invalidUtf8 = new Uint8Array([0x68, 0x69, 0xff, 0xfe, 0xc0]);
|
||||
expect(bytesToPortableFileEntry("tasks/pap-1/raw-notes", invalidUtf8)).toEqual({
|
||||
encoding: "base64",
|
||||
data: Buffer.from(invalidUtf8).toString("base64"),
|
||||
contentType: "application/octet-stream",
|
||||
});
|
||||
});
|
||||
|
||||
it("decodes valid UTF-8 entries to text", () => {
|
||||
const bytes = new TextEncoder().encode("# Notes\n\ncafé ✅\n");
|
||||
expect(bytesToPortableFileEntry("tasks/pap-1/TASK.md", bytes)).toBe("# Notes\n\ncafé ✅\n");
|
||||
});
|
||||
});
|
||||
|
||||
describe("readZipArchive", () => {
|
||||
it("round-trips blob and invalid UTF-8 entries byte-exactly", async () => {
|
||||
const blobBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x00, 0xff]);
|
||||
const invalidUtf8 = new Uint8Array([0x68, 0x69, 0xff, 0xfe, 0xc0]);
|
||||
const archive = createStoredZipArchive(
|
||||
{
|
||||
"COMPANY.md": "# Company\n",
|
||||
"blobs/4f2d1c9a": blobBytes,
|
||||
"notes/raw": invalidUtf8,
|
||||
},
|
||||
"paperclip-demo",
|
||||
);
|
||||
|
||||
await expect(readZipArchive(archive)).resolves.toEqual({
|
||||
rootPath: "paperclip-demo",
|
||||
files: {
|
||||
"COMPANY.md": "# Company\n",
|
||||
"blobs/4f2d1c9a": {
|
||||
encoding: "base64",
|
||||
data: Buffer.from(blobBytes).toString("base64"),
|
||||
contentType: "application/octet-stream",
|
||||
},
|
||||
"notes/raw": {
|
||||
encoding: "base64",
|
||||
data: Buffer.from(invalidUtf8).toString("base64"),
|
||||
contentType: "application/octet-stream",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"bucket": "pages.paperclip.ing",
|
||||
"prefix": "pap-14557-monitor-visibility/",
|
||||
"slug": "pap-14557-monitor-visibility",
|
||||
"url": "https://pages.paperclip.ing/pap-14557-monitor-visibility/",
|
||||
"baseUrl": "https://pages.paperclip.ing",
|
||||
"publishedAt": "2026-07-17T15:51:31Z",
|
||||
"sourceHash": "4d276364eb4e122d782247da6f1f0996763c12175ec0d7efa8ac7a0568fb6f2c",
|
||||
"version": 1
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>PAP-14557 — Monitor visibility wireframes</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafaf8;
|
||||
--panel: #ffffff;
|
||||
--ink: #111111;
|
||||
--muted: #666666;
|
||||
--line: #e5e5e0;
|
||||
--accent: #d33;
|
||||
--maxw: 1200px;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
font: 15px/1.55 -apple-system, system-ui, "Segoe UI", sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
a { color: inherit; }
|
||||
|
||||
.shell { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }
|
||||
|
||||
.toc {
|
||||
position: sticky; top: 0; align-self: start;
|
||||
height: 100vh; overflow: auto;
|
||||
padding: 24px 16px 24px 24px;
|
||||
border-right: 1px solid var(--line);
|
||||
background: var(--panel);
|
||||
font-size: 13px;
|
||||
z-index: 20;
|
||||
}
|
||||
.toc h1 { font-size: 14px; margin: 0 0 4px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
|
||||
.toc h2 { font-size: 12px; margin: 24px 0 6px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
|
||||
.toc a { display: block; text-decoration: none; padding: 6px 8px; margin-left: -8px; border-radius: 4px; color: var(--ink); }
|
||||
.toc a:hover { background: var(--bg); }
|
||||
.toc .num { display: inline-block; width: 22px; color: var(--muted); }
|
||||
.toc .toc-summary { display: none; }
|
||||
.toc .toc-body { display: block; }
|
||||
|
||||
main { padding: 48px 56px 96px; max-width: 1300px; min-width: 0; }
|
||||
|
||||
header.hero { max-width: var(--maxw); margin-bottom: 64px; }
|
||||
header.hero .crumb { color: var(--muted); font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; }
|
||||
header.hero h1 { font-size: 36px; line-height: 1.15; margin: 8px 0 16px; }
|
||||
header.hero p { color: var(--muted); max-width: 720px; font-size: 16px; }
|
||||
header.hero .pills { margin-top: 20px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
header.hero .pill { font-size: 12px; padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--panel); color: var(--muted); }
|
||||
|
||||
section { scroll-margin-top: 24px; max-width: var(--maxw); margin-bottom: 96px; }
|
||||
section .lede { color: var(--muted); margin: 0 0 12px; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 700; }
|
||||
section h2 { font-size: 28px; line-height: 1.2; margin: 0 0 8px; }
|
||||
section h2 .step-num { display: inline-block; width: 40px; color: var(--muted); }
|
||||
section h3 { font-size: 16px; margin: 24px 0 8px; }
|
||||
section .desc { color: var(--muted); margin: 0 0 24px; max-width: 760px; }
|
||||
|
||||
.grid {
|
||||
display: grid; gap: 24px;
|
||||
grid-template-columns: minmax(0, 1.4fr) 220px minmax(0, 0.9fr);
|
||||
align-items: start;
|
||||
}
|
||||
.grid.no-mobile { grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.9fr); }
|
||||
@media (max-width: 1180px) {
|
||||
.grid { grid-template-columns: minmax(0, 1fr) 200px; }
|
||||
.grid.no-mobile { grid-template-columns: minmax(0, 1fr); }
|
||||
.grid > .notes-col { grid-column: 1 / -1; }
|
||||
}
|
||||
@media (max-width: 980px) { .grid { grid-template-columns: 1fr; } .grid > .mobile-col { max-width: 280px; } }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.shell { grid-template-columns: 1fr; }
|
||||
.toc {
|
||||
position: sticky; top: 0;
|
||||
height: auto; max-height: 60vh;
|
||||
padding: 12px 16px;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.toc .toc-summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.toc .toc-summary::-webkit-details-marker { display: none; }
|
||||
.toc .toc-summary .title { font-size: 13px; font-weight: 600; }
|
||||
.toc .toc-summary .crumb { font-size: 11px; color: var(--muted); letter-spacing: 0.04em; text-transform: uppercase; }
|
||||
.toc .toc-summary .chevron {
|
||||
width: 10px; height: 10px;
|
||||
border-right: 1.5px solid var(--ink);
|
||||
border-bottom: 1.5px solid var(--ink);
|
||||
transform: rotate(45deg);
|
||||
transition: transform 120ms ease;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.toc[open] .toc-summary .chevron { transform: rotate(225deg) translate(-2px, -2px); }
|
||||
.toc .toc-body { display: none; padding-top: 8px; border-top: 1px solid var(--line); margin-top: 8px; }
|
||||
.toc[open] .toc-body { display: block; }
|
||||
main { padding: 24px 20px 64px; }
|
||||
header.hero { margin-bottom: 40px; }
|
||||
header.hero h1 { font-size: 28px; }
|
||||
header.hero p { font-size: 15px; }
|
||||
section { margin-bottom: 64px; scroll-margin-top: 80px; }
|
||||
section h2 { font-size: 24px; }
|
||||
section h2 .step-num { width: 32px; }
|
||||
.flow-section { padding: 20px; margin-bottom: 40px; scroll-margin-top: 80px; }
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
main { padding: 16px 14px 56px; }
|
||||
header.hero { margin-bottom: 32px; }
|
||||
header.hero h1 { font-size: 24px; line-height: 1.2; }
|
||||
header.hero p { font-size: 14px; }
|
||||
section { margin-bottom: 48px; }
|
||||
section h2 { font-size: 20px; line-height: 1.25; }
|
||||
section h2 .step-num { width: 28px; }
|
||||
section h3 { font-size: 15px; margin: 20px 0 6px; }
|
||||
section .desc { font-size: 14px; }
|
||||
.grid { gap: 16px; }
|
||||
.flow-section { padding: 16px; border-radius: 8px; }
|
||||
.wire .label { padding: 8px 10px; font-size: 11px; gap: 8px; }
|
||||
body { font-size: 14px; }
|
||||
}
|
||||
|
||||
.wire {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
cursor: zoom-in;
|
||||
transition: transform 80ms ease, box-shadow 80ms ease;
|
||||
}
|
||||
.wire:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||
.wire .label {
|
||||
padding: 10px 14px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
border-bottom: 1px solid var(--line);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.wire img { display: block; width: 100%; height: auto; background: #fff; }
|
||||
.wire.mobile-wire img { max-width: 220px; margin: 0 auto; }
|
||||
|
||||
.ref {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.ref .label { font-size: 12px; color: var(--muted); margin-bottom: 8px; display: flex; justify-content: space-between; align-items: baseline; }
|
||||
.ref img { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: 4px; cursor: zoom-in; }
|
||||
|
||||
.notes ul { padding-left: 20px; margin: 8px 0; }
|
||||
.notes li { margin-bottom: 6px; }
|
||||
.notes .why {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-left: 3px solid var(--ink);
|
||||
padding: 14px 18px;
|
||||
border-radius: 4px;
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.flow-section { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 32px; margin-bottom: 64px; max-width: var(--maxw); }
|
||||
.flow-section h2 { margin-top: 0; }
|
||||
.flow-section .wire { margin-top: 16px; }
|
||||
|
||||
.lightbox {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(0,0,0,0.86);
|
||||
display: none;
|
||||
align-items: center; justify-content: center;
|
||||
z-index: 100;
|
||||
cursor: zoom-out;
|
||||
padding: 32px;
|
||||
}
|
||||
.lightbox.open { display: flex; }
|
||||
.lightbox img { max-width: 100%; max-height: 100%; background: #fff; box-shadow: 0 30px 80px rgba(0,0,0,0.4); border-radius: 4px; }
|
||||
.lightbox .close { position: absolute; top: 16px; right: 24px; font-size: 28px; color: #fff; cursor: pointer; }
|
||||
.lightbox .caption { position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); color: #fff; font-size: 13px; opacity: 0.9; }
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.lightbox { padding: 12px; overflow: auto; touch-action: pinch-zoom; }
|
||||
.lightbox.open { display: block; }
|
||||
.lightbox img { display: block; width: 100%; max-width: none; max-height: none; height: auto; margin: 40px auto 56px; box-shadow: none; }
|
||||
.lightbox .close { position: fixed; top: 8px; right: 12px; font-size: 32px; }
|
||||
.lightbox .caption { position: fixed; left: 50%; transform: translateX(-50%); font-size: 12px; bottom: 12px; text-align: center; background: rgba(0,0,0,0.7); border-radius: 4px; padding: 6px 12px; }
|
||||
}
|
||||
|
||||
.footer { color: var(--muted); font-size: 12px; max-width: var(--maxw); border-top: 1px solid var(--line); padding-top: 24px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="shell">
|
||||
<details class="toc">
|
||||
<summary class="toc-summary">
|
||||
<span>
|
||||
<span class="crumb">PAP-14557 · Monitor visibility</span><br>
|
||||
<span class="title">Jump to a screen</span>
|
||||
</span>
|
||||
<span class="chevron" aria-hidden="true"></span>
|
||||
</summary>
|
||||
<nav class="toc-body" aria-label="Section navigation">
|
||||
<h1>PAP-14557</h1>
|
||||
<div style="font-size: 13px; color: var(--muted); margin-bottom: 16px;">Monitor visibility wireframes</div>
|
||||
|
||||
<h2>Flow</h2>
|
||||
<a href="#flow"><span class="num">⤳</span>The three surfaces</a>
|
||||
|
||||
<h2>Screens</h2>
|
||||
<a href="#s1"><span class="num">1</span>Task page top — banner</a>
|
||||
<a href="#s2"><span class="num">2</span>Above the composer — strip</a>
|
||||
<a href="#s3"><span class="num">3</span>Properties row + tooltip</a>
|
||||
<a href="#s4"><span class="num">4</span>State matrix + copy rules</a>
|
||||
|
||||
<h2>Open questions</h2>
|
||||
<a href="#open"><span class="num">?</span>For reviewer</a>
|
||||
</nav>
|
||||
</details>
|
||||
|
||||
<main>
|
||||
<header class="hero">
|
||||
<div class="crumb">PAP-14557 · Wireframes</div>
|
||||
<h1>Make a waiting monitor obvious</h1>
|
||||
<p>When an issue has a monitor armed, nothing on the page says so unless you squint at a truncated properties row. These wireframes surface the same monitor state in three places — a banner at the top of the task page, a strip directly above the reply composer, and a readable two-line properties row with a hover tooltip for full details. Every surface shows both the exact time and how far away it is from now.</p>
|
||||
<div class="pills">
|
||||
<span class="pill">4 screens + 2 mobile</span>
|
||||
<span class="pill">Desktop + mobile</span>
|
||||
<span class="pill">Lo-fi · monochrome</span>
|
||||
<span class="pill">Click any wireframe to zoom</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="flow" class="flow-section">
|
||||
<div class="lede">Flow</div>
|
||||
<h2>One monitor state, three surfaces</h2>
|
||||
<p class="desc">The banner, the composer strip, and the properties row are all fed by the same state + copy rules (screen 04), so they can never disagree. Red dashed marks are annotation callouts, not UI.</p>
|
||||
<div class="wire" data-zoom data-caption="Flow — the three surfaces">
|
||||
<div class="label"><span>flow.svg</span><span>1280×304</span></div>
|
||||
<img src="wireframes/flow.svg" alt="Flow diagram of the three monitor surfaces" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="s1">
|
||||
<div class="lede">Step 1 · Task page top</div>
|
||||
<h2><span class="step-num">1.</span>Monitor banner under the title</h2>
|
||||
<p class="desc">A full-width banner sits between the issue title and the description whenever a monitor is scheduled, due, or overdue. Headline leads with the relative time ("resumes in 2h 12m"); the second line gives the exact local time, the attempt count, and what is being watched. "Check now" fires the monitor immediately; "Edit" opens the existing monitor editor.</p>
|
||||
<div class="grid">
|
||||
<div class="wire" data-zoom data-caption="01 · Task page top with monitor banner (desktop)">
|
||||
<div class="label"><span>01-issue-top.svg</span><span>1280×800 · desktop</span></div>
|
||||
<img src="wireframes/01-issue-top.svg" alt="Task page top with monitor banner (desktop) wireframe" />
|
||||
</div>
|
||||
<div class="wire mobile-wire mobile-col" data-zoom data-caption="01 · Task page (mobile)">
|
||||
<div class="label"><span>mobile</span><span>375×812</span></div>
|
||||
<img src="wireframes/01-issue-top-mobile.svg" alt="Task page with monitor banner (mobile) wireframe" />
|
||||
</div>
|
||||
<div class="notes-col">
|
||||
<div class="notes">
|
||||
<h3>Annotations</h3>
|
||||
<ul>
|
||||
<li><b>1</b> — Monitor banner: bold relative time first, exact time + attempt + watched service second, actions on the right. On mobile the actions drop below the text.</li>
|
||||
<li><b>2</b> — Properties Monitor row switches to the two-line layout (detail on screen 3). On mobile (callouts 2–3 on the phone frame) the collapsed properties bar carries a compact "in 2h 12m" chip and the strip repeats above the composer.</li>
|
||||
</ul>
|
||||
<div class="why"><b>Why this changes:</b> today the only always-visible signal is the truncated properties row; the existing "Monitor scheduled" card renders in the description area where it scrolls away and is easy to miss. The banner is pinned to the page header area so the very first glance answers "why is nothing happening, and when will it?"</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="s2">
|
||||
<div class="lede">Step 2 · Thread bottom</div>
|
||||
<h2><span class="step-num">2.</span>Strip above the reply composer</h2>
|
||||
<p class="desc">The bottom of the thread is where you go to ask "what's happening?" — so the answer sits right there. A compact strip directly above the composer repeats the resume time (exact + relative + attempt) with a "Check now" action, and a one-line hint under the composer explains that replying wakes the agent before the scheduled check.</p>
|
||||
<div class="grid">
|
||||
<div class="wire" data-zoom data-caption="02 · Monitor strip above the composer (desktop)">
|
||||
<div class="label"><span>02-composer.svg</span><span>1280×800 · desktop</span></div>
|
||||
<img src="wireframes/02-composer.svg" alt="Monitor strip above composer (desktop) wireframe" />
|
||||
</div>
|
||||
<div class="wire mobile-wire mobile-col" data-zoom data-caption="02 · Monitor strip above the composer (mobile)">
|
||||
<div class="label"><span>mobile</span><span>375×812</span></div>
|
||||
<img src="wireframes/02-composer-mobile.svg" alt="Monitor strip above composer (mobile) wireframe" />
|
||||
</div>
|
||||
<div class="notes-col">
|
||||
<div class="ref">
|
||||
<div class="label"><span>Reference</span><span>current thread bottom</span></div>
|
||||
<img src="screenshots/current-thread-bottom.png" alt="Current thread bottom with no monitor indication" data-zoom />
|
||||
</div>
|
||||
<div class="notes">
|
||||
<h3>Annotations</h3>
|
||||
<ul>
|
||||
<li><b>1</b> — Monitor strip: anchored to the composer (scrolls with it), not part of the comment list. Same copy system as the banner.</li>
|
||||
<li><b>2</b> — Wake hint under the composer: sets the expectation that sending a message preempts the scheduled check.</li>
|
||||
</ul>
|
||||
<div class="why"><b>Why this changes:</b> in the reference screenshot the last comment says a monitor is armed only in prose ("issue monitor fires 16:08Z"), and once more comments land even that disappears. The strip keeps the resume time visible at the exact spot where a human decides whether to wait or to nudge.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="s3">
|
||||
<div class="lede">Step 3 · Properties pane</div>
|
||||
<h2><span class="step-num">3.</span>Readable Monitor row + details tooltip</h2>
|
||||
<p class="desc">The properties row stops cramming label, date, and attempt onto one colliding line. Collapsed: two lines — bold relative time, then muted "absolute time · Attempt N". Hover (tap on mobile) opens a details card with the full timestamp incl. timezone, watched service, notes, last-trigger info, and quick actions. Click still opens the existing editor popover.</p>
|
||||
<div class="grid no-mobile">
|
||||
<div class="wire" data-zoom data-caption="03 · Properties Monitor row — before / after / hover">
|
||||
<div class="label"><span>03-properties-monitor.svg</span><span>1280×800 · component sheet</span></div>
|
||||
<img src="wireframes/03-properties-monitor.svg" alt="Properties monitor row redesign wireframe" />
|
||||
</div>
|
||||
<div class="notes-col">
|
||||
<div class="ref">
|
||||
<div class="label"><span>Reference</span><span>current properties row</span></div>
|
||||
<img src="screenshots/current-properties-monitor.png" alt="Current cramped monitor properties row" data-zoom />
|
||||
</div>
|
||||
<div class="notes">
|
||||
<h3>Annotations</h3>
|
||||
<ul>
|
||||
<li><b>1</b> — Current failure: "Next check" truncates to "Ne…", the date shows the year but no time, and "Attempt 1" wraps into a two-line crumb.</li>
|
||||
<li><b>2</b> — Proposed collapsed row: relative time is the headline; absolute time and attempt live on a second muted line sized for the 352px pane.</li>
|
||||
<li><b>3</b> — Hover/tap tooltip: exact timestamp with timezone, watched service, notes, last-trigger state, and Check now / Edit / Clear without opening the editor.</li>
|
||||
</ul>
|
||||
<div class="why"><b>Why this changes:</b> the row currently tries to show four facts in one 200px slot, so none of them are readable. Two lines fit the pane; everything else moves to a tooltip you summon only when you care about specifics.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="s4">
|
||||
<div class="lede">Step 4 · System</div>
|
||||
<h2><span class="step-num">4.</span>State matrix and copy rules</h2>
|
||||
<p class="desc">All three surfaces share one formatter. Relative time always leads and uses two units under 24h ("in 2h 12m", not "in 2h" — today's single-unit rounding is why the current UI feels vague). Overdue and due-now states get explicit copy instead of silently showing a past date.</p>
|
||||
<div class="grid no-mobile">
|
||||
<div class="wire" data-zoom data-caption="04 · Monitor state matrix">
|
||||
<div class="label"><span>04-states.svg</span><span>1280×800 · component sheet</span></div>
|
||||
<img src="wireframes/04-states.svg" alt="Monitor state matrix wireframe" />
|
||||
</div>
|
||||
<div class="notes-col">
|
||||
<div class="notes">
|
||||
<h3>States covered</h3>
|
||||
<ul>
|
||||
<li>Scheduled — under 1h / under 24h / beyond today.</li>
|
||||
<li>Retrying — attempt count > 1 gets visual weight.</li>
|
||||
<li>Due now / overdue — explicit copy, never a stale-looking past date.</li>
|
||||
<li>Cleared / none — muted, no clock icon, banner and strip hidden.</li>
|
||||
</ul>
|
||||
<div class="why"><b>Why this changes:</b> a single copy system means the banner, strip, and row can never disagree about when the task resumes, and edge states (overdue, cleared) stop looking like bugs.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="open">
|
||||
<div class="lede">For reviewer</div>
|
||||
<h2>Open questions</h2>
|
||||
<div class="notes">
|
||||
<ul>
|
||||
<li><b>Banner vs. existing card</b> — there is already an "IssueMonitorActivityCard" that renders in the description area. Proposal: promote/replace it with the pinned banner (screen 1) rather than showing both. OK?</li>
|
||||
<li><b>Strip stickiness</b> — should the composer strip scroll with the thread (inline, as drawn) or stick to the composer even mid-scroll? Drawn as anchored to the composer.</li>
|
||||
<li><b>Relative-time precision</b> — adopt the two-unit format ("in 2h 12m") everywhere, including the existing scheduled-retry row which uses the same formatter?</li>
|
||||
<li><b>Hover vs. click</b> — the properties row currently opens the editor on click. Proposal keeps click = editor, hover/tap = read-only details tooltip. Fine, or should tap open the editor directly on mobile?</li>
|
||||
<li><b>Live countdown</b> — should the relative time tick live (re-render every minute) or is on-load accuracy enough?</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="footer">
|
||||
Wireframes follow the in-tree <code>wireframe</code> skill house style — black on white, 1.5pt strokes, 8px grid, type sizes 12/14/20/28. Red dashed annotations are the call-out layer only and not part of the proposed UI.
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div class="lightbox" id="lb" aria-hidden="true">
|
||||
<span class="close" id="lbClose">×</span>
|
||||
<img id="lbImg" alt="" />
|
||||
<div class="caption" id="lbCap"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const lb = document.getElementById('lb');
|
||||
const lbImg = document.getElementById('lbImg');
|
||||
const lbCap = document.getElementById('lbCap');
|
||||
const lbClose = document.getElementById('lbClose');
|
||||
|
||||
document.querySelectorAll('[data-zoom]').forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
const target = el.tagName === 'IMG' ? el : el.querySelector('img');
|
||||
if (!target) return;
|
||||
lbImg.src = target.src;
|
||||
lbImg.alt = target.alt;
|
||||
lbCap.textContent = el.dataset.caption || target.alt || '';
|
||||
lb.classList.add('open');
|
||||
});
|
||||
});
|
||||
|
||||
function close() { lb.classList.remove('open'); }
|
||||
lb.addEventListener('click', close);
|
||||
lbClose.addEventListener('click', close);
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); });
|
||||
|
||||
const toc = document.querySelector('details.toc');
|
||||
if (toc) {
|
||||
const mq = window.matchMedia('(max-width: 900px)');
|
||||
const apply = () => { toc.open = !mq.matches; };
|
||||
apply();
|
||||
mq.addEventListener('change', apply);
|
||||
toc.querySelectorAll('.toc-body a').forEach((a) => {
|
||||
a.addEventListener('click', () => { if (mq.matches) toc.open = false; });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 83 KiB |
@@ -1,105 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="375" height="812" viewBox="0 0 375 812"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- 01 · Issue page (mobile) — banner, properties chip, composer strip. 375×812 -->
|
||||
<rect x="0" y="0" width="375" height="812" />
|
||||
|
||||
<!-- top bar -->
|
||||
<line x1="0" y1="56" x2="375" y2="56" />
|
||||
<polyline points="28,20 20,28 28,36" fill="none" />
|
||||
<text x="44" y="33" font-size="14" font-weight="600" stroke="none" fill="#000">PAP-14557</text>
|
||||
<circle cx="343" cy="28" r="12" fill="#e6e6e6" />
|
||||
|
||||
<!-- title + pill -->
|
||||
<text x="16" y="92" font-size="20" font-weight="700" stroke="none" fill="#000">Nightly model comparison sweep</text>
|
||||
<g transform="translate(16,108)">
|
||||
<rect width="96" height="24" rx="12" />
|
||||
<text x="48" y="16" font-size="12" text-anchor="middle" stroke="none" fill="#000">In Progress</text>
|
||||
</g>
|
||||
|
||||
<!-- NEW: monitor banner -->
|
||||
<g transform="translate(16,148)">
|
||||
<rect width="344" height="112" rx="8" />
|
||||
<circle cx="32" cy="32" r="12" />
|
||||
<line x1="32" y1="32" x2="32" y2="24" />
|
||||
<line x1="32" y1="32" x2="38" y2="32" />
|
||||
<text x="56" y="28" font-size="14" font-weight="600" stroke="none" fill="#000">Waiting on monitor — in 2h 12m</text>
|
||||
<text x="56" y="48" font-size="12" stroke="none" fill="#666">Thu Jul 17, 4:08 PM · Attempt 1</text>
|
||||
<g transform="translate(56,64)">
|
||||
<rect width="104" height="32" rx="4" />
|
||||
<text x="52" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#000">Check now</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- description placeholder -->
|
||||
<g transform="translate(16,292)">
|
||||
<line x1="0" y1="8" x2="344" y2="8" stroke="#666" />
|
||||
<line x1="0" y1="24" x2="344" y2="24" stroke="#666" />
|
||||
<line x1="0" y1="40" x2="240" y2="40" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<!-- comment card -->
|
||||
<g transform="translate(16,356)">
|
||||
<rect width="344" height="120" rx="8" />
|
||||
<circle cx="28" cy="28" r="12" fill="#e6e6e6" />
|
||||
<text x="48" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Senior Engineer Fable</text>
|
||||
<text x="48" y="40" font-size="12" stroke="none" fill="#666">15m ago</text>
|
||||
<line x1="16" y1="60" x2="328" y2="60" stroke="#666" />
|
||||
<line x1="16" y1="76" x2="328" y2="76" stroke="#666" />
|
||||
<line x1="16" y1="92" x2="224" y2="92" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<!-- collapsed properties row with monitor chip -->
|
||||
<g transform="translate(16,500)">
|
||||
<rect width="344" height="48" rx="8" />
|
||||
<text x="16" y="29" font-size="14" font-weight="600" stroke="none" fill="#000">Properties</text>
|
||||
<circle cx="240" cy="24" r="7" />
|
||||
<line x1="240" y1="24" x2="240" y2="19" />
|
||||
<line x1="240" y1="24" x2="244" y2="24" />
|
||||
<text x="252" y="28" font-size="12" stroke="none" fill="#666">in 2h 12m</text>
|
||||
<polyline points="324,20 332,28 324,36" fill="none" />
|
||||
</g>
|
||||
|
||||
<!-- NEW: monitor strip above composer -->
|
||||
<g transform="translate(16,624)">
|
||||
<rect width="344" height="56" rx="8" />
|
||||
<circle cx="28" cy="28" r="10" />
|
||||
<line x1="28" y1="28" x2="28" y2="21" />
|
||||
<line x1="28" y1="28" x2="33" y2="28" />
|
||||
<text x="48" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Resumes in 2h 12m</text>
|
||||
<text x="48" y="42" font-size="12" stroke="none" fill="#666">Jul 17, 4:08 PM · Attempt 1</text>
|
||||
<text x="288" y="32" font-size="12" stroke="none" fill="#000">Check now</text>
|
||||
</g>
|
||||
|
||||
<!-- composer -->
|
||||
<g transform="translate(16,696)">
|
||||
<rect width="344" height="96" rx="8" />
|
||||
<text x="16" y="28" font-size="14" stroke="none" fill="#666">Reply…</text>
|
||||
<g transform="translate(16,48)">
|
||||
<rect width="32" height="32" rx="4" />
|
||||
<line x1="10" y1="22" x2="22" y2="10" />
|
||||
</g>
|
||||
<g transform="translate(240,48)">
|
||||
<rect width="88" height="32" rx="4" fill="#000" />
|
||||
<text x="44" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#fff">Send</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- annotations -->
|
||||
<g data-region="annotations">
|
||||
<rect x="8" y="140" width="360" height="128" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(360,140)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
</g>
|
||||
<rect x="224" y="508" width="112" height="32" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(216,504)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">2</text>
|
||||
</g>
|
||||
<rect x="8" y="616" width="360" height="72" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(360,616)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">3</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1,100 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- 01 · Issue page top — monitor banner. Desktop 1280×800 -->
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- top bar -->
|
||||
<line x1="0" y1="56" x2="1280" y2="56" />
|
||||
<text x="24" y="36" font-size="14" stroke="none" fill="#666">Issues /</text>
|
||||
<text x="88" y="36" font-size="14" font-weight="600" stroke="none" fill="#000">PAP-14557</text>
|
||||
<circle cx="1240" cy="28" r="12" fill="#e6e6e6" />
|
||||
|
||||
<!-- properties rail divider -->
|
||||
<line x1="880" y1="56" x2="880" y2="800" />
|
||||
|
||||
<!-- title + status pill -->
|
||||
<text x="48" y="112" font-size="28" font-weight="700" stroke="none" fill="#000">Nightly model comparison sweep</text>
|
||||
<g transform="translate(48,128)">
|
||||
<rect width="104" height="24" rx="12" />
|
||||
<text x="52" y="16" font-size="12" text-anchor="middle" stroke="none" fill="#000">In Progress</text>
|
||||
</g>
|
||||
|
||||
<!-- NEW: monitor banner -->
|
||||
<g transform="translate(48,168)">
|
||||
<rect width="784" height="88" rx="8" />
|
||||
<!-- clock glyph -->
|
||||
<circle cx="40" cy="44" r="14" />
|
||||
<line x1="40" y1="44" x2="40" y2="35" />
|
||||
<line x1="40" y1="44" x2="47" y2="44" />
|
||||
<text x="72" y="40" font-size="14" font-weight="600" stroke="none" fill="#000">Waiting on monitor — resumes in 2h 12m</text>
|
||||
<text x="72" y="62" font-size="12" stroke="none" fill="#666">Thu Jul 17, 4:08 PM (your time) · Attempt 1 · Watching: vercel-deploy</text>
|
||||
<g transform="translate(568,28)">
|
||||
<rect width="104" height="32" rx="4" />
|
||||
<text x="52" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#000">Check now</text>
|
||||
</g>
|
||||
<g transform="translate(688,28)">
|
||||
<rect width="72" height="32" rx="4" />
|
||||
<text x="36" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#000">Edit</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- description placeholder -->
|
||||
<g transform="translate(48,288)">
|
||||
<line x1="0" y1="8" x2="784" y2="8" stroke="#666" />
|
||||
<line x1="0" y1="24" x2="784" y2="24" stroke="#666" />
|
||||
<line x1="0" y1="40" x2="520" y2="40" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<line x1="48" y1="360" x2="832" y2="360" />
|
||||
<text x="48" y="392" font-size="14" font-weight="600" stroke="none" fill="#000">Activity</text>
|
||||
|
||||
<!-- first comment card -->
|
||||
<g transform="translate(48,408)">
|
||||
<rect width="784" height="136" rx="8" />
|
||||
<circle cx="32" cy="32" r="16" fill="#e6e6e6" />
|
||||
<text x="56" y="28" font-size="14" font-weight="600" stroke="none" fill="#000">Senior Engineer Fable</text>
|
||||
<text x="720" y="28" font-size="12" stroke="none" fill="#666">15m ago</text>
|
||||
<line x1="56" y1="56" x2="744" y2="56" stroke="#666" />
|
||||
<line x1="56" y1="72" x2="744" y2="72" stroke="#666" />
|
||||
<line x1="56" y1="88" x2="744" y2="88" stroke="#666" />
|
||||
<line x1="56" y1="104" x2="480" y2="104" stroke="#666" />
|
||||
</g>
|
||||
<text x="440" y="584" font-size="12" text-anchor="middle" stroke="none" fill="#666">⌄ thread continues — see screen 02 for the composer</text>
|
||||
|
||||
<!-- properties rail -->
|
||||
<text x="904" y="96" font-size="12" stroke="none" fill="#666">PROPERTIES</text>
|
||||
<text x="904" y="136" font-size="12" stroke="none" fill="#666">Status</text>
|
||||
<text x="1032" y="136" font-size="14" stroke="none" fill="#000">In Progress</text>
|
||||
<text x="904" y="176" font-size="12" stroke="none" fill="#666">Priority</text>
|
||||
<text x="1032" y="176" font-size="14" stroke="none" fill="#000">Medium</text>
|
||||
<text x="904" y="216" font-size="12" stroke="none" fill="#666">Assignee</text>
|
||||
<circle cx="1040" cy="212" r="8" fill="#e6e6e6" />
|
||||
<text x="1056" y="216" font-size="14" stroke="none" fill="#000">Fable</text>
|
||||
<text x="904" y="256" font-size="12" stroke="none" fill="#666">Approvers</text>
|
||||
<text x="1032" y="256" font-size="14" stroke="none" fill="#666">None</text>
|
||||
|
||||
<!-- NEW: two-line monitor row -->
|
||||
<text x="904" y="304" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="1040" cy="300" r="7" />
|
||||
<line x1="1040" y1="300" x2="1040" y2="295" />
|
||||
<line x1="1040" y1="300" x2="1044" y2="300" />
|
||||
<text x="1056" y="304" font-size="14" font-weight="600" stroke="none" fill="#000">In 2h 12m</text>
|
||||
<text x="1056" y="324" font-size="12" stroke="none" fill="#666">Jul 17, 4:08 PM · Attempt 1</text>
|
||||
|
||||
<text x="904" y="376" font-size="12" stroke="none" fill="#666">Watchdog</text>
|
||||
<text x="1032" y="376" font-size="14" stroke="none" fill="#666">None</text>
|
||||
|
||||
<!-- annotations -->
|
||||
<g data-region="annotations">
|
||||
<rect x="40" y="160" width="800" height="104" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(32,152)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
</g>
|
||||
<rect x="896" y="284" width="376" height="48" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(888,276)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">2</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,74 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="375" height="812" viewBox="0 0 375 812"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- 02 · Thread bottom (mobile) — strip + composer. 375×812 -->
|
||||
<rect x="0" y="0" width="375" height="812" />
|
||||
|
||||
<!-- top bar -->
|
||||
<line x1="0" y1="56" x2="375" y2="56" />
|
||||
<polyline points="28,20 20,28 28,36" fill="none" />
|
||||
<text x="44" y="33" font-size="14" font-weight="600" stroke="none" fill="#000">PAP-14557</text>
|
||||
|
||||
<!-- last comment (cut off) -->
|
||||
<text x="188" y="80" font-size="12" text-anchor="middle" stroke="none" fill="#666">⌃ earlier comments</text>
|
||||
<g transform="translate(16,96)">
|
||||
<rect width="344" height="176" rx="8" />
|
||||
<circle cx="28" cy="28" r="12" fill="#e6e6e6" />
|
||||
<text x="48" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Senior Engineer Fable</text>
|
||||
<text x="48" y="40" font-size="12" stroke="none" fill="#666">15m ago</text>
|
||||
<line x1="16" y1="64" x2="328" y2="64" stroke="#666" />
|
||||
<line x1="16" y1="80" x2="328" y2="80" stroke="#666" />
|
||||
<line x1="16" y1="96" x2="328" y2="96" stroke="#666" />
|
||||
<text x="16" y="128" font-size="14" font-weight="600" stroke="none" fill="#000">Next action (armed):</text>
|
||||
<line x1="16" y1="148" x2="264" y2="148" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<!-- previous-issue nav card -->
|
||||
<g transform="translate(16,296)">
|
||||
<rect width="280" height="56" rx="8" />
|
||||
<polyline points="20,20 14,28 20,36" fill="none" />
|
||||
<text x="32" y="24" font-size="12" stroke="none" fill="#666">Previous · PAP-14544</text>
|
||||
<text x="32" y="44" font-size="14" stroke="none" fill="#000">Phase 3 — rescore…</text>
|
||||
</g>
|
||||
|
||||
<!-- NEW: monitor strip -->
|
||||
<g transform="translate(16,536)">
|
||||
<rect width="344" height="88" rx="8" />
|
||||
<circle cx="28" cy="28" r="10" />
|
||||
<line x1="28" y1="28" x2="28" y2="21" />
|
||||
<line x1="28" y1="28" x2="33" y2="28" />
|
||||
<text x="48" y="26" font-size="14" font-weight="600" stroke="none" fill="#000">Resumes in 2h 12m</text>
|
||||
<text x="48" y="44" font-size="12" stroke="none" fill="#666">Thu Jul 17, 4:08 PM · Attempt 1</text>
|
||||
<g transform="translate(48,52)">
|
||||
<rect width="104" height="28" rx="4" />
|
||||
<text x="52" y="19" font-size="12" text-anchor="middle" stroke="none" fill="#000">Check now</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- composer -->
|
||||
<g transform="translate(16,640)">
|
||||
<rect width="344" height="136" rx="8" />
|
||||
<text x="16" y="32" font-size="14" stroke="none" fill="#666">Reply…</text>
|
||||
<g transform="translate(16,88)">
|
||||
<rect width="32" height="32" rx="4" />
|
||||
<line x1="10" y1="22" x2="22" y2="10" />
|
||||
</g>
|
||||
<g transform="translate(56,88)">
|
||||
<rect width="120" height="32" rx="16" />
|
||||
<text x="16" y="21" font-size="12" stroke="none" fill="#000">Agent mode</text>
|
||||
<polyline points="96,14 102,20 108,14" fill="none" />
|
||||
</g>
|
||||
<g transform="translate(240,88)">
|
||||
<rect width="88" height="32" rx="4" fill="#000" />
|
||||
<text x="44" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#fff">Send</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- annotations -->
|
||||
<g data-region="annotations">
|
||||
<rect x="8" y="528" width="360" height="104" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(360,528)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,100 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- 02 · Thread bottom — monitor strip above the composer. Desktop 1280×800 -->
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- top bar -->
|
||||
<line x1="0" y1="56" x2="1280" y2="56" />
|
||||
<text x="24" y="36" font-size="14" stroke="none" fill="#666">Issues /</text>
|
||||
<text x="88" y="36" font-size="14" font-weight="600" stroke="none" fill="#000">PAP-14557</text>
|
||||
<circle cx="1240" cy="28" r="12" fill="#e6e6e6" />
|
||||
|
||||
<!-- properties rail divider + abbreviated rail -->
|
||||
<line x1="880" y1="56" x2="880" y2="800" />
|
||||
<text x="904" y="96" font-size="12" stroke="none" fill="#666">PROPERTIES</text>
|
||||
<text x="904" y="136" font-size="12" stroke="none" fill="#666">Status</text>
|
||||
<text x="1032" y="136" font-size="14" stroke="none" fill="#000">In Progress</text>
|
||||
<text x="904" y="184" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="1040" cy="180" r="7" />
|
||||
<line x1="1040" y1="180" x2="1040" y2="175" />
|
||||
<line x1="1040" y1="180" x2="1044" y2="180" />
|
||||
<text x="1056" y="184" font-size="14" font-weight="600" stroke="none" fill="#000">In 2h 12m</text>
|
||||
<text x="1056" y="204" font-size="12" stroke="none" fill="#666">Jul 17, 4:08 PM · Attempt 1</text>
|
||||
|
||||
<!-- last agent comment (cut off at top) -->
|
||||
<text x="440" y="76" font-size="12" text-anchor="middle" stroke="none" fill="#666">⌃ earlier comments</text>
|
||||
<g transform="translate(48,96)">
|
||||
<rect width="784" height="176" rx="8" />
|
||||
<circle cx="32" cy="32" r="16" fill="#e6e6e6" />
|
||||
<text x="56" y="28" font-size="14" font-weight="600" stroke="none" fill="#000">Senior Engineer Fable</text>
|
||||
<text x="720" y="28" font-size="12" stroke="none" fill="#666">15m ago</text>
|
||||
<line x1="56" y1="56" x2="744" y2="56" stroke="#666" />
|
||||
<line x1="56" y1="72" x2="744" y2="72" stroke="#666" />
|
||||
<line x1="56" y1="88" x2="744" y2="88" stroke="#666" />
|
||||
<text x="56" y="120" font-size="14" font-weight="600" stroke="none" fill="#000">Next action (armed):</text>
|
||||
<line x1="216" y1="116" x2="744" y2="116" stroke="#666" />
|
||||
<line x1="56" y1="140" x2="560" y2="140" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<!-- previous-issue nav card -->
|
||||
<g transform="translate(48,304)">
|
||||
<rect width="384" height="64" rx="8" />
|
||||
<polyline points="24,24 16,32 24,40" fill="none" />
|
||||
<text x="40" y="28" font-size="12" stroke="none" fill="#666">Previous · PAP-14544</text>
|
||||
<text x="40" y="48" font-size="14" stroke="none" fill="#000">Phase 3 — rescore + gap re-runs…</text>
|
||||
</g>
|
||||
|
||||
<!-- NEW: monitor strip -->
|
||||
<g transform="translate(48,408)">
|
||||
<rect width="784" height="64" rx="8" />
|
||||
<circle cx="32" cy="32" r="12" />
|
||||
<line x1="32" y1="32" x2="32" y2="24" />
|
||||
<line x1="32" y1="32" x2="38" y2="32" />
|
||||
<text x="56" y="28" font-size="14" font-weight="600" stroke="none" fill="#000">Resumes in 2h 12m</text>
|
||||
<text x="56" y="48" font-size="12" stroke="none" fill="#666">Thu Jul 17, 4:08 PM · Attempt 1 · Watching: vercel-deploy</text>
|
||||
<g transform="translate(656,16)">
|
||||
<rect width="112" height="32" rx="4" />
|
||||
<text x="56" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#000">Check now</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- composer -->
|
||||
<g transform="translate(48,496)">
|
||||
<rect width="784" height="152" rx="8" />
|
||||
<text x="16" y="36" font-size="14" stroke="none" fill="#666">Reply…</text>
|
||||
<g transform="translate(16,104)">
|
||||
<rect width="32" height="32" rx="4" />
|
||||
<line x1="10" y1="22" x2="22" y2="10" />
|
||||
</g>
|
||||
<g transform="translate(56,104)">
|
||||
<rect width="128" height="32" rx="16" />
|
||||
<text x="16" y="21" font-size="12" stroke="none" fill="#000">Agent mode</text>
|
||||
<polyline points="100,14 106,20 112,14" fill="none" />
|
||||
</g>
|
||||
<g transform="translate(432,104)">
|
||||
<rect width="192" height="32" rx="16" />
|
||||
<text x="96" y="21" font-size="12" text-anchor="middle" stroke="none" fill="#000">Senior Engineer Fable</text>
|
||||
</g>
|
||||
<g transform="translate(640,104)">
|
||||
<rect width="128" height="32" rx="4" fill="#000" />
|
||||
<text x="64" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#fff">Send</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- wake hint -->
|
||||
<text x="48" y="676" font-size="12" stroke="none" fill="#666">Sending a reply wakes the agent now — before the scheduled check.</text>
|
||||
|
||||
<!-- annotations -->
|
||||
<g data-region="annotations">
|
||||
<rect x="40" y="400" width="800" height="80" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(32,392)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
</g>
|
||||
<rect x="40" y="660" width="472" height="24" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(32,656)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">2</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,121 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- 03 · Properties pane — Monitor row redesign (component sheet). Desktop 1280×800 -->
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<text x="48" y="56" font-size="20" font-weight="600" stroke="none" fill="#000">Properties pane — Monitor row redesign</text>
|
||||
<text x="48" y="80" font-size="12" stroke="none" fill="#666">Side pane fragment, 352px wide. The same row is reused in the mobile properties sheet.</text>
|
||||
|
||||
<!-- ===== Panel A · current ===== -->
|
||||
<text x="48" y="128" font-size="14" font-weight="600" stroke="none" fill="#000">A · Current — cramped, truncated</text>
|
||||
<g transform="translate(48,144)">
|
||||
<rect width="352" height="176" rx="8" />
|
||||
<text x="16" y="44" font-size="12" stroke="none" fill="#666">Approvers</text>
|
||||
<text x="144" y="44" font-size="14" stroke="none" fill="#666">None</text>
|
||||
|
||||
<text x="16" y="84" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="80" r="7" />
|
||||
<line x1="152" y1="80" x2="152" y2="75" />
|
||||
<line x1="152" y1="80" x2="156" y2="80" />
|
||||
<text x="168" y="84" font-size="14" stroke="none" fill="#000">Ne…</text>
|
||||
<text x="208" y="84" font-size="12" stroke="none" fill="#666">Jul 17, 2026</text>
|
||||
<text x="288" y="76" font-size="12" stroke="none" fill="#666">Attempt</text>
|
||||
<text x="288" y="92" font-size="12" stroke="none" fill="#666">1</text>
|
||||
|
||||
<text x="16" y="124" font-size="12" stroke="none" fill="#666">Watchdog</text>
|
||||
<text x="144" y="124" font-size="14" stroke="none" fill="#666">None</text>
|
||||
</g>
|
||||
<text x="48" y="352" font-size="12" stroke="none" fill="#666">Label truncates to “Ne…”, date is ambiguous (which year matters,</text>
|
||||
<text x="48" y="368" font-size="12" stroke="none" fill="#666">not the time?), attempt badge wraps into two lines.</text>
|
||||
|
||||
<!-- ===== Panel B · proposed collapsed row ===== -->
|
||||
<text x="464" y="128" font-size="14" font-weight="600" stroke="none" fill="#000">B · Proposed — two-line row</text>
|
||||
<g transform="translate(464,144)">
|
||||
<rect width="352" height="176" rx="8" />
|
||||
<text x="16" y="44" font-size="12" stroke="none" fill="#666">Approvers</text>
|
||||
<text x="144" y="44" font-size="14" stroke="none" fill="#666">None</text>
|
||||
|
||||
<text x="16" y="84" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="80" r="7" />
|
||||
<line x1="152" y1="80" x2="152" y2="75" />
|
||||
<line x1="152" y1="80" x2="156" y2="80" />
|
||||
<text x="168" y="84" font-size="14" font-weight="600" stroke="none" fill="#000">In 2h 12m</text>
|
||||
<text x="168" y="104" font-size="12" stroke="none" fill="#666">Jul 17, 4:08 PM · Attempt 1</text>
|
||||
|
||||
<text x="16" y="144" font-size="12" stroke="none" fill="#666">Watchdog</text>
|
||||
<text x="144" y="144" font-size="14" stroke="none" fill="#666">None</text>
|
||||
</g>
|
||||
<text x="464" y="352" font-size="12" stroke="none" fill="#666">Relative time leads and is bold; absolute time + attempt get their</text>
|
||||
<text x="464" y="368" font-size="12" stroke="none" fill="#666">own muted line. Nothing truncates at 352px. Row grows to 2 lines.</text>
|
||||
|
||||
<!-- ===== Panel C · hover tooltip ===== -->
|
||||
<text x="880" y="128" font-size="14" font-weight="600" stroke="none" fill="#000">C · Hover / tap — full details</text>
|
||||
<g transform="translate(880,144)">
|
||||
<!-- pane fragment with the row -->
|
||||
<rect width="352" height="80" rx="8" />
|
||||
<text x="16" y="44" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="40" r="7" />
|
||||
<line x1="152" y1="40" x2="152" y2="35" />
|
||||
<line x1="152" y1="40" x2="156" y2="40" />
|
||||
<text x="168" y="44" font-size="14" font-weight="600" stroke="none" fill="#000">In 2h 12m</text>
|
||||
<text x="168" y="64" font-size="12" stroke="none" fill="#666">Jul 17, 4:08 PM · Attempt 1</text>
|
||||
|
||||
<!-- tooltip card -->
|
||||
<polygon points="180,96 188,88 196,96" fill="#fff" />
|
||||
<g transform="translate(16,96)">
|
||||
<rect width="320" height="368" rx="8" />
|
||||
<text x="16" y="32" font-size="14" font-weight="600" stroke="none" fill="#000">Monitor</text>
|
||||
<text x="304" y="32" font-size="12" text-anchor="end" stroke="none" fill="#666">Attempt 1</text>
|
||||
<line x1="0" y1="48" x2="320" y2="48" />
|
||||
|
||||
<text x="16" y="76" font-size="12" stroke="none" fill="#666">Next check</text>
|
||||
<text x="16" y="96" font-size="14" stroke="none" fill="#000">Thu, Jul 17, 2026 · 4:08 PM PDT</text>
|
||||
<text x="16" y="114" font-size="12" stroke="none" fill="#666">in 2h 12m</text>
|
||||
|
||||
<text x="16" y="144" font-size="12" stroke="none" fill="#666">Watching</text>
|
||||
<text x="16" y="164" font-size="14" stroke="none" fill="#000">vercel-deploy</text>
|
||||
|
||||
<text x="16" y="192" font-size="12" stroke="none" fill="#666">Notes</text>
|
||||
<text x="16" y="212" font-size="14" stroke="none" fill="#000">Verify completion, commit ledger /</text>
|
||||
<text x="16" y="230" font-size="14" stroke="none" fill="#000">scores, extend comparison site.</text>
|
||||
|
||||
<text x="16" y="260" font-size="12" stroke="none" fill="#666">Last triggered</text>
|
||||
<text x="16" y="280" font-size="14" stroke="none" fill="#000">— not yet triggered</text>
|
||||
|
||||
<line x1="0" y1="300" x2="320" y2="300" />
|
||||
<g transform="translate(16,316)">
|
||||
<rect width="96" height="32" rx="4" />
|
||||
<text x="48" y="21" font-size="12" text-anchor="middle" stroke="none" fill="#000">Check now</text>
|
||||
</g>
|
||||
<g transform="translate(120,316)">
|
||||
<rect width="56" height="32" rx="4" />
|
||||
<text x="28" y="21" font-size="12" text-anchor="middle" stroke="none" fill="#000">Edit</text>
|
||||
</g>
|
||||
<g transform="translate(184,316)">
|
||||
<rect width="64" height="32" rx="4" />
|
||||
<text x="32" y="21" font-size="12" text-anchor="middle" stroke="none" fill="#000">Clear</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<text x="880" y="656" font-size="12" stroke="none" fill="#666">Hover on desktop, tap on mobile. Exact timestamp with timezone,</text>
|
||||
<text x="880" y="672" font-size="12" stroke="none" fill="#666">service, notes and quick actions — no digging into the edit popover.</text>
|
||||
|
||||
<!-- annotations -->
|
||||
<g data-region="annotations">
|
||||
<rect x="200" y="204" width="152" height="36" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(192,200)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
</g>
|
||||
<rect x="608" y="208" width="216" height="52" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(600,204)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">2</text>
|
||||
</g>
|
||||
<rect x="888" y="232" width="344" height="384" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
<g transform="translate(880,228)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">3</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.3 KiB |
@@ -1,103 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- 04 · Monitor row — state matrix. Desktop 1280×800 -->
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<text x="48" y="56" font-size="20" font-weight="600" stroke="none" fill="#000">Monitor row — state matrix</text>
|
||||
<text x="48" y="80" font-size="12" stroke="none" fill="#666">One copy system across banner, composer strip and properties row. Relative time always leads.</text>
|
||||
|
||||
<!-- 1 · scheduled, soon -->
|
||||
<g transform="translate(48,112)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">SCHEDULED · UNDER 1 HOUR</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="68" r="7" />
|
||||
<line x1="152" y1="68" x2="152" y2="63" />
|
||||
<line x1="152" y1="68" x2="156" y2="68" />
|
||||
<text x="168" y="72" font-size="14" font-weight="600" stroke="none" fill="#000">In 24m</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">Today, 2:20 PM</text>
|
||||
</g>
|
||||
|
||||
<!-- 2 · scheduled, today -->
|
||||
<g transform="translate(656,112)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">SCHEDULED · UNDER 24 HOURS</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="68" r="7" />
|
||||
<line x1="152" y1="68" x2="152" y2="63" />
|
||||
<line x1="152" y1="68" x2="156" y2="68" />
|
||||
<text x="168" y="72" font-size="14" font-weight="600" stroke="none" fill="#000">In 2h 12m</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">Today, 4:08 PM</text>
|
||||
</g>
|
||||
|
||||
<!-- 3 · scheduled, future day -->
|
||||
<g transform="translate(48,248)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">SCHEDULED · BEYOND TODAY</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="68" r="7" />
|
||||
<line x1="152" y1="68" x2="152" y2="63" />
|
||||
<line x1="152" y1="68" x2="156" y2="68" />
|
||||
<text x="168" y="72" font-size="14" font-weight="600" stroke="none" fill="#000">In 3d</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">Mon Jul 20, 9:00 AM</text>
|
||||
</g>
|
||||
|
||||
<!-- 4 · retrying -->
|
||||
<g transform="translate(656,248)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">RETRYING · ATTEMPT > 1</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="68" r="7" />
|
||||
<line x1="152" y1="68" x2="152" y2="63" />
|
||||
<line x1="152" y1="68" x2="156" y2="68" />
|
||||
<text x="168" y="72" font-size="14" font-weight="600" stroke="none" fill="#000">In 2h 12m</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">Today, 4:08 PM · </text>
|
||||
<text x="272" y="92" font-size="12" font-weight="700" stroke="none" fill="#000">Attempt 3</text>
|
||||
</g>
|
||||
|
||||
<!-- 5 · due now -->
|
||||
<g transform="translate(48,384)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">DUE NOW</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="68" r="7" fill="#e6e6e6" />
|
||||
<line x1="152" y1="68" x2="152" y2="63" />
|
||||
<line x1="152" y1="68" x2="156" y2="68" />
|
||||
<text x="168" y="72" font-size="14" font-weight="600" stroke="none" fill="#000">Due now</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">checking momentarily…</text>
|
||||
</g>
|
||||
|
||||
<!-- 6 · overdue -->
|
||||
<g transform="translate(656,384)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">OVERDUE</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<circle cx="152" cy="68" r="7" fill="#e6e6e6" />
|
||||
<line x1="152" y1="68" x2="152" y2="63" />
|
||||
<line x1="152" y1="68" x2="156" y2="68" />
|
||||
<text x="168" y="72" font-size="14" font-weight="600" stroke="none" fill="#000">Overdue by 18m</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">Jul 17, 1:38 PM · fires on next tick</text>
|
||||
</g>
|
||||
|
||||
<!-- 7 · cleared -->
|
||||
<g transform="translate(48,520)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">CLEARED</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<text x="168" y="72" font-size="14" stroke="none" fill="#666">Cleared</text>
|
||||
<text x="168" y="92" font-size="12" stroke="none" fill="#666">2h ago · after attempt 2</text>
|
||||
</g>
|
||||
|
||||
<!-- 8 · none -->
|
||||
<g transform="translate(656,520)">
|
||||
<rect width="576" height="112" rx="8" />
|
||||
<text x="16" y="28" font-size="12" stroke="none" fill="#666">NONE</text>
|
||||
<text x="16" y="72" font-size="12" stroke="none" fill="#666">Monitor</text>
|
||||
<text x="144" y="72" font-size="14" stroke="none" fill="#666">None</text>
|
||||
</g>
|
||||
|
||||
<!-- format rules -->
|
||||
<text x="48" y="688" font-size="14" font-weight="600" stroke="none" fill="#000">Copy rules</text>
|
||||
<text x="48" y="712" font-size="12" stroke="none" fill="#666">< 1h → “In 24m” · < 24h → “In 2h 12m” (two units, not “in 2h”) · beyond → “In 3d” + weekday date · past due → “Overdue by …”.</text>
|
||||
<text x="48" y="732" font-size="12" stroke="none" fill="#666">Absolute time says “Today” when it is today; adds the year only when it is not the current year. Banner + strip render only in scheduled / due / overdue states.</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
@@ -1,65 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="304" viewBox="0 0 1280 304"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- Flow — the three surfaces that show the same monitor state -->
|
||||
<rect x="0" y="0" width="1280" height="304" />
|
||||
<text x="48" y="40" font-size="14" font-weight="600" stroke="none" fill="#000">One monitor state, three surfaces — banner (top), strip (composer), row (properties) — all fed by the same copy rules (04).</text>
|
||||
|
||||
<!-- 01 thumbnail: page top with banner -->
|
||||
<g transform="translate(48,72)">
|
||||
<rect width="240" height="160" rx="4" />
|
||||
<line x1="0" y1="24" x2="240" y2="24" />
|
||||
<rect x="16" y="40" width="144" height="8" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="16" y="60" width="208" height="32" rx="4" fill="#e6e6e6" stroke="none" />
|
||||
<circle cx="32" cy="76" r="8" fill="#fff" />
|
||||
<line x1="16" y1="112" x2="224" y2="112" stroke="#666" />
|
||||
<line x1="16" y1="128" x2="224" y2="128" stroke="#666" />
|
||||
<line x1="16" y1="144" x2="160" y2="144" stroke="#666" />
|
||||
</g>
|
||||
<text x="168" y="264" font-size="12" text-anchor="middle" stroke="none" fill="#000">01 · Task page top — banner</text>
|
||||
|
||||
<line x1="296" y1="152" x2="336" y2="152" />
|
||||
<polygon points="336,152 326,146 326,158" fill="#000" stroke="none" />
|
||||
|
||||
<!-- 02 thumbnail: strip + composer -->
|
||||
<g transform="translate(344,72)">
|
||||
<rect width="240" height="160" rx="4" />
|
||||
<line x1="16" y1="24" x2="224" y2="24" stroke="#666" />
|
||||
<line x1="16" y1="40" x2="224" y2="40" stroke="#666" />
|
||||
<line x1="16" y1="56" x2="160" y2="56" stroke="#666" />
|
||||
<rect x="16" y="80" width="208" height="24" rx="4" fill="#e6e6e6" stroke="none" />
|
||||
<circle cx="30" cy="92" r="6" fill="#fff" />
|
||||
<rect x="16" y="112" width="208" height="32" rx="4" />
|
||||
</g>
|
||||
<text x="464" y="264" font-size="12" text-anchor="middle" stroke="none" fill="#000">02 · Above the composer — strip</text>
|
||||
|
||||
<line x1="592" y1="152" x2="632" y2="152" />
|
||||
<polygon points="632,152 622,146 622,158" fill="#000" stroke="none" />
|
||||
|
||||
<!-- 03 thumbnail: properties row + tooltip -->
|
||||
<g transform="translate(640,72)">
|
||||
<rect width="240" height="160" rx="4" />
|
||||
<rect x="16" y="16" width="64" height="8" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="120" y="16" width="104" height="8" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="16" y="40" width="64" height="8" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="120" y="36" width="80" height="8" fill="#000" stroke="none" />
|
||||
<rect x="120" y="50" width="104" height="6" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="120" y="72" width="104" height="72" rx="4" />
|
||||
<rect x="132" y="84" width="80" height="6" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="132" y="98" width="80" height="6" fill="#e6e6e6" stroke="none" />
|
||||
<rect x="132" y="112" width="56" height="6" fill="#e6e6e6" stroke="none" />
|
||||
</g>
|
||||
<text x="760" y="264" font-size="12" text-anchor="middle" stroke="none" fill="#000">03 · Properties row + tooltip</text>
|
||||
|
||||
<line x1="888" y1="152" x2="928" y2="152" />
|
||||
<polygon points="928,152 918,146 918,158" fill="#000" stroke="none" />
|
||||
|
||||
<!-- 04 thumbnail: states grid -->
|
||||
<g transform="translate(936,72)">
|
||||
<rect width="240" height="160" rx="4" />
|
||||
<rect x="16" y="16" width="96" height="56" rx="4" />
|
||||
<rect x="128" y="16" width="96" height="56" rx="4" />
|
||||
<rect x="16" y="88" width="96" height="56" rx="4" />
|
||||
<rect x="128" y="88" width="96" height="56" rx="4" />
|
||||
</g>
|
||||
<text x="1056" y="264" font-size="12" text-anchor="middle" stroke="none" fill="#000">04 · State matrix + copy rules</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -36,6 +36,13 @@ secret redaction still run on the bounded result set.
|
||||
Agent detail links fetch an older selected run by ID when it is outside the
|
||||
history page, and verify that it belongs to the current company and agent.
|
||||
|
||||
Saved-run policy and parent-report lookups read only the `executionPolicy`
|
||||
value or the legacy `issueId` and `taskId` values from a run context. PostgreSQL projects
|
||||
those JSON values before it sends the row to the controller. Unused prompt and
|
||||
launch context does not travel with each policy or parent-report check. JSON
|
||||
types, task-key precedence, and company and agent identity checks are preserved.
|
||||
Each decision still reads current data; this is not an authorization cache.
|
||||
|
||||
To compare the real summary and full history queries with an earlier revision:
|
||||
|
||||
```sh
|
||||
@@ -45,6 +52,30 @@ pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-run-history.ts -
|
||||
The fixture has 1,400 synthetic runs with 192 KiB contexts. Both queries return
|
||||
the same 200 complete rows. This benchmark excludes HTTP and redaction latency.
|
||||
|
||||
Workspace reads also compare retained task IDs as guarded, canonical UUIDs.
|
||||
This lets PostgreSQL check each source task by primary key before it evaluates
|
||||
task privacy. Casting the indexed task ID to text can instead evaluate privacy
|
||||
across company history for each workspace. Repository admission repeats these
|
||||
reads while holding its company provenance lock, so that plan can delay other
|
||||
agent launches. Missing, malformed, noncanonical, and foreign-company source
|
||||
IDs still deny access.
|
||||
|
||||
The subsequent source-task enumeration also keeps `issues.id` as a UUID. Its
|
||||
OR predicate combines the workspace binding index with the task primary key.
|
||||
Casting that ID to text forces another scan of company history even after the
|
||||
workspace privacy query has passed. The workspace authorization regression
|
||||
suite checks both plans and verifies the attached and retained source results.
|
||||
|
||||
To compare this query on a disposable fixture with 13,429 tasks:
|
||||
|
||||
```sh
|
||||
pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-workspace-authorization.ts --baseline-ref=5c7a025032
|
||||
```
|
||||
|
||||
The benchmark loads the baseline authorization module with current dependencies,
|
||||
compares complete workspace rows, and prints five warm samples and query plans.
|
||||
It measures SQL execution, not the full repository admission or agent startup.
|
||||
|
||||
## 1. Embedded PostgreSQL — zero config
|
||||
|
||||
If you don't set `DATABASE_URL`, the server automatically starts an embedded PostgreSQL instance and manages a local data directory.
|
||||
@@ -851,6 +882,8 @@ Migration [0337_exotic_proemial_gods.sql](../packages/db/src/migrations/0337_exo
|
||||
|
||||
Migration [0338_absurd_vengeance.sql](../packages/db/src/migrations/0338_absurd_vengeance.sql) adds internal `execution_workspace_repository_access` provenance. Company/canonical-root source entries retain execution-workspace and source-project identities; reader entries retain the exact admitted run, task, agent and responsible user. New repository receipts also record their root at creation; legacy receipts receive no inferred origin. Shared project-source entries retain which physical owners require workspace write authority. Current admission, selection, requests and publication check the current task’s writer against those owners; retained past readers and file APIs still require only read authority. Genuine isolated copies retain source read access without requiring shared-folder write access. An existing linked worktree can recover a missing isolated-copy proof only after Git registration and its captured source owner are verified. Configured local sibling copies retain deny-only task/project source ownership on the bound task root before materialization; ordinary repository clones create no isolation proof. The shared root fence checks every reader against every source before admission and future publication, including tasks without repository requests. File APIs apply the same source privacy without writing provenance. These entries grant no authority and have no workspace or run deletion cascade; unavailable retained identity fails closed. Automatic provenance release is deferred, so a newly incompatible permission scope requires a separate physical root.
|
||||
|
||||
Repository admission loads retained workspace and task sources in bounded batches. Readers share source rows and privacy evaluation for identical principals, but each retained run receives its own trust and responsible-user decision. The snapshot lasts only for one read batch; later admission and publication reload current authority. Keep source-loading query counts bounded as both workspace history and retained reader counts grow.
|
||||
|
||||
Git-source capture checks the actual common Git storage directory and verified
|
||||
checkout owners. Separately stored metadata does not make its parent a checkout.
|
||||
When a linked worktree cannot identify its primary checkout, the service verifies
|
||||
|
||||
@@ -49,6 +49,15 @@ workflow reads GitHub metadata without checking out or executing PR code. Its au
|
||||
repository, head, and merge validation still decide whether tests can use AWS.
|
||||
Unapproved test workloads continue to use GitHub-hosted runners.
|
||||
|
||||
The routing workflow also selects the CI scope. Middle PRs in a stack and PRs
|
||||
whose diff changes only release notes (`releases/v*.md` or
|
||||
`releases/beta/v*.md`) skip the build, test, and e2e lanes. The `policy` job
|
||||
and the `verify` and `e2e` aggregate checks still run. The release-notes check
|
||||
reads the diff between the event's base and head commits. If that lookup
|
||||
fails, or the diff is empty or may be truncated, the PR runs full CI. A
|
||||
notes-only PR stacked above other PRs also runs full CI, because its own diff
|
||||
does not include the lower PRs' code.
|
||||
|
||||
The routing Fleet has two warm On-Demand runners and a four-runner ceiling.
|
||||
Test fleets cannot consume its runner slots. Hosted test queues therefore do
|
||||
not block AWS runner selection. A manual dispatch of `ci-routing.yml` on master
|
||||
@@ -670,6 +679,18 @@ Activity-only reorderings wait for one second without further activity changes;
|
||||
new and removed tasks appear immediately. Titles, status, and live indicators stay
|
||||
current during that delay.
|
||||
|
||||
### Local Linux runner qualification
|
||||
|
||||
Before serving requests, the server reads `getconf GNU_LIBC_VERSION` once with
|
||||
an asynchronous subprocess, a one-second timeout, and a 1 KiB output limit.
|
||||
Runner discovery reuses those immutable platform facts. This check does not
|
||||
generate a Node diagnostic report or resolve socket hostnames.
|
||||
|
||||
If `getconf` is missing, fails, times out, or returns an unrecognized version,
|
||||
the local libc remains unknown and automatic GNU/Linux runner selection stays
|
||||
disabled. Qualified Linux targets still require GNU libc 2.39 or newer. Existing
|
||||
saved execution choices remain unchanged.
|
||||
|
||||
### OpenCode native runtime
|
||||
|
||||
Paperclip Runner resolves the pinned OpenCode platform package installed with
|
||||
@@ -1065,6 +1086,20 @@ Workspace preparation resolves an existing root symlink before reading the snaps
|
||||
|
||||
Execution-workspace close-readiness and branch-reconciliation status checks allow up to 32 MiB of buffered output. This accommodates nested task worktrees while preserving exact untracked-file counts. Larger output fails the scan and blocks destructive cleanup. Other buffered scan bounds stay unchanged.
|
||||
|
||||
Heartbeat configuration, environment selection, and finalization read persisted
|
||||
workspace records through the company-scoped `getStoredById` service method.
|
||||
Those reads do not inspect Git, refresh runtime service health, or resolve PR
|
||||
delivery. Workspace detail responses still load live display state, and the
|
||||
execution and cleanup boundaries retain their live branch and cleanliness
|
||||
guards. Close readiness derives ancestry from its existing ahead/behind
|
||||
comparison, avoiding a second history walk with `merge-base`.
|
||||
|
||||
Measure the avoided reads on synthetic workspaces with
|
||||
`pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-workspace-record-reads.ts`.
|
||||
The benchmark checks identical persisted fields and reports wall time and actual
|
||||
Git command counts. Use `--resident-mib=1536` to exercise a large Node process on
|
||||
Linux. It measures workspace reads, not end-to-end agent latency.
|
||||
|
||||
The cache intentionally trades up to a few seconds of changed-file freshness for stable server latency. The file browser retains an explicit refresh action, does not start its query while the panel or browser tab is hidden, and presents overloads as retryable failures rather than an empty workspace. A full queue returns `503` with code `workspace_git_scan_saturated`; a scan exceeding its wall-clock limit returns `504` with code `workspace_git_scan_timeout`. Both responses include `Retry-After: 1`.
|
||||
|
||||
Sandbox Git sync treats only the selected repository root as a clone source. A selected subfolder uses directory sync within that folder, applies the enclosing repository's ignore rules, and does not transfer parent files or Git history.
|
||||
@@ -1417,6 +1452,10 @@ as they are available, so later setup failures still clean up acquired resources
|
||||
Busy-account deferral returns explicitly before session/workspace preparation.
|
||||
Public-path tests are in `server/src/__tests__/heartbeat-execution-configuration.test.ts`.
|
||||
|
||||
Browser Use skill assignment first checks for an enabled, active Browser Use
|
||||
connection in the run's company. With none, it skips full tool discovery.
|
||||
With one, the existing task, tool-policy and credential-grant checks still apply.
|
||||
|
||||
Session preparation is in `server/src/services/heartbeat/session-preparation.ts`.
|
||||
It selects fresh or resumable sessions from effective config and managed-account
|
||||
identity, then resolves adapter session identity and compaction after workspace
|
||||
@@ -1803,6 +1842,21 @@ that classification finishes.
|
||||
waits for the previous executor to finish releasing its task resources.
|
||||
- A verified dead runner starts a replacement from the same durable root and
|
||||
resumes the same provider checkpoint. Only the operating-system PID changes.
|
||||
- A prepared remote workspace interrupted before the executor's first claim
|
||||
can continue on the same run without a provider checkpoint. Under the recovery
|
||||
locks, Paperclip requires attempt and controller generation zero, no prior
|
||||
controller or lease owner, and no session, process, launch, or provider evidence.
|
||||
Any existing durable runner root must still prove an untouched bootstrap before
|
||||
execution starts. A prior claim or contradictory evidence retains the hold.
|
||||
A server-owned preparation protocol marker is required. It is recorded only
|
||||
when fresh native identities are created by a server with the ownership fence.
|
||||
Older unmarked preparations remain held during rolling upgrades; revisiting an
|
||||
existing run never upgrades that marker from caller-supplied profile data.
|
||||
- If the original preparation resumes after another execution claimed the run,
|
||||
it yields without failing the shared run or cleaning up the new owner. An
|
||||
expired foreign lease still requires normal recovery before takeover.
|
||||
Recovery leaves this server's active preparation with its current execution.
|
||||
A detached execution also preserves the current owner's run-scoped gateway tokens.
|
||||
- A runner that died before its first authenticated connection can restart on
|
||||
the same run only when its durable root proves that no provider authority or
|
||||
checkpoint exists. Paperclip quarantines the incomplete root first.
|
||||
@@ -1874,6 +1928,10 @@ automatically alongside it; see [Agent commentary](agent-commentary.md).
|
||||
Other runtime skills remain controlled by
|
||||
`paperclipSkillSync.desiredSkills`. The native `paperclip_runner` does not use
|
||||
this legacy default because its protocol supplies the control-plane contract.
|
||||
An empty native runtime selection returns before company skill inventory work.
|
||||
An explicit empty stored key set does the same. Nonempty selections still use
|
||||
the current inventory, version and access checks. Legacy automatic selections
|
||||
still resolve their operational and feedback skills.
|
||||
|
||||
Validate the catalog without writing the manifest:
|
||||
|
||||
@@ -2021,9 +2079,16 @@ Environment overrides:
|
||||
- `PAPERCLIP_WORKSPACE_REAPER_COOLDOWN_DAYS=<days>` sets how long the
|
||||
terminal-workspace reaper waits after an issue tree becomes terminal before it
|
||||
archives the execution workspace and deletes the worktree. A person can reopen
|
||||
the work inside this window. The default is `7`. A value of `0` disables the
|
||||
the work inside this window. The default is `1`. A value of `0` disables the
|
||||
cooldown and restores immediate reaping. A negative or non-numeric value falls
|
||||
back to the default.
|
||||
- `PAPERCLIP_WORKSPACE_COMPACTION_DELAY_HOURS=<hours>` sets how long the reaper
|
||||
waits after an issue tree becomes terminal before it removes regenerable files
|
||||
from the workspace (see "Terminal workspace compaction" below). The default is
|
||||
`1`. A value of `0` compacts on the next sweep. A negative, empty, or
|
||||
non-numeric value falls back to the default.
|
||||
- `PAPERCLIP_WORKSPACE_COMPACTION_ENABLED=false` turns compaction off. Any other
|
||||
value keeps it on.
|
||||
|
||||
Without `PAPERCLIP_DB_BACKUP_ALERT_FILE`, health checks look for
|
||||
`db-backup-to-s3.failure` in the backup directory, beside the backup directory,
|
||||
@@ -2333,6 +2398,57 @@ fields before disabling a connection. Verify workspace ownership, active runs,
|
||||
Git state, and runtime-service readiness before closing a workspace. A missing
|
||||
URL or old workspace timestamp alone does not prove that a row is disposable.
|
||||
|
||||
### Terminal workspace compaction
|
||||
|
||||
A finished task often keeps its worktree for a long time: the reaper archives a
|
||||
worktree only when its delivery is merged and its files are clean, and then only
|
||||
after the cooldown. Most of a worktree's size is installed dependencies and build
|
||||
output, not work. Compaction removes those files and keeps everything else, so a
|
||||
finished task does not hold gigabytes while it waits for the reaper (or forever,
|
||||
when it has no merged pull request or has leftover files).
|
||||
|
||||
The reaper compacts a workspace once per terminal cycle, after
|
||||
`PAPERCLIP_WORKSPACE_COMPACTION_DELAY_HOURS`. Server startup waits for the
|
||||
reaper's first sweep, so that sweep never compacts; later sweeps do. A workspace
|
||||
qualifies when all of these hold:
|
||||
|
||||
- the workspace is an isolated linked Git worktree (never a shared workspace or a
|
||||
project's main checkout);
|
||||
- every issue in its tree is done or cancelled;
|
||||
- no queued or running heartbeat run works on the tree or the workspace;
|
||||
- no runtime service runs from the workspace.
|
||||
|
||||
Compaction removes only directories that have a regenerable name
|
||||
(`node_modules`, `dist`, `build`, `coverage`, `storybook-static`, `target`,
|
||||
`.cache`, `.next`, `.nuxt`, `.parcel-cache`, `.svelte-kit`, `.turbo`, `.vite`),
|
||||
that the repository's own `.gitignore` files ignore (a negated pattern, a global
|
||||
excludes file, or `.git/info/exclude` does not count), and that hold no tracked
|
||||
file. A directory with such a name
|
||||
that does not qualify (for example a tracked `build/`) is searched in turn, so
|
||||
ignored dependencies inside it still go. It never enters `.git` or
|
||||
`.paperclip`, and it never follows a symlink. It checks each directory's ignore
|
||||
status on its own, so dependencies below an ignored parent (for example
|
||||
`vendor/tool/node_modules`) are found. Its Git reads have a deadline and stop
|
||||
when the sweep stops. Tracked files, uncommitted changes,
|
||||
untracked files, other ignored files (such as `.env` or local data), the branch,
|
||||
and its commits all stay. The workspace row stays active.
|
||||
|
||||
The removal is a rename into `.paperclip-compaction-trash` beside the worktree,
|
||||
made while the reaper holds the workspace lifecycle lock. The reaper checks the
|
||||
conditions above before the rename and again after it, in the same transaction.
|
||||
If the task reopened or a run started in between, the directories move back.
|
||||
Otherwise the trash is deleted. Trash that an interrupted compaction or a failed
|
||||
delete leaves behind is deleted by a later sweep once it is more than an hour
|
||||
old. The sweep checks each worktree parent directory at most every 30 minutes,
|
||||
whether or not anything is left to compact.
|
||||
|
||||
A compacted worktree is in the same state as a freshly created one, so when the
|
||||
task resumes, whatever produced those files the first time produces them again:
|
||||
reuse runs the workspace provision command (or `scripts/provision-worktree.sh`),
|
||||
which reinstalls dependencies when `node_modules` is missing. Repositories without
|
||||
a provision command rely on the agent's own install step, as they do for a new
|
||||
worktree.
|
||||
|
||||
### Browser realtime connection recovery
|
||||
|
||||
If a browser cannot construct a WebSocket, the live-update and run transcript
|
||||
|
||||
@@ -36,6 +36,18 @@ delivery path. Managed gateway discovery excludes historical native assignments.
|
||||
Their stored rows remain available for existing runs. Explicit company gateways
|
||||
continue to use their configured scopes.
|
||||
|
||||
## Client-side tool-call deadline
|
||||
|
||||
The gateway bounds each provider call, but the agent's MCP client also needs a
|
||||
bound. Without one, a gateway request that never answers holds the agent turn
|
||||
until the adapter execution timeout ends the run. Claude's default MCP tool
|
||||
timeout is far longer than a run, and ACP `mcpServers` entries have no
|
||||
per-server timeout field. So when an ACP Claude run receives runtime MCP
|
||||
servers, the ACPX engine sets `MCP_TOOL_TIMEOUT` to 15 minutes. A call that
|
||||
passes this deadline returns an MCP error to the model, and the turn
|
||||
continues. An `MCP_TOOL_TIMEOUT` value in the adapter `env` configuration
|
||||
takes priority.
|
||||
|
||||
## Metrics
|
||||
|
||||
The board runtime health API summarizes one-hour event windows plus current durable slot state:
|
||||
|
||||
@@ -111,10 +111,12 @@ default branch marked. People can also enter a branch name that is not listed.
|
||||
Saving checks GitHub access and the selected ref before changing tracking; failed
|
||||
saves keep the selection and installed content. Project refs remain owned by project
|
||||
configuration so changing a source cannot redirect another project's tracking.
|
||||
If a tracked repository grows beyond the supported package size, its installed
|
||||
repository skills and shared dependencies remain available until a safe refresh
|
||||
can succeed. Initial oversized imports can use self-contained skill packages;
|
||||
packages with omitted dependencies are invalid and are not installed.
|
||||
If a repository exceeds the supported package size, Paperclip first packages its
|
||||
skill folders and referenced repository files, preserving their original paths.
|
||||
Missing references are advisory: a valid selected skill can still be imported.
|
||||
Unavailable files are listed, and existing installed dependencies are retained
|
||||
when a bounded refresh cannot preserve them. File limits and content validation
|
||||
still apply; importing never executes repository code.
|
||||
|
||||
Project-derived skills inherit current project access. Access through any originating
|
||||
project is sufficient, including the responsible-user restrictions on agent execution.
|
||||
|
||||
@@ -1355,6 +1355,8 @@ The current app also exposes V1-supporting surfaces for:
|
||||
derive `deliveryState` as `merged_via_pr | merged_by_ancestry | unmerged | unknown`; terminal issue trees with a
|
||||
merged delivery and no active checkout run become cleanup-eligible with reason `issue_terminal` and are archived
|
||||
through the workspace cleanup path. Reopening the source issue records activity but does not restore that workspace.
|
||||
Before that, terminal isolated worktrees are compacted: ignored, untracked dependency, build, and cache directories
|
||||
are removed while the workspace, its branch, and all source changes stay, and reuse regenerates them.
|
||||
- task watchdog configuration and reusable watchdog issue orchestration for explicitly watched issue subtrees
|
||||
- routines and scheduled/API/webhook triggers
|
||||
- plugin installation, configuration, state, jobs, logs, webhooks, and plugin database namespace migration
|
||||
@@ -2108,11 +2110,15 @@ unavailable. Preserve current ownership and newer-work fences. See
|
||||
be paused independently of selection. Project sources select future additions unless
|
||||
excluded; existing manually imported sources retain their selections. Legacy projects
|
||||
adopt through an explicit company-scoped, project-authorized endpoint, never a GET.
|
||||
- Repository size fallback must not discard installed shared dependencies or
|
||||
publish packages with detected omitted dependencies. Retain affected versions
|
||||
and report the size problem. Self-contained packages can still update through
|
||||
fallback. Project sources retry complete repository packaging and return to it
|
||||
when supported; selections and exclusions remain unchanged.
|
||||
- Repository size fallback first retains skill folders and transitively referenced
|
||||
repository files within the existing file/byte limits, preserving original paths.
|
||||
If that set is still too large, bounded skill folders remain importable. Missing
|
||||
references are advisory and do not invalidate otherwise valid selected skills.
|
||||
Web routes and inline Markdown examples are not package dependencies. Preserve
|
||||
installed versions if fallback would omit their previously installed shared files.
|
||||
Project sources retry complete packaging; selections and exclusions remain unchanged.
|
||||
Refresh reinspects cached dependency-rejection errors once even at an unchanged
|
||||
upstream commit. Content validation and company/project access remain enforced.
|
||||
- Unattended refresh stores setup identity and connection/grant identifiers, never token
|
||||
values. Current membership, connection authorization and skill policy are revalidated.
|
||||
Unrecoverable legacy setup identities require explicit connection repair. Retries are
|
||||
|
||||
@@ -722,6 +722,9 @@ the runtime. A declarative manifest can suggest public entrypoints and setup req
|
||||
new imports keep repository files together and select all discovered skills. Missing
|
||||
references, setup problems and omitted files produce persistent warnings. Files rejected
|
||||
for unsafe execution or secret disclosure are excluded without blocking other files.
|
||||
Oversized repositories use a bounded package of skill folders and referenced files
|
||||
with their original paths. Missing references do not block a valid selected skill;
|
||||
fallback preserves previously installed shared dependencies or retains the old version.
|
||||
The installed file viewer exposes saved repository dependencies. Importing never runs
|
||||
setup or configures host tools. New
|
||||
upstream skills require reviewed selection; removed or deselected skills remain
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Dependency wakes and deliberate stops
|
||||
|
||||
A task can remain blocked after all recorded dependencies finish. Its assignee
|
||||
may be waiting for an external action. Blocking its own task does not request
|
||||
another dependency wake. The periodic recovery sweep preserves this decision,
|
||||
including native status decisions attributed to the assignee's bound run.
|
||||
|
||||
Title, monitor, and other unrelated edits do not change this wait. A later
|
||||
assignment, status, or blocker-set update permits reconciliation again. A blocker
|
||||
that completes or finishes workspace export after the wait also permits a wake.
|
||||
The export must name the blocker issue. An export from the waiting task or an
|
||||
unattributed operation in the same shared workspace does not remove its wait.
|
||||
Normal readiness, duplicate-wake, interaction, pause, and execution gates still
|
||||
apply. A board transition into blocked keeps the existing recovery behavior.
|
||||
|
||||
Cancelling a run or pausing its agent suppresses immediate stranded-work recovery.
|
||||
A queued user message remains a separate continuation request. The release path
|
||||
still preserves independently authorized deferred input; stopping the old run
|
||||
does not discard that input or grant permission to replay its old tools.
|
||||
@@ -157,6 +157,11 @@ permission, and requester scope decisions through saved browser interactions.
|
||||
|
||||
## Validation ladder
|
||||
|
||||
The native Codex Daytona `recover-controller` cell in Everyday Workflows
|
||||
requires active process and lease continuity, native receipt continuity and
|
||||
workspace finalization. See the [restart evidence contract](../tests/runner-e2e/EVERYDAY-WORKFLOWS.md).
|
||||
Catalog discovery and deterministic calibration do not establish live recovery.
|
||||
|
||||
The explicit-only [public MCP suite](../tests/runner-e2e/PUBLIC-MCP.md) evaluates
|
||||
paid assistant delegation, later retrieval, feedback, review, uncertain retries
|
||||
and permission boundaries. It uses the Product E2E fixtures, launcher, evidence
|
||||
|
||||
@@ -212,6 +212,13 @@ The active-lock lifecycle is part of the checkout contract:
|
||||
|
||||
Stale-lock recovery is crash recovery, not a retry loop. Paperclip must not clear or adopt locks held by non-terminal runs. After stale cleanup, a checkout `409` should mean a real live owner, status/assignee mismatch, unresolved blocker, or active gate still prevents checkout. Agents must treat that `409` as an ownership conflict and stop rather than retrying the same checkout.
|
||||
|
||||
Stopping a queued legacy run before runtime selection clears only its own task
|
||||
locks. It preserves deferred messages without promoting another run. The stop
|
||||
must have a recorded, expected operator cancellation and no runtime selection,
|
||||
start timestamp, or process identity. A later eligible instruction can adopt the
|
||||
saved messages through normal admission. This rule does not prove a dispatched
|
||||
provider has stopped or change native cancellation acknowledgement.
|
||||
|
||||
### Known execution waits at admission
|
||||
|
||||
A known execution hold is a waiting condition, not a new execution attempt. Every issue wake must read the current effective reconciliation hold under the issue admission lock before creating a run. Resolved recovery bookkeeping can still carry a no-replay hold; only clearing the effective hold makes admission eligible again. The final dispatch gate remains required for changes after admission.
|
||||
@@ -528,6 +535,15 @@ Warm sandbox reuse must match the current host Git tip and branch as well as the
|
||||
file snapshot and saved stamp, including managed nested repositories. A history
|
||||
or branch mismatch restages the host before the next run begins.
|
||||
|
||||
Disk-backed native sandbox workspace snapshots run in at most two worker threads per
|
||||
controller process. Each capture retains the same file hashes, modes, symlinks,
|
||||
exclusions and ignored-path manifest as an inline capture. A worker seals its
|
||||
private manifest before handing it to the controller; ordinary snapshot disposal
|
||||
removes that worker's temporary directory. A failed capture removes its temporary
|
||||
state before releasing its worker slot. This scheduling boundary does not replace
|
||||
source authorization, warm-stamp validation, Git identity checks or restore
|
||||
conflict detection. Legacy adapter snapshots remain inline.
|
||||
|
||||
### Remote Codex model compatibility
|
||||
|
||||
Fresh remote Codex Runner runs check the selected image CLI before saving the
|
||||
@@ -841,6 +857,15 @@ On startup and on the periodic recovery loop, Paperclip performs the following r
|
||||
|
||||
The stranded-work pass closes the gap where issue state survives a crash but the wake/run path does not. The silent-run scan covers the separate case where a live process exists but has stopped producing observable output.
|
||||
|
||||
Startup health (`/api/health`) reports `starting` until these recovery passes and
|
||||
the other startup steps that serving requests depends on finish. Housekeeping
|
||||
that every scheduler tick repeats does not delay it: the custom-image setup
|
||||
cleanup, tool connection health, decision expiry, adapter and setup-token login
|
||||
reapers, environment lease cleanup, and the retention sweep run after readiness.
|
||||
A failed housekeeping step is logged as `startup housekeeping step failed` and
|
||||
the next step still runs. The heartbeat scheduler interval starts after
|
||||
housekeeping, so the reapers still finish before the first timer tick.
|
||||
|
||||
The stale-lock sweep leaves native runs with their finalization coordinator while
|
||||
same-session recovery or accepted-result finalization is pending. A provider can
|
||||
exit before workspace copy-back, assessment, or arbitration completes. An expired
|
||||
@@ -1124,6 +1149,25 @@ recording the proof and successor lineage. Retained provider files are not edite
|
||||
|
||||
Bootstrap retries, exact-checkpoint resumes, and fresh replacement sessions share three total provider attempts, including the original attempt. Linked run IDs, controller restarts, and duplicate wakes do not reset this budget. Automatic attempts retain the 30-second delay. Replacement scheduling and predecessor lineage commit together, with one successor per predecessor and admission through the normal task locks, authorization, pause, approval, and budget gates.
|
||||
|
||||
A prepared remote workspace interrupted before the executor's first durable
|
||||
claim may resume its untouched bootstrap on the same run. This requires attempt
|
||||
and controller generation zero, no prior owner, and no process, provider session,
|
||||
checkpoint, or launch evidence. The executor still verifies any durable runner
|
||||
root before starting. Missing evidence after a prior claim does not qualify.
|
||||
Classification preserves the provider attempt count; execution consumes the
|
||||
first attempt through the normal claim and admission gates.
|
||||
|
||||
This path also requires a server-owned preparation fencing marker created with
|
||||
fresh native run identities. Unmarked preparations from older servers remain
|
||||
held during rolling upgrades, because those servers cannot safely yield to a
|
||||
recovery owner. Revisiting an existing preparation cannot add that marker.
|
||||
|
||||
A preparation that resumes after another execution acquired the coordinator
|
||||
lease yields through the controller-detachment path. It does not fail the shared
|
||||
run, release its environment, or start another provider. Lease expiry alone does
|
||||
not authorize that stale preparation to take ownership; normal recovery must
|
||||
establish a new claim first.
|
||||
|
||||
An exact local Codex restart can restore the conversation after losing its active
|
||||
turn. Runnerd records this as `provider_turn_lost_on_restore`, with no invented
|
||||
task result. The admitted restart attempt may send one continuation in that same
|
||||
@@ -1265,6 +1309,13 @@ Examples:
|
||||
|
||||
The recovery action stays source-scoped by default. Stranded-task escalation is board-owned and records the cause, evidence, next action, source and return owner, `routingPolicy: board_escalation_no_takeover_v1`, and wake or monitor policy in the source thread/detail surface.
|
||||
|
||||
The recovery-actions read API also lists settled records whose automatic recovery
|
||||
receipt still blocks replay. A resolved recovery record does not by itself prove
|
||||
that work resumed. The task's stopped-run notice lets authorized board users
|
||||
inspect the matching decision under Recovery details, even when there is no
|
||||
active recovery action. Existing run inspection, verified continuation, new
|
||||
messages, and required-file repair remain the supported recovery paths.
|
||||
|
||||
The board owns the recovery decision, not the source deliverable. Automatic recovery must preserve both source assignee fields. Only an explicit operator decision or applicable serious-failure policy may transfer the deliverable.
|
||||
|
||||
An upgrade may encounter an already-active agent-owned recovery action. Paperclip keeps that record readable and resolvable for compatibility, but periodic reconciliation does not enqueue another takeover wake from it.
|
||||
|
||||
@@ -33,8 +33,10 @@ Explicit project policies and issue workspace settings still take precedence. A
|
||||
Projects and individual issues can set `sharedWorkspaceConcurrency` in their execution workspace policy/settings:
|
||||
|
||||
- `auto` (the default when absent): allow concurrent shared-workspace runs on `local` and `ssh` environments, and serialize runs on `sandbox` and `plugin` environments. An instance forced to Kubernetes always serializes in `auto` mode.
|
||||
- `serialize`: on `sandbox` and `plugin` environments, defer a run while another live run holds the same project workspace, using the `workspace_busy` retry path. Local and SSH folders remain concurrent, even when an existing project or issue policy requests serialization.
|
||||
- `allow`: dispatch alongside a live holder on every environment.
|
||||
- `serialize`: on `sandbox` and `plugin` environments, defer a run while another live run holds the same project workspace or the same reused shared execution workspace, using the `workspace_busy` retry path. Local and SSH folders remain concurrent, even when an existing project or issue policy requests serialization.
|
||||
- `allow`: dispatch alongside a live holder on `local` and `ssh` environments. Other environments cannot place concurrent agents in one physical shared folder, so a run with a live holder fails with `configuration_incomplete` (`shared_realization_unsupported`) instead of waiting.
|
||||
|
||||
A reused shared execution workspace follows the same rules. For example, a child task can inherit the shared execution workspace of a parent whose run is still live. In `auto` or `serialize` mode on a sandbox, the child waits for the parent run to finish and then continues automatically. A parent run that is still running but has produced no output for more than an hour still holds the folder. In that case the child fails with `configuration_incomplete` (`shared_realization_unsupported`). It does not share the folder with the parent, and it does not wait without limit.
|
||||
|
||||
Issue settings override the project policy, which overrides the default `auto`. The final execution environment determines whether workspace serialization applies; an instance forced to Kubernetes is a sandbox even if the agent normally runs locally. Local and SSH folder runs never wait for exclusive workspace ownership. Task checkout and per-agent concurrency limits still apply. When concurrency is allowed and a live holder exists, Paperclip adds the holder run and issue to the dispatched task context so agents can coordinate concurrent mutations through commits. The setting is optional JSON policy data, so existing databases require no migration.
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Grok Bot agents (experimental)
|
||||
|
||||
Grok Bot is an externally managed Paperclip agent. It uses Grok Bot's existing
|
||||
apps and persistent context. It is separate from the Grok Build coding runtime.
|
||||
Paperclip coordinates tasks through the managed Runner. Grok usage and spending
|
||||
are not reported.
|
||||
|
||||
From **Agents → New agent → Invite external agent → Grok Bot**, name the agent
|
||||
and select its role and manager. Follow the connection instructions in Grok Bot.
|
||||
Approve the fixed Paperclip company and agent in the browser. Copy the one-use
|
||||
pairing code with **Copy code**; enter it only on the authorization page.
|
||||
Grok plugins are account-wide, so connecting a Bot does not isolate credentials
|
||||
between Bots on that Grok account.
|
||||
|
||||
Copy the routine instructions into the Bot's chat. Open its desktop routine's
|
||||
Webhook panel and paste the URL and key into Paperclip's secure configuration.
|
||||
Use a webhook-only routine with no schedule. **Verify connection** must show both
|
||||
assignment delivery and an authenticated reply before **Done** becomes available.
|
||||
Use **Save & exit** to preserve unfinished setup. See the official
|
||||
[routine instructions](https://cursor.com/help/grok-bot/routines).
|
||||
|
||||
Manage the Bot from its agent's **Runtime** settings. This shows connection
|
||||
health, routine configuration, and verification. Model selection, local paths,
|
||||
and runtime tests are managed outside Paperclip; use **Verify connection** to
|
||||
check both directions. Use **Reconnect agent access** to restore the same agent's
|
||||
connection. Select **Copy code** beside the masked one-use pairing code, then
|
||||
paste it into Paperclip's authorization page. Keep it out of Bot chat.
|
||||
|
||||
Assign ordinary tasks to the agent. Read its progress, answer its question cards,
|
||||
and review its documents and files in the task thread. The Bot can initiate a
|
||||
new task from its Grok conversation through the admitted work-session tools.
|
||||
An immediate proactive claim does not send the same assignment back through the
|
||||
routine.
|
||||
|
||||
## Files and completion
|
||||
|
||||
Use `paperclip_grok_get_upload_url` for an accepted current assignment. Compute
|
||||
the exact file's SHA-256 and size, then PUT those bytes with Grok's own HTTP/file
|
||||
tools. The successful response includes the attachment and its verified
|
||||
`publication.evidenceRef`. Cite that reference in the completion report. No
|
||||
Paperclip filesystem is exposed, and no separate upload completion call is needed.
|
||||
Repeat the same request ID after a lost response. Concurrent or repeated uploads
|
||||
retain one attachment and one publication receipt. Person MCP uploads remain
|
||||
person uploads; they do not gain agent execution or publication authority.
|
||||
|
||||
The upload checks current grant, company, agent, task, work session and work mode.
|
||||
Publication also verifies stored filename, byte size and SHA-256. Normal Runner
|
||||
completion verifies that receipt against the upload and admitted assignment.
|
||||
A visible attachment without that verified publication does not satisfy a task
|
||||
that requires a file output.
|
||||
|
||||
## Feedback and pending operations
|
||||
|
||||
An assignment wake starts a Grok routine. Once the Bot accepts that work,
|
||||
Paperclip keeps new comments and asynchronous operation receipts durable without
|
||||
starting another routine for that session. Silence does not mean the Bot is idle.
|
||||
Acceptance and the final webhook handoff are serialized. If acceptance finishes
|
||||
while credentials are loading, the unsent wake is cancelled. Stop and
|
||||
authenticated acknowledgements remain independent of a pending HTTP response.
|
||||
Human-question answers and resolved durable waits resume through normal Runner
|
||||
admission and a new assignment wake.
|
||||
|
||||
If a tool returns pending, poll `paperclip_grok_operation_status` with the same
|
||||
assignment and request IDs for its durable outcome. Do not repeat it under a new
|
||||
ID or perform dependent work first. An unknown outcome requires reconciliation. At safe boundaries and before finishing,
|
||||
the Bot reads its inbox and current task history for queued feedback. Current
|
||||
assignment reads include these instructions, including for existing connections.
|
||||
|
||||
## Recovery and stop
|
||||
|
||||
Inspect a rejected delivery before using **Retry rejected delivery**. An uncertain
|
||||
outcome needs inspection and explicit recovery; it is never sent again automatically.
|
||||
Saved work remains on the task if execution fails. Reverify after clearing an
|
||||
inspected recovery hold before admitting another assignment.
|
||||
|
||||
**Stop Paperclip work** immediately revokes the session's Paperclip authority and
|
||||
stops future dispatch. Remote Grok execution may continue. Open the Grok Bot app
|
||||
to inspect it. Paperclip shows reported activity, not a complete remote transcript
|
||||
or computer recording.
|
||||
@@ -74,6 +74,41 @@ Verify a full scheduled sleep/wake/execution cycle on staging before enabling
|
||||
this host capability. The [tests](../server/src/__tests__/idle-sleep-safety.test.ts)
|
||||
cover ownership, future deadlines, changed generations and conservative fallback.
|
||||
|
||||
## Passive issues and scheduled monitors
|
||||
|
||||
Signed Cloud control callers can request `scheduledWake=2` to include issue
|
||||
monitoring alongside routines. Earlier hosts requesting `scheduledWake=1`
|
||||
retain the routine-only rules, including the conservative open-issue check.
|
||||
Browser administrators and tenant credentials cannot request either extension.
|
||||
|
||||
With the second capability, backlog tasks, unassigned todo tasks, explicit
|
||||
blocked tasks and human-owned tasks may be passive records. Their presence
|
||||
alone does not require compute. An assigned onboarding first task is also
|
||||
passive with an idle assignee before its first run, user-authored comment or user-resolved opening
|
||||
card. This uses the same awaiting-user predicate as recovery; a seeded greeting
|
||||
does not start work. User input still enters through the host wake path.
|
||||
Execution locks, automated pending review,
|
||||
conversation state, unknown state and dependency repair remain blockers. A
|
||||
blocked task whose blockers have all finished stays awake for reconciliation.
|
||||
Enabled task watchdogs and unsupported recovery keep their existing checks.
|
||||
Incoming changes still require the hosting admission and wake path.
|
||||
|
||||
An active issue monitor needs a saved future `monitor_next_check_at` matching
|
||||
its validated execution policy, a company-scoped agent, no outstanding dispatch
|
||||
claim, and remaining attempts and timeout. Due, malformed, quota-recovery,
|
||||
unowned and inconsistent monitors keep the workspace awake. Inspection does
|
||||
not clear schedules, advance attempts or dispatch an agent.
|
||||
|
||||
The combined report uses `version: 2` and a scheduled proof with `version: 1`
|
||||
and `kind: "routines_and_issue_monitors"`. Its deadline is the earliest required
|
||||
routine or monitor time across all companies. Its generation binds the current
|
||||
process, idle owner and inspected catalogs. Hosts must acknowledge this known
|
||||
proof kind, persist the alarm and validate the unchanged owned report before
|
||||
stopping compute. Existing hosts reject the new kind. Monitor dispatch and
|
||||
startup catch-up continue through the existing scheduler and atomic claims;
|
||||
self-hosted scheduling is unchanged. This capability does not cover all future
|
||||
work in the instance: every other safety check continues to apply.
|
||||
|
||||
## Retry history
|
||||
|
||||
Retry promotion keeps `scheduled_retry_at` on a run for inspection. That date
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Native workspace finalization ownership and recovery
|
||||
|
||||
A native provider can fail before it submits a result while its workspace still
|
||||
contains unpublished edits. A prepared workspace reference is not an export
|
||||
receipt. Terminal provider failure pins the exact bound sandbox and records a
|
||||
file-recovery obligation in the same transaction as the failed run. Both setup
|
||||
failure handlers record that obligation before their terminal writer and provider
|
||||
cleanup run. This includes descriptor reads that fail inside runtime selection. Only its exact live recovery claim can settle an owned
|
||||
preparation failure; another owner or a stale claim prevents cleanup. Settling
|
||||
that claim and recording file retention share a transaction.
|
||||
|
||||
The recovery card names the original run and requires evidence that its exact
|
||||
allocation stopped and its files were recovered. Recording that evidence does
|
||||
not retry the old turn, certify unknown external actions, change the current task
|
||||
owner or status, or require an accepted native result. Accepted-result export
|
||||
recovery and same-run provider recovery keep their existing paths. An export
|
||||
that has already finalized does not create this file obligation.
|
||||
|
||||
Native workspace export and merge acquire a PostgreSQL advisory lock scoped to
|
||||
company and run before the first physical copyback. The live heartbeat and the
|
||||
reconciler share that lock. Recovery skips a busy owner without recording another
|
||||
@@ -35,6 +51,14 @@ leave tar/Git children alive, so incomplete copyback requires operator stop
|
||||
verification even on the same host. PID reuse is checked against its recorded
|
||||
start time rather than trusted by PID alone.
|
||||
|
||||
When PostgreSQL configures an idle-transaction timeout, the owner probes its
|
||||
dedicated lock transaction at one third of that interval, capped at
|
||||
30 seconds. The probe stops and joins before the transaction is released. This
|
||||
keeps a live copyback from losing its lock only because filesystem work has no
|
||||
database statements. It does not disable the database timeout or reconnect a
|
||||
failed lock session. Publication still requires the physical connection and
|
||||
the exact durable owner token. A disabled timeout adds no periodic probe.
|
||||
|
||||
## Automatic cloud recovery
|
||||
|
||||
New Railway copyback receipts record the exact project, environment, service and
|
||||
|
||||
@@ -175,7 +175,7 @@ GitHub identity selection, secret resolution, managed AI runtime preparation,
|
||||
skill inventory, connector assignments, connector skill preparation and delivery,
|
||||
connection instructions, and agent identity. Their names are
|
||||
`heartbeat.prepare.configuration_<step>`, using the closed names in
|
||||
`server/src/services/heartbeat/preparation-timing.ts`. These intervals are inside
|
||||
`server/src/services/execution-preparation-timing.ts`. These intervals are inside
|
||||
`execution_configuration`; do not add them to its duration. They remain available
|
||||
when a later setup step fails before the provider lifecycle trace starts.
|
||||
|
||||
@@ -194,6 +194,52 @@ traces; the provider lifecycle trace is created later and can have a different
|
||||
trace ID. Without an endpoint, tracing remains a no-op. These spans add no
|
||||
first-party Telemetry events or database run-log writes.
|
||||
|
||||
Workspace preparation also records `heartbeat.prepare.workspace_<step>` spans
|
||||
for source resolution, provisioning policy, base validation, source access,
|
||||
provisioning, retained source access, workspace record creation and updates,
|
||||
task binding, operation binding, configuration records, repository admission,
|
||||
project repositories, and recovery-root reservation. These intervals are inside
|
||||
`heartbeat.prepare.workspace`. Repeated bindings or updates have separate spans.
|
||||
Do not add these intervals to the containing workspace duration. They use the
|
||||
same run hash and contain no workspace paths, commands, or returned records.
|
||||
|
||||
The `paperclip.native-controller` tracer records separate
|
||||
`native.controller.<phase>` root spans around GitHub access setup, wake binding,
|
||||
backend construction, connector assignments, assigned MCP discovery, tool
|
||||
authority, companion selection and Runner binary resolution. It also records
|
||||
GitHub shutdown, coordinator release, instruction checkpoints and collection,
|
||||
and warm-session release. These intervals can occur between the existing native
|
||||
preparation, execution and settlement spans. They are controller work, not model
|
||||
turn time, and carry only the existing run hash. Nested phases overlap their
|
||||
containing backend or lifecycle intervals; do not add both durations.
|
||||
|
||||
All preparation timing uses the endpoint gate, even when another library has
|
||||
registered a tracer provider. Tracer lookup, callback, status or ending failures
|
||||
cannot repeat work or replace its result or error. Automatic database children
|
||||
are not guaranteed: an aggregate workspace trace can contain just one span.
|
||||
If a process exits before an operation settles, its open span may not be exported.
|
||||
|
||||
With an endpoint, `paperclip.controller-startup` records independent
|
||||
`controller.startup.<phase>` root spans for tool reviews, gateway sessions,
|
||||
delivery-worker readiness, accounting, plugin-worker readiness, native recovery,
|
||||
hot-restart adoption, orphan reaping, scheduled retries, queued runs, and goal
|
||||
recovery. These spans have no custom attributes. Each ends when its awaited
|
||||
phase settles, including failures. They separate controller readiness from
|
||||
provider startup; they are not agent-run durations. A disabled endpoint takes
|
||||
the direct work path without a tracer lookup or additional log messages.
|
||||
Tracing failures cannot repeat recovery or replace its result or error.
|
||||
|
||||
Native recovery and orphan reaping each run a native finalization
|
||||
reconciliation pass; the periodic recovery loop runs it on every scheduler tick.
|
||||
With an endpoint, a pass that takes at least one second records, after it ends,
|
||||
a `controller.native_reconciliation` root span from `paperclip.native-reconciliation`
|
||||
with one child span per phase: `legacy_workspace_exports`,
|
||||
`obsolete_policy_reviews`, `automatic_completion_reviews`, `candidate_scan` and
|
||||
`candidates`. The pass span carries `paperclip.native_reconciliation.scoped` and,
|
||||
once known, `paperclip.native_reconciliation.candidates` (a count). A failed
|
||||
pass marks the pass span and the phase that was running with an error status.
|
||||
Faster passes record nothing.
|
||||
|
||||
## Native Runner Trace Spans
|
||||
|
||||
Paperclip Runner task runs emit a single foldable OpenTelemetry trace. This is
|
||||
@@ -591,6 +637,14 @@ whether remote work may have been accepted.
|
||||
identifiers, untyped terminal failures, and timeouts after accepted work remain
|
||||
reportable. Stream/poll errors retain their existing retry and logging behavior.
|
||||
|
||||
A Codex ACP model capacity failure also stays local. The Codex ACP classifier
|
||||
must match a typed `service` terminal failure that reports the model at capacity
|
||||
or the server overloaded. It records `codex_transient_upstream` with the
|
||||
`transient_upstream` family, and the bounded transient retry owns recovery. The
|
||||
report must come after setup and carry no signal. Other `service` failures,
|
||||
untyped capacity text, and Codex CLI transient upstream failures remain reportable.
|
||||
The run keeps its failed state and its terminal diagnostic.
|
||||
|
||||
Expected pre-provider waits for a shared project workspace, a credential rotation
|
||||
lock, or an exhausted AI connection pool carry a server-owned expected
|
||||
cancellation receipt. They retain existing retry and lock behavior without a
|
||||
@@ -746,6 +800,18 @@ only for these labels; its text and stack are not copied, because they bypass th
|
||||
executor's identity redaction. These observations do not change close, quarantine
|
||||
or recovery behavior.
|
||||
|
||||
Remote runner reattachment failures also record `remoteRunnerRecoveryStage`
|
||||
and `remoteRunnerRecoveryReason` from the saved run. When reattachment fails
|
||||
before the coordinator saves its result, the report reads the same labels from
|
||||
the caught recovery error instead. These fields contain
|
||||
closed labels for marker reads and process verification. They distinguish
|
||||
missing or unsafe markers, invalid or missing fingerprints, changed or
|
||||
unavailable process identity, and failed or timed-out probes. Paths, PIDs,
|
||||
nonces, command lines and transport output are excluded. The existing
|
||||
`runner_remote_process_identity_unavailable` error message and native failure
|
||||
classification remain unchanged. The coordinator also saves these labels in
|
||||
its failure detail and recovery evidence. A later failure clears stale labels.
|
||||
|
||||
ACP turns record `acpLastEventAgeMs`, `acpObservedEventCount`,
|
||||
`acpPendingToolCount`, and `acpToolInventoryComplete` at finalization, before
|
||||
usage reads, error logging, and cleanup. The age measures time since the last
|
||||
|
||||
@@ -27,3 +27,11 @@ The importer saves available repository files without asking for repair decision
|
||||
The production dialog shows every skill by default, selects all discovered skills, and collapses individual package files. It uses one collapsed warnings disclosure and leaves Import repository available. The importer has no packaging option: new imports keep repository files together. Storybook covers omitted files, warning details, missing references, setup problems, warnings after import, and mobile review. Focused scanner/delivery/PostgreSQL tests and UI tests verify saved warnings and available bytes; browser fixtures exercise the production UI. This revision has not rerun the paid local/Daytona campaigns above.
|
||||
|
||||
Final acceptance also found and fixed duplicate saved warnings and an installed-file viewer that did not expose repository dependencies. The viewer now lists repository files and reads their saved bytes without GitHub access. Content errors for unsafe execution or secret disclosure omit the affected files with warnings; the remaining repository can still import. Database and scanner regressions verify these exclusions and file-reader company isolation.
|
||||
|
||||
## October 14: dependency read batches
|
||||
|
||||
Oversized repository imports read each Markdown dependency frontier through the existing bounded Git batch reader. The next frontier contains newly found references. The scanner retains its file and byte limits, content audits, reference order, and fallback for snapshot readers without batch support. This avoids one Git child process for each Markdown file during a source refresh.
|
||||
|
||||
The regression checks a real oversized Git repository with 64 skills, transitive references, and a reference cycle. It compares every retained file's bytes and requires four batch reads with no individual reads. A local comparison of the old and final dependency walkers on 500 Markdown files and two shared references returned identical entries: three old samples took 4.9–6.2 seconds with 502 child processes, while the new samples took 62–85 milliseconds with three batches. These measurements exclude provider execution and do not establish agent response latency.
|
||||
|
||||
The frontier uses a queue of newly added Markdown files. A regression at the 9,990-file limit retains every file, terminates a reference cycle, and bounds path visits. The earlier frontier rescan made 50,014,936 path visits in a local comparison. The queue makes 89,911 visits and retains the same output order.
|
||||
@@ -67,7 +67,7 @@ Preserve simultaneous local/SSH tasks in an explicitly configured identical phys
|
||||
|
||||
Concurrent remote sharing requires proven common physical placement, separate session authority and coherent snapshot capability. Unsupported new concurrent sharing returns a typed unavailable result; do not instantiate separate clones and label them shared. Already-admitted descriptors remain recoverable unchanged. Local authoritative roots need no replica copyback; SSH retains its actual existing persistence contract.
|
||||
|
||||
Sparse filesystem checkpoints use disk-backed full manifests, changed bytes/deletions, safe-link and existing ignore semantics, and separate nested Git history. Capture still enumerates and hashes files with stable-read validation; a metadata hash cache is deferred. No-change sends zero file-content payload where capability is supported; manifest metadata still transfers; unsupported transport uses explicit confined full fallback. Sparse capture capacity failures before outbound data is read also use the existing confined full transfer, so a tiny output can recover when the sandbox cannot preserve the sparse manifest reserve. Returned manifest, path, digest and payload validation failures remain errors and never trigger fallback. Full checkpoint exports validate the completed staging root before merge, reject unsupported entries, and omit absolute or escaping links without following them; safe relative links remain supported. Reuse immutable durable seed generations, not only warm sandbox contents; reuse still verifies archive digests. Existing run references pin seeds/deltas until all admitted runs/recovery obligations release them. Retain the final host rescan because the run ownership fence does not exclude external writers to shared folders; do not reuse a stale post-merge manifest.
|
||||
Sparse filesystem checkpoints use disk-backed full manifests, changed bytes/deletions, safe-link and existing ignore semantics, and separate nested Git history. Capture still enumerates and hashes files with stable-read validation; a metadata hash cache is deferred. No-change sends zero file-content payload where capability is supported; manifest metadata still transfers; unsupported transport uses explicit confined full fallback. Sparse capture capacity failures before outbound data is read also use the existing confined full transfer, so a tiny output can recover when the sandbox cannot preserve the sparse manifest reserve. Returned manifest, path, digest and payload validation failures remain errors and never trigger fallback. Full checkpoint exports validate the completed staging root before merge, reject unsupported entries, and omit absolute or escaping links without following them; safe relative links remain supported. Reuse immutable durable seed generations, not only warm sandbox contents; reuse still verifies archive digests. If warm identity validation requires a host-current transfer, that fallback retains the same company/workspace seed-cache scope, so a verified generation can supply the transfer without a second Git clone and archive rebuild. Missing or invalid generations still rebuild from the host. When the generation cache declines a new snapshot, warm fallback may replay the current preparation's private durable seed after its existing file-type and digest checks. It does not raise the cache quotas or reuse a prior run's seed. Missing or changed current-run archives retain the host rebuild path. Existing run references pin seeds/deltas until all admitted runs/recovery obligations release them. Retain the final host rescan because the run ownership fence does not exclude external writers to shared folders; do not reuse a stale post-merge manifest.
|
||||
|
||||
A returned sparse snapshot must describe a coherent tree before any host mutation. Every nested entry requires an immediate parent marked as a directory in that same returned snapshot, including baseline-unchanged files. Checking every directory also verifies recursive ancestry. Top-level entries use the implicit root. Invalid trees fail without overwriting retained host files and keep recovery evidence. The check uses the existing indexed manifest interface; it adds no host-path scans or unbounded in-memory inventory.
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Heartbeat disposition test audit — 2026-10-12
|
||||
|
||||
Two duplicate fixture-and-recovery executions are retired. Test registrations fall from 398 to 396. Test source changes by +13/−68, net−55 lines. No execution-time or CI-time benefit is claimed.
|
||||
|
||||
The historical pre-integration proof parent was #16099 at `8e955181d8a3776acc86e4662de3c77933231a76`, branch `codex/test-audit-daytona-pack`. The complete 248-declaration, 398-runtime-case ledger and 49 helper/hook hashes are in the companion JSON evidence file. All 52 matrix declarations remain. All 245 declarations outside the keeper and the two retirements retain their source hashes.
|
||||
|
||||
## Retired and keeper map
|
||||
|
||||
| Role | Original lines | Case |
|
||||
| --- | --- | --- |
|
||||
| Retired case 202 |13446–13471| `repairs missing disposition independently of the productive diagnostic label` |
|
||||
| Retired case 207 |13710–13749| `re-enqueues recovery when the latest in-progress continuation made progress but left no live path` |
|
||||
| Keeper case 239 |15632–15706| `does not treat a productive terminal run as healthy when in-progress work has no live path` |
|
||||
|
||||
Each seeds `seedStrandedIssueFixture({status: "in_progress", runStatus: "succeeded", livenessState: "advanced"})`, creates one heartbeat service, and reconciles once. No optional fixture input differs. The keeper's extra precondition queries are reads. They confirm the source owner and the absence of active company runs and live company wakes.
|
||||
|
||||
The keeper retains its complete original checks. It receives the exact successful/continuation/escalation counters, post-reconcile issue state, two wakes, the original agent-only run count, and the conditional settlement wait. Both the agent-only and company+agent query boundaries remain. Full retry issue/task/source/ancestor identity and the absent modelProfile remain. Crash, ownership, persistence, restart, cancellation, error and distinct liveness scenarios outside this exact fixture are unchanged.
|
||||
|
||||
## Historical sequential exact-parent controls
|
||||
|
||||
| Phase | Passed | Failed | Skipped |
|
||||
| --- | --- | --- | --- |
|
||||
| baseline | 398 | 0 | 0 |
|
||||
| candidate | 396 | 0 | 0 |
|
||||
| revert | 398 | 0 | 0 |
|
||||
| mutation-source-original | 0 | 1 | 397 |
|
||||
| mutation-source-keeper | 0 | 1 | 395 |
|
||||
| mutation-counter-original | 0 | 1 | 397 |
|
||||
| mutation-counter-keeper | 0 | 1 | 395 |
|
||||
| restoration | 396 | 0 | 0 |
|
||||
|
||||
The four mutation runs select one intended case each. Their unselected skips do not count as coverage. The real recovery owner runs against isolated embedded Postgres; adapter mocks are unchanged.
|
||||
|
||||
The source mutation changes `server/src/services/recovery/service.ts:3163` from the canonical persisted context source to `issue.wrong_disposition_repair`. It fails retired case 202 at original line 13463 and the keeper at candidate line 15635, at the context source oracle.
|
||||
|
||||
The independent counter mutation adds `successfulContinuationObserved += 1` in the active queued legacy-repair branch at `server/src/services/recovery/service.ts:4513`. It fails retired case 207 at original line 13721 and the keeper at candidate line 15608, where the counter expects 0 and receives 1. Other earlier liveness checks pass before this guard.
|
||||
|
||||
Restoring the owner returns the full candidate to 396/396 with zero failures/skips. Test and owner source are then restored byte for byte to the exact parent before the final patch is applied. The process snapshot has no owned remaining test/Postgres process. No global main Postgres process is present at that snapshot. One unrelated Vitest process in another checkout remains. It is not part of this proof. No global process is terminated by this audit.
|
||||
|
||||
## Integrated revision checks
|
||||
|
||||
The counts, source hashes, matrix/declaration ledger and mutation line numbers
|
||||
below and in the companion JSON were recorded before integration. They do not
|
||||
claim a passing run or matching whole-file hashes on today's PR head.
|
||||
|
||||
The integrated code revisions are parent
|
||||
`ef04ba1592a5932d2d971e09b09467f878572d7a` and candidate
|
||||
`1daaa7496b67c9d38eb7ee4f3eec9e1a3e27cd86`. These immutable public revisions
|
||||
precede this documentation clarification; the clarification changes no tests or
|
||||
production source. Fresh static checks confirm the candidate still changes only
|
||||
+13/-68 lines in `heartbeat-process-recovery.test.ts`, and the production owner
|
||||
has identical bytes in both revisions.
|
||||
|
||||
| Integrated file | Parent SHA-256 | Candidate SHA-256 |
|
||||
| --- | --- | --- |
|
||||
| `heartbeat-process-recovery.test.ts` | `66c6fcfd0a798bc690355eef86306c3b02dead31341e14e27a27bf3f7e5ca061` | `e1d787440f7a24cde1601da41a7c2d78897f0e0b9b31c88657479332d3ff3310` |
|
||||
| `recovery/service.ts` | `6f23f16a963eb4fe743daad39bc3a51373b72799c851ac9b367ebf119c2b706c` | `6f23f16a963eb4fe743daad39bc3a51373b72799c851ac9b367ebf119c2b706c` |
|
||||
|
||||
```sh
|
||||
base=ef04ba1592a5932d2d971e09b09467f878572d7a
|
||||
candidate=1daaa7496b67c9d38eb7ee4f3eec9e1a3e27cd86
|
||||
git fetch https://github.com/paperclipai/paperclip.git "$base" "$candidate"
|
||||
git diff --check "$base" "$candidate"
|
||||
git diff --numstat "$base" "$candidate" -- server/src/__tests__/heartbeat-process-recovery.test.ts
|
||||
git show "$base":server/src/__tests__/heartbeat-process-recovery.test.ts | shasum -a 256
|
||||
git show "$candidate":server/src/__tests__/heartbeat-process-recovery.test.ts | shasum -a 256
|
||||
git diff --exit-code "$base" "$candidate" -- server/src/services/recovery/service.ts
|
||||
```
|
||||
|
||||
To validate the integrated owner, create separate isolated checkouts at these
|
||||
revisions, install the pinned dependencies and run the owner command below in
|
||||
each. Record actual current counts; do not substitute the historical 398/396
|
||||
counts. Current-head CI must validate the integrated change before merge. This
|
||||
clarification records static checks, not a new local runtime or mutation run.
|
||||
|
||||
## Reproduce historical controls
|
||||
|
||||
Use isolated PAPERCLIP_HOME, PAPERCLIP_CONFIG and TMPDIR values and the checked-in root/server Vitest configuration:
|
||||
|
||||
```sh
|
||||
node node_modules/vitest/vitest.mjs run --project @paperclipai/server \
|
||||
server/src/__tests__/heartbeat-process-recovery.test.ts --exclude '**/dist/**'
|
||||
```
|
||||
|
||||
Run baseline, candidate, full revert, each original/keeper mutation independently, then the full restored candidate. Restore source after proof. Mutation patches are described above and are never committed.
|
||||
|
||||
## Limits
|
||||
|
||||
This is a narrow test change. Local controls cover the entire owner. Repository-wide typecheck/test/build and review gates must be checked on the new PR head. No timing or broader live-product acceptance claim is made.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Round 10 database schema test audit
|
||||
|
||||
This test-only change is stacked on #16075 at `3432307f95f679e20b991c6a250934c2114b7aaf`. The original matched baseline and control ran on its prior head `e86a747fc028069d2bb4d699bd814e0d82fad558`. The parent correction changes documentation only; the rebased complete owner replay passed 6/6 with no failures or skips. It removes one registered case and six net test lines. It makes no measured CI time claim.
|
||||
|
||||
| Retired case | Keeper | Preserved observations |
|
||||
| --- | --- | --- |
|
||||
| `keeps the owner principal non-null and drops the environment from the key` | `serializes the active slot on company, owner, and adapter` | Copy the exact `started_by_user_id` non-null assertion. The keeper's exact index column list already proves that the owner is present and the environment is absent. |
|
||||
|
||||
Both cases inspect the same `adapterAuthSessions` table and active index. The keeper still checks index presence, uniqueness, the exact company/owner/adapter columns, and the partial predicate. The public-session identity, claim marker, active states, secret-column exclusion, and migration SQL cases remain separate. No fixture, helper, schema, migration, or caller changes.
|
||||
|
||||
The direct owner is `packages/db/src/schema/adapter_auth_sessions.ts`. Its owner column prevents a null principal from escaping the active unique index. Local login, device login, and setup-token services use this company/owner/adapter scope. The DB project runs in the default `general-workspaces-c` CI group.
|
||||
|
||||
Complete-file command:
|
||||
|
||||
```sh
|
||||
pnpm exec vitest run packages/db/src/adapter-auth-sessions-schema.test.ts
|
||||
```
|
||||
|
||||
| Run on the exact stacked base | Passed | Failed | Skipped |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Baseline | 7 | 0 | 0 |
|
||||
| Candidate | 6 | 0 | 0 |
|
||||
| Nullability control | 5 | 1 | 0 |
|
||||
| Restored-owner candidate | 6 | 0 | 0 |
|
||||
|
||||
The deliberate control removes only `.notNull()` from `startedByUserId` in the production schema. The complete file fails only the keeper at the copied non-null assertion (`false` instead of `true`). The other five cases pass. Restore the schema before the positive replay.
|
||||
|
||||
A whole-program byte comparison proves that the only test changes are one retired statement and one exact copied assertion. Production schema bytes are restored with SHA-256 `ada8f01da2171306b97e5cb2b14b52dd53f6a7d58dd9dd51414d381c56821235`. Candidate test SHA-256 is `9719104e3f6d79d868dee36b1778ce758da8829ffc4485019cad41a128bcff6f`. The earlier independent proof at `77408fa6c3d7c916d7160760eae2144aaa0f8fe7` is preserved separately. Root review approved the keeper map before this PR.
|
||||
|
||||
Risk: the index keeper now reports both index and owner-nullability defects. An earlier index failure can stop the later assertion, but still fails the scenario. Each distinct schema and migration contract remains covered.
|
||||
@@ -0,0 +1,113 @@
|
||||
# 2026-10-12 native status corpus preservation map
|
||||
|
||||
The native status-authority corpus keeps one complete execution of its 53 real fixtures. The comparison-negative loop uses those same actual observations. It no longer creates another set of issues, runs, decisions, effects, and wakes only to change expected comparison fields.
|
||||
|
||||
The change is stacked on public PR #16076 at parent commit `9abd9b23bde1da5fa1f11bfb9ce9a0d5d4c5a8dd`. The dedicated branch is `codex/test-audit-r11-corpus`.
|
||||
|
||||
## Change and retained proof
|
||||
|
||||
| Item | Before | After |
|
||||
|---|---:|---:|
|
||||
| Full real-corpus passes in the two affected declarations | 2 | 1 |
|
||||
| Real fixture executions in those passes | 106 | 53 |
|
||||
| Corpus fixture definitions | 53 | 53 |
|
||||
| Joined contract matrix rows | 70 | 70 |
|
||||
| Negative comparison field categories | 11 | 11 |
|
||||
| Registered owner and sibling cases | 62 | 61 |
|
||||
| Corpus test source lines | 2,981 | 2,976 |
|
||||
|
||||
No production source changes. All later live-entrypoint negative controls and storage, status-lock, lineage, reviewer, child-delivery, and monitor tests remain. Those controls still run their targeted scenarios separately.
|
||||
|
||||
The first declaration at `server/src/__tests__/native-status-arbiter-corpus.test.ts:1821` keeps all real consumer execution and matrix checks. It absorbs the pure negative-comparison loop from the former declaration at line 1870. The loop body is byte-identical. The two original full passes had identical fixture lists and `executeFixture` calls. The moved loop changes expected fields and local comparison objects. It does not write to the database or change the original observations.
|
||||
|
||||
All 53 fixture behavior rows are distinct when comparing mode, given facts, and expected behavior. No fixture definitions were removed.
|
||||
|
||||
## Exact-parent verification
|
||||
|
||||
| Phase | Result |
|
||||
|---|---|
|
||||
| Baseline at the parent head | 62/62 passed |
|
||||
| Candidate | 61/61 passed |
|
||||
| Comparator owner mutation | Keeper failed inside the moved negative assertion |
|
||||
| Comparator restored to candidate bytes | 61/61 passed |
|
||||
|
||||
Mutation: remove the `reasonCode` comparison from the test-local `comparisonFailures` owner at original line 358. Correct production results still satisfy the positive comparison. The moved negative check fails with `native-mechanical-completion:reasonCode: expected [] to not deeply equal []` at mutated candidate line 1889. This is a comparator/harness-owner regression, not a production status-policy mutation. The comparator was restored byte for byte before the final full owner and sibling replay.
|
||||
|
||||
Run from `server/`:
|
||||
|
||||
```sh
|
||||
node ../node_modules/vitest/vitest.mjs run --config vitest.config.ts src/__tests__/native-status-arbiter-corpus.test.ts src/services/native-runtime/status-arbiter.test.ts
|
||||
```
|
||||
|
||||
The mutation replay selects the merged keeper with `-t 'executes all 53 fixtures, joins all 70 matrix rows'`.
|
||||
|
||||
Repo-wide typecheck, the full Vitest suite, and build were not run locally for this narrow test-only change. The draft PR checks must pass before a merge-ready handoff.
|
||||
|
||||
## Source identity and local cost
|
||||
|
||||
- Baseline corpus SHA-256: `1dffa2f852ff2a9c5c6b174cfb3ef21746b65d124e4593d00401588923bbdcf0`.
|
||||
- Candidate corpus SHA-256: `e4974c65b704ea3aac7887cddf2707f53c686578fe5c0b1b2aeab42fa10c9d38`.
|
||||
- Local fixture-test subtotals: baseline `1897.437 ms`; candidate `967.448 ms`; comparator-restored candidate `985.561 ms`.
|
||||
|
||||
These are local Vitest replay samples, not CI timings or a controlled benchmark. The evidence supports removal of the repeated pass. It does not claim a fixed CI time reduction.
|
||||
|
||||
## Declaration ledger
|
||||
|
||||
The full-file review covers the corpus test (31 declarations) and the sibling policy test (17 declarations). Each table row below records one declaration. All parameter rows stay unless the ledger says otherwise.
|
||||
|
||||
| File | Parent line | Test declaration | Mark and retained contract |
|
||||
|---|---:|---|---|
|
||||
| native-status-arbiter-corpus.test.ts | 1821 | executes all 53 fixtures in their production consumers and joins all 70 matrix rows | R: Primary real-consumer receipt owner; catches incorrect decisions/effects, missing persisted targets, replay duplication, and incomplete matrix joins. Absorbs the comparator-negative loop. |
|
||||
| native-status-arbiter-corpus.test.ts | 1870 | fails independently when any asserted field category is mutated | C: Keep every comparison-negative assertion, but use the first declaration’s 53 real observations; this declaration currently executes exactly the same fixture list a second time. |
|
||||
| native-status-arbiter-corpus.test.ts | 1900 | fails mapped fixtures when live entrypoints or owning actions are removed despite matching policy labels | R: Distinct live-entrypoint negative controls; policy labels alone must not masquerade as cancellation, attention, workspace reconciliation, rollout, or effect delivery. |
|
||||
| native-status-arbiter-corpus.test.ts | 2066 | retires a proven automatic review and applies the current successful completion exactly once | R: Automatic-review retirement must produce exactly one superseding completion and remain idempotent. |
|
||||
| native-status-arbiter-corpus.test.ts | 2081 | automatic review reconciliation preserves unread child results (%s); rows: queued, claimed, deferred_issue_execution, current_run, current_intent, consumed | R: Distinct six durable wake delivery states govern whether unread child results hold completion; preserve all rows. |
|
||||
| native-status-arbiter-corpus.test.ts | 2113 | rechecks child delivery under the status lock during automatic review reconciliation | R: A child wake arriving between initial assessment and status lock must block a stale completion commit. |
|
||||
| native-status-arbiter-corpus.test.ts | 2138 | new evidence cannot complete reconciliation before a pending child result is consumed | R: Reconciliation policy must prioritize pending child results and emit the release-checkout effect, which the storage-level sibling does not assert. |
|
||||
| native-status-arbiter-corpus.test.ts | 2146 | new-evidence reconciliation respects %s child delivery; rows: queued, consumed | R: Actual queued/consumed wake rows must select the new-evidence reconciliation path and persisted task status; preserve both rows. |
|
||||
| native-status-arbiter-corpus.test.ts | 2164 | completes a new merge run after an old CI review and bounds repeated incomplete results | R: New merge runs may retire old CI reviews, while repeated incomplete reports must terminate bounded repair without manufacturing approvals. |
|
||||
| native-status-arbiter-corpus.test.ts | 2195 | rejects wrong, missing, and duplicate criterion IDs before accepting completion | R: Completion feedback validates exact criterion ID membership/cardinality and remains read-only. |
|
||||
| native-status-arbiter-corpus.test.ts | 2206 | keeps genuine approval actionable in the finish response and in finalization | R: Genuine human approval remains pending; finish feedback escapes prompt-injection titles and rejects failed verification. |
|
||||
| native-status-arbiter-corpus.test.ts | 2231 | preserves answered cards, explicit attention, stronger authority, and later task edits | R: Six independent guard facts prevent unsafe automatic review retirement: resolved response, explicit attention, authority, later status, newer contract, failed workspace. |
|
||||
| native-status-arbiter-corpus.test.ts | 2251 | rejects an empty needs_review report with actionable feedback without inventing an approval | R: Empty needs-review reports fail with actionable feedback and leave review retirement to its owning reconciler. |
|
||||
| native-status-arbiter-corpus.test.ts | 2262 | withdraws obsolete policy reviews, restores the prior status, and is idempotent | R: Obsolete-policy cleanup restores prior status/version while retaining decision history, then becomes idempotent. |
|
||||
| native-status-arbiter-corpus.test.ts | 2276 | leaves an already %s review untouched; rows: accepted, rejected | R: Accepted and rejected interactions are both authoritative responses and must remain untouched; preserve both rows. |
|
||||
| native-status-arbiter-corpus.test.ts | 2286 | preserves real completion reviews and limits cleanup to the requested runs | R: Genuine reviews and unrelated run-scoped cards must not be retired. |
|
||||
| native-status-arbiter-corpus.test.ts | 2296 | dismisses the obsolete card without undoing a later %s status; rows: blocked, done, cancelled | R: Blocked, done, and cancelled later states have distinct cancellation/expiry behavior and must not be overwritten; preserve all rows. |
|
||||
| native-status-arbiter-corpus.test.ts | 2306 | does not restore status after a newer decision or a status change back to review | R: Changed decision pointer and a round-trip status edit independently invalidate restoration authority. |
|
||||
| native-status-arbiter-corpus.test.ts | 2321 | keeps review status when a separate request still needs a response | R: Another pending interaction retains review liveness even after obsolete-card retirement. |
|
||||
| native-status-arbiter-corpus.test.ts | 2332 | isolates a failed cleanup candidate and retries it on the next pass | R: One failed cleanup transaction must not stop other candidates; a later pass retries the failed candidate. |
|
||||
| native-status-arbiter-corpus.test.ts | 2354 | keeps committed cleanup and continues publication after a live event fails | R: Live-event publication failure must not roll back committed cleanup or prevent later publication. |
|
||||
| native-status-arbiter-corpus.test.ts | 2374 | continues native finalization when the obsolete-card lookup fails | R: Obsolete-card lookup failure must not halt native finalization. |
|
||||
| native-status-arbiter-corpus.test.ts | 2388 | preserves a later authoritative status during reconciliation | R: Reconciliation reducer preserves externally advanced blocked state and its explicit authority reason. |
|
||||
| native-status-arbiter-corpus.test.ts | 2399 | supersedes the exact committed coordinator decision and sequences preserve decisions independently of issue status versions | R: Semantic decision sequencing is independent of statusVersion changes and supersedes the exact committed coordinator decision. |
|
||||
| native-status-arbiter-corpus.test.ts | 2469 | keeps assessment lineage run-local while superseding an issue decision from another run | R: Cross-run issue decision supersession must preserve current-run assessment ancestry and durable effect payload linkage. |
|
||||
| native-status-arbiter-corpus.test.ts | 2634 | preserves an intermediate same-run assessment when the issue decision points to an older ancestor | R: An intermediate same-run assessment must not be replaced with the older issue-decision ancestor. |
|
||||
| native-status-arbiter-corpus.test.ts | 2741 | ignores historical committed finalizations superseded by a newer authoritative decision | R: Historical committed finalization must remain untouched after a newer authoritative decision wins. |
|
||||
| native-status-arbiter-corpus.test.ts | 2811 | records run-scoped superseding assessment lineage with %s predecessor; rows: none, same_run, other_run | R: No predecessor, same-run predecessor, and other-run predecessor must all preserve run-local assessment lineage; preserve all rows. |
|
||||
| native-status-arbiter-corpus.test.ts | 2893 | binds a tools-refresh continuation to its existing durable wake without a duplicate | R: Tools-refresh continuation must bind its existing wake exactly once, rather than insert a duplicate. |
|
||||
| native-status-arbiter-corpus.test.ts | 2910 | revalidates a %s monitor under the final disposition lock; rows: scheduled, cleared, replaced, reassigned | R: Scheduled, cleared, replaced, and reassigned monitor authority is rechecked under the status lock; preserve all rows. |
|
||||
| native-status-arbiter-corpus.test.ts | 2943 | fails the transaction closed for an unknown status effect | R: Unknown effect kinds fail the complete transaction closed, leaving no decision/effect/status/coordinator mutation. |
|
||||
| status-arbiter.test.ts | 47 | keeps a verified accepted Cursor plan passive without completing or replaying work | R: Accepted plan wait is passive only with verified authority and still yields to failed terminals, cancelled issues, and governance. |
|
||||
| status-arbiter.test.ts | 68 | a reviewer finishes its decision without completing rejected or still-reviewed work | R: A resolved reviewer action must preserve worker/review task status and avoid retrying the worker assignment. |
|
||||
| status-arbiter.test.ts | 76 | routes an unfinished reviewer action to bounded recovery instead of retrying the worker task | R: Pending reviewer actions require bounded reviewer recovery without generic continuation/retry. |
|
||||
| status-arbiter.test.ts | 85 | treats only the authorized Board response_wake as passive and preserves governance | R: Board response-wait authority, origin, governance, and non-response continuations select distinct policy branches. |
|
||||
| status-arbiter.test.ts | 138 | repairs unfinished response waits once using the server continuation budget | R: Unfinished response waits must use bounded server repair while preserving pause, dependency, governance, external-chat, and conversation authority. |
|
||||
| status-arbiter.test.ts | 172 | marks done only from successful finalization and complete durable evidence | R: Durable evidence and accepted low-risk policy are separate completion authorities; missing evidence alone cannot complete standard work. |
|
||||
| status-arbiter.test.ts | 245 | creates explicit liveness paths for review, continuation, cancellation, and governance | R: Reviewless needs-review reports, retry continuations, external-chat authority, run cancellation, and governance have distinct liveness/effect contracts. |
|
||||
| status-arbiter.test.ts | 379 | accepts low-risk completion claims with unrun verification caveats | R: Explicit unrun-verification caveats are permitted under accepted low-risk completion policy without treating them as failed verification. |
|
||||
| status-arbiter.test.ts | 424 | keeps failed verification with the agent and routes explicit attention to its owner | R: Failed verification overrides low-risk claim acceptance; explicit attention separately creates review authority. |
|
||||
| status-arbiter.test.ts | 485 | blocks only for a task-wide blocker with a named owner and action | R: Task-wide blockers bind named unblock ownership and send the required owner notification. |
|
||||
| status-arbiter.test.ts | 513 | waits for an explicit unblock instead of inventing another productive track | R: Current-track blockers bind explicit unblock actions without inventing a runnable alternate track; policy version is independently guarded. |
|
||||
| status-arbiter.test.ts | 554 | blocks a current-track result when a durable dependency already gates the issue | R: Existing durable dependencies override provider current-track labels without an immediate wake. |
|
||||
| status-arbiter.test.ts | 581 | lets a durable dependency override an optimistic done result | R: Durable dependency blockers also override an optimistic done report with policy claim acceptance. |
|
||||
| status-arbiter.test.ts | 598 | does not create a duplicate review after this run's review was already accepted | R: Accepted human review authority suppresses a second review card for needs-review disposition. |
|
||||
| status-arbiter.test.ts | 626 | does not create a duplicate review after this run's planning confirmation was already accepted | R: Accepted planning approval must also suppress a second card when disposition is blocked; this exercises a distinct allowed disposition. |
|
||||
| status-arbiter.test.ts | 654 | preserves authoritative terminal statuses | R: Authoritative done task status must not be overwritten or produce new liveness effects. |
|
||||
| status-arbiter.test.ts | 663 | routes each explicit request to its own reviewer | R: Mixed human/agent attention requests preserve separate reviewer routing and ownership. |
|
||||
|
||||
## History and stopping point
|
||||
|
||||
The executable corpus and comparison-negative control arrived in #12616. The corpus grew to 53 fixtures in #13038. The empty-effect forbidden-effects negative was corrected in #13266; this change keeps that correction verbatim.
|
||||
|
||||
The remaining declarations protect distinct policy, lifecycle, storage, atomicity, routing, and authority contracts. No further cuts are proposed in this lane.
|
||||
@@ -0,0 +1,432 @@
|
||||
{
|
||||
"baseSHA": "e12e8de07b7cbd0c1b37a921d6db432bbb8d32c5",
|
||||
"testPatchSHA256": "f339d62b12317218d4206eafa1434725a50ded2610285193802c6379afa44506",
|
||||
"tests": [
|
||||
{
|
||||
"file": "cli/src/__tests__/runtime-info.test.ts",
|
||||
"originalSHA256": "ab601b9fef15f75e5685d362a35750a891c0c8b7db466ff7d047010ff9e51714",
|
||||
"candidateSHA256": "a8beaa462ce1220221b6c2628116ccd3f45152e4177ecb3fb75410ac8a28e7c4"
|
||||
},
|
||||
{
|
||||
"file": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.test.ts",
|
||||
"originalSHA256": "501bb3720e431a49f360d60b80e9b92184825920109858077bd5f2ae53ad26fa",
|
||||
"candidateSHA256": "7c00cd14422f2657056a7fd760b4fb5dc66c07e3abcda3abe6356edb8c29731a"
|
||||
},
|
||||
{
|
||||
"file": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.test.ts",
|
||||
"originalSHA256": "4af358ecdac53c1b22b0288939565be015decaefcfa8502bcd2fadb9c8a14c52",
|
||||
"candidateSHA256": "39c4d03eeed86d16184df9173ad0df7c46a2c3eb1fe58f2a2125c965e64e8d0b"
|
||||
},
|
||||
{
|
||||
"file": "packages/adapters/codex-local/src/server/adapter-auth-promotion.test.ts",
|
||||
"originalSHA256": "19e37c4f9e470239408d3710a4ed1ca2414170d6ec83d50f3431593bda79019a",
|
||||
"candidateSHA256": "3be8594756d8d23ff28361e88c5175c4dc043d3ec08a64d802f5c9373c97070d"
|
||||
}
|
||||
],
|
||||
"owners": [
|
||||
{
|
||||
"file": "cli/src/runtime-info.ts",
|
||||
"originalSHA256": "b051153fd3553d71cadccff7d79edc85f6dc74fd87de5563fe91424f05eb90ec",
|
||||
"finalSHA256": "b051153fd3553d71cadccff7d79edc85f6dc74fd87de5563fe91424f05eb90ec",
|
||||
"productionRestored": true
|
||||
},
|
||||
{
|
||||
"file": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.cjs",
|
||||
"originalSHA256": "86d7323e0eb4af87718c48efa4af28fb18b5c925ca42c91cd10337e696b45557",
|
||||
"finalSHA256": "86d7323e0eb4af87718c48efa4af28fb18b5c925ca42c91cd10337e696b45557",
|
||||
"productionRestored": true
|
||||
},
|
||||
{
|
||||
"file": "packages/adapters/codex-local/src/server/adapter-auth-promotion.ts",
|
||||
"originalSHA256": "6065e6e64c53965709acec0fd21431c1b08799bef8a9f2a9fba92f4c367a740a",
|
||||
"finalSHA256": "6065e6e64c53965709acec0fd21431c1b08799bef8a9f2a9fba92f4c367a740a",
|
||||
"productionRestored": true
|
||||
},
|
||||
{
|
||||
"file": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.cjs",
|
||||
"originalSHA256": "573b115eebbb2ccf93dcbae415f1f182862f196a8d10ca3b1c3aafe5ced7ec64",
|
||||
"finalSHA256": "573b115eebbb2ccf93dcbae415f1f182862f196a8d10ca3b1c3aafe5ced7ec64",
|
||||
"productionRestored": true
|
||||
}
|
||||
],
|
||||
"completeFileRuns": [
|
||||
{
|
||||
"label": "baseline",
|
||||
"exitCode": 0,
|
||||
"passed": 177,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/adapter-auth-promotion.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 28
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-cache.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 31
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 17
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 14
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 53
|
||||
},
|
||||
{
|
||||
"name": "cli/src/__tests__/runtime-info.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 3
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 9
|
||||
}
|
||||
],
|
||||
"reportSHA256": "a2a46938d83607e37c114e7b4da42068230185e787fbd08ad97101f30820acec",
|
||||
"logSHA256": "79fc48066d2f3f285d8a270b163121d73c99885c2708bc862c3ba8aa042abec0"
|
||||
},
|
||||
{
|
||||
"label": "candidate",
|
||||
"exitCode": 0,
|
||||
"passed": 170,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/adapter-auth-promotion.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 25
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-cache.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 31
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 17
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 12
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 53
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 10
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 9
|
||||
},
|
||||
{
|
||||
"name": "cli/src/__tests__/runtime-info.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 2
|
||||
}
|
||||
],
|
||||
"reportSHA256": "4604c4120f3a892eda54fa9836a62e1805ee5c48fbfb4e9086eaf3ae8870c782",
|
||||
"logSHA256": "084c1048d51859ee32c514672fd0a4a0828eeea0ca2dc42bf8310d2de46e215c"
|
||||
},
|
||||
{
|
||||
"label": "revert",
|
||||
"exitCode": 0,
|
||||
"passed": 177,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/adapter-auth-promotion.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 28
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-cache.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 31
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 17
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 14
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 53
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 9
|
||||
},
|
||||
{
|
||||
"name": "cli/src/__tests__/runtime-info.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 3
|
||||
}
|
||||
],
|
||||
"reportSHA256": "b60e9b8b949acb1fb603100c59bf9c36cc564858426ebe2576fae3c11a8ab3e7",
|
||||
"logSHA256": "0395a74d8c069e3a8963c297599d94655e3768ccc5c8c6f2ab108cb4c8a3b450"
|
||||
},
|
||||
{
|
||||
"label": "candidate-after-restoration",
|
||||
"exitCode": 0,
|
||||
"passed": 170,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/adapter-auth-promotion.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 25
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-cache.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 31
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 17
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 12
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/codex-local/src/server/codex-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 53
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-copyback.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 10
|
||||
},
|
||||
{
|
||||
"name": "packages/adapters/grok-local/src/server/grok-home.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 9
|
||||
},
|
||||
{
|
||||
"name": "cli/src/__tests__/runtime-info.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 2
|
||||
}
|
||||
],
|
||||
"reportSHA256": "d2d363ab57d607633f12d75eaea17265010e834c7d92bed588334a39e6550867",
|
||||
"logSHA256": "21cfea7bd04b674fccbe2226aebfba9b19dc12b022df995d7c35e779122c01bf"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"label": "mutation-runtime-write",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 1,
|
||||
"reportSHA256": "5271aa251ce2c804eaea9fb345854c61674247c5cf91aaf3f2c043f1177fde69",
|
||||
"logSHA256": "68f6c2f446ef0f1717f1f676f65e999657f9455954279890566a2a777dade6da",
|
||||
"originalSHA256": "b051153fd3553d71cadccff7d79edc85f6dc74fd87de5563fe91424f05eb90ec",
|
||||
"mutatedSHA256": "4bab7a0eb48f9ec37c7912640c544a460e498ecc806200fb086c7f9c23c24490",
|
||||
"owner": "cli/src/runtime-info.ts",
|
||||
"keeper": "runtime info does not remove runtime info owned by a replacement process",
|
||||
"mutationId": "runtime-write",
|
||||
"assertion": "AssertionError: expected null to deeply equal { schemaVersion: 1, \u2026(6) }",
|
||||
"edit": {
|
||||
"from": " fs.renameSync(temporaryPath, filePath);",
|
||||
"to": "omit the atomic rename statement"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-codex-default-absent",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 11,
|
||||
"reportSHA256": "5441f2d038e120fdd62f40fbf0adad3711f5bee66ac214a2f188ad90b798a22a",
|
||||
"logSHA256": "d52cd9f252f1f9ebf4da23f86cacd9682fc7137aef7ec64d311c14074aed2226",
|
||||
"originalSHA256": "86d7323e0eb4af87718c48efa4af28fb18b5c925ca42c91cd10337e696b45557",
|
||||
"mutatedSHA256": "c2140ce41b07a007adc6232b1db6ee308db14f5adfd7e31aa17d509970afa97f",
|
||||
"owner": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.cjs",
|
||||
"keeper": "codex-auth-merge-decision predicate seed mode the leading positional --seed-if-dest-absent flag is parsed before the two path arguments; a default two-path call never enters seed mode",
|
||||
"mutationId": "codex-default-absent",
|
||||
"assertion": "AssertionError: expected 10 to be 20 // Object.is equality",
|
||||
"edit": {
|
||||
"from": " seedIfDestAbsent &&",
|
||||
"to": " true &&"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-codex-seed-absent",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 11,
|
||||
"reportSHA256": "0784aed1b34cb2dfa13d7c93ac10d5ef8a9f38da153eebce234c762fab6cb628",
|
||||
"logSHA256": "02e7b7367bdecc722e3a57bffb8974dee0afc2c0304f23dcea76db3a13ce8ad6",
|
||||
"originalSHA256": "86d7323e0eb4af87718c48efa4af28fb18b5c925ca42c91cd10337e696b45557",
|
||||
"mutatedSHA256": "867730de34972c627b235a1499aee126ab428dd899098e5b1d30d450dd764113",
|
||||
"owner": "packages/adapters/codex-local/src/server/codex-auth-merge-decision.cjs",
|
||||
"keeper": "codex-auth-merge-decision predicate seed mode the leading positional --seed-if-dest-absent flag is parsed before the two path arguments; a default two-path call never enters seed mode",
|
||||
"mutationId": "codex-seed-absent",
|
||||
"assertion": "AssertionError: expected 20 to be 10 // Object.is equality",
|
||||
"edit": {
|
||||
"from": " return sourceIsImplausible ? IMPLAUSIBLE_LAST_REFRESH : USE_SOURCE;",
|
||||
"to": " return KEEP_DESTINATION;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-grok-iso-expiry",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 9,
|
||||
"reportSHA256": "3aa14c8f3dc9a4720743926fb0f62601aba8a7cc862d154064ab32b17cc3e2fd",
|
||||
"logSHA256": "f7835d91b6d8bbdd039ca56cc83205de798b82b4ce791b33a546a2d7cde7bb25",
|
||||
"originalSHA256": "573b115eebbb2ccf93dcbae415f1f182862f196a8d10ca3b1c3aafe5ced7ec64",
|
||||
"mutatedSHA256": "20a815272eff6fefe592f09e55d9d690ca3eee41e98145b4447fcdac9828d856",
|
||||
"owner": "packages/adapters/grok-local/src/server/grok-auth-merge-decision.cjs",
|
||||
"keeper": "grok-auth-merge-decision predicate reads an ISO-8601 expiry, an epoch-seconds expiry, and an epoch-milliseconds expiry",
|
||||
"mutationId": "grok-iso-expiry",
|
||||
"assertion": "AssertionError: ISO-8601: expected 20 to be 10 // Object.is equality",
|
||||
"edit": {
|
||||
"from": " const ms = Date.parse(raw);",
|
||||
"to": " const ms = 0;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-promotion-company-home",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 24,
|
||||
"reportSHA256": "aca6aa3dc842e1f1ba1c956b245393de4d2ee993d8da156b960b0af6fd69c2bc",
|
||||
"logSHA256": "0fa22f697fbc4b042cb58ad1efe6d8db0459a6fe129e584b4666cfc421cad8f8",
|
||||
"originalSHA256": "6065e6e64c53965709acec0fd21431c1b08799bef8a9f2a9fba92f4c367a740a",
|
||||
"mutatedSHA256": "cf54fc5ca674868f4b98f8af0700800153837fc5db26066aec99eb530127f06e",
|
||||
"owner": "packages/adapters/codex-local/src/server/adapter-auth-promotion.ts",
|
||||
"keeper": "device-login credential promotion a user login seeds this account's own home and the company default home",
|
||||
"mutationId": "promotion-company-home",
|
||||
"assertion": "AssertionError: expected 'wrong-account' to be 'acct-42' // Object.is equality",
|
||||
"edit": {
|
||||
"from": " sourceBytes: authBytes,\n destinationPath: companyHomeAuthPath,",
|
||||
"to": " sourceBytes: Buffer.from(JSON.stringify({ ...JSON.parse(authBytes.toString()), tokens: { ...JSON.parse(authBytes.toString()).tokens, account_id: \"wrong-account\" } })),\n destinationPath: companyHomeAuthPath,"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-promotion-account-id",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 24,
|
||||
"reportSHA256": "73d41f793829a226a0aee3cbb12b66702353f9d83bbc7e3d146f72486f193a43",
|
||||
"logSHA256": "4deb6470827ab1cd43e3761b43ec4f73b16dbd8a6941bbf92b50c7f527d7b549",
|
||||
"originalSHA256": "6065e6e64c53965709acec0fd21431c1b08799bef8a9f2a9fba92f4c367a740a",
|
||||
"mutatedSHA256": "d1b3f413f0c9e5a0c2f3fd57e37f4eaf38254884c4adb894b8e2fb03605272c0",
|
||||
"owner": "packages/adapters/codex-local/src/server/adapter-auth-promotion.ts",
|
||||
"keeper": "device-login credential promotion a user login seeds this account's own home and the company default home",
|
||||
"mutationId": "promotion-account-id",
|
||||
"assertion": "AssertionError: expected 'wrong-account' to be 'acct-42' // Object.is equality",
|
||||
"edit": {
|
||||
"from": " outcome: accountHomeOutcome === \"written\" ? \"promoted\" : \"kept\",\n accountId,",
|
||||
"to": " outcome: accountHomeOutcome === \"written\" ? \"promoted\" : \"kept\",\n accountId: \"wrong-account\","
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-promotion-account-home",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 24,
|
||||
"reportSHA256": "2202e3a9350c8cccbbf880e728055c5703c594d1480a62525858842db7052f25",
|
||||
"logSHA256": "daa89ab14e44be704e8a6926bda850d0c2e214bf602cff161bc00c5b3fa930b7",
|
||||
"originalSHA256": "6065e6e64c53965709acec0fd21431c1b08799bef8a9f2a9fba92f4c367a740a",
|
||||
"mutatedSHA256": "b03b3fac62ac26b179b13505268fbfd59cbf63213f10da87835f33fd5c54843d",
|
||||
"owner": "packages/adapters/codex-local/src/server/adapter-auth-promotion.ts",
|
||||
"keeper": "device-login credential promotion a user login seeds this account's own home and the company default home",
|
||||
"mutationId": "promotion-account-home",
|
||||
"assertion": "AssertionError: expected 'wrong-home' to be the exact independently resolved account-home path",
|
||||
"edit": {
|
||||
"from": " outcome: accountHomeOutcome === \"written\" ? \"promoted\" : \"kept\",\n accountId,\n accountHomeDir,",
|
||||
"to": " outcome: accountHomeOutcome === \"written\" ? \"promoted\" : \"kept\",\n accountId,\n accountHomeDir: \"wrong-home\","
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "mutation-promotion-created-receipt",
|
||||
"exitCode": 1,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"skipped": 24,
|
||||
"reportSHA256": "3ded03074b1f22efa84f940fbc17a91465b4f65434010e0b2b8ccc9fb16a3fa8",
|
||||
"logSHA256": "c12316bc00a075f589b03d2b6686120d39cd8ec89763d22599546801fcf4900f",
|
||||
"originalSHA256": "6065e6e64c53965709acec0fd21431c1b08799bef8a9f2a9fba92f4c367a740a",
|
||||
"mutatedSHA256": "3d13658dcc9bfeab21ace2d5f44d30bd6490e73bdacb18810ab3415822bc4956",
|
||||
"owner": "packages/adapters/codex-local/src/server/adapter-auth-promotion.ts",
|
||||
"keeper": "device-login credential promotion a user login seeds this account's own home and the company default home",
|
||||
"mutationId": "promotion-created-receipt",
|
||||
"assertion": "AssertionError: expected false to be true // Object.is equality",
|
||||
"edit": {
|
||||
"from": " outcome: accountHomeOutcome === \"written\" ? \"promoted\" : \"kept\",\n accountId,\n accountHomeDir,\n accountHomeCreated,",
|
||||
"to": " outcome: accountHomeOutcome === \"written\" ? \"promoted\" : \"kept\",\n accountId,\n accountHomeDir,\n accountHomeCreated: false,"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
# Test audit R12: runtime and credential owner coverage
|
||||
|
||||
Date: 2026-10-12. Base: `e12e8de07b7cbd0c1b37a921d6db432bbb8d32c5`, the head of [R11](https://github.com/paperclipai/paperclip/pull/16078). Branch: `codex/test-audit-r12-runtime-auth`.
|
||||
|
||||
This change removes **seven redundant registered executions and 76 net test lines** across four files. It changes no production code. Each retired case repeats an existing owner invocation. Three result assertions move into the existing stronger first-login case. No wall-time improvement is inferred from these counts.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| File | Retired exact case | Retained keeper and observation |
|
||||
| --- | --- | --- |
|
||||
| `cli/src/__tests__/runtime-info.test.ts` | `writes and reads the selected runtime endpoint` | `does not remove runtime info owned by a replacement process`: same fresh fixture, write and exact read; wrong-PID preservation and matching-PID removal also remain. |
|
||||
| `packages/adapters/codex-local/src/server/codex-auth-merge-decision.test.ts` | `default mode keeps destination when destination is absent (host-default fail-closed)` | `the leading positional --seed-if-dest-absent flag is parsed before the two path arguments; a default two-path call never enters seed mode`: identical subscription bytes and absent destination, actual subprocess, exit 20. |
|
||||
| Same Codex decision file | `seed mode uses source when destination is absent and source is a usable subscription credential` | Same leading-flag keeper: identical opt-in call, actual subprocess, exit 10. |
|
||||
| `packages/adapters/grok-local/src/server/grok-auth-merge-decision.test.ts` | `uses the source when the identity matches and the source expiry is later` | `reads an ISO-8601 expiry, an epoch-seconds expiry, and an epoch-milliseconds expiry`: ISO row retains identical source/destination credential shape, markers and later-expiry relation through actual files and subprocess; all encodings remain. |
|
||||
| `packages/adapters/codex-local/src/server/adapter-auth-promotion.test.ts` | `promotion returns the account id and the account home path` | `a user login seeds this account's own home and the company default home`: identical fresh-root input and callbacks. Exact account ID and account-home path assertions move unchanged into this keeper. |
|
||||
| Same promotion file | `promotion reports accountHomeCreated true for a first login of an account` | Same first-login keeper gains the exact true receipt. Repeat-login and concurrent-created receipts remain separate. |
|
||||
| Same promotion file | `promotion seeds the company default home when it holds no usable credential` | Same first-login keeper already reads the company default home's actual account ID and also reads the account home, checks promoted outcome and forbids instance-global writes. |
|
||||
|
||||
The retired invocations comprise one runtime-info write/read scenario, two Codex decision subprocesses, one Grok decision subprocess and three fresh-login promotions. Table registration and internal case execution are unchanged in the keepers. This is reduced duplicate execution, not only shorter test setup.
|
||||
|
||||
Net test lines: runtime-info 6; Codex decision 17; Grok decision 7; promotion 46, including five added assertion lines. Total: 81 deletions and five additions. The evidence files are excluded from those savings.
|
||||
|
||||
## Preserved contracts
|
||||
|
||||
The runtime file's malformed-input and process-ownership guards remain. Both decision suites retain identity isolation, unusable credentials, strict freshness and ties, exact clock bounds and encoding rejection. Promotion retains readiness, user-initiated/sole-owner gates, company isolation, account separation, repeat and concurrent creation, persistence failures, symlink replacement and secret redaction. No API, schema, migration, platform, lifecycle or security contract is retired.
|
||||
|
||||
## Replay on the exact stacked base
|
||||
|
||||
Nine complete existing files ran through the repository's real Vitest project configuration with isolated home, config, instance ID and temporary paths. The four owner files contain 56 original or 49 candidate cases. Five unchanged auth-cache, copyback and home siblings contain 121 cases.
|
||||
|
||||
| Phase | Passed | Failed | Skipped |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Original baseline | 177 | 0 | 0 |
|
||||
| Candidate | 170 | 0 | 0 |
|
||||
| Reverted original tests | 177 | 0 | 0 |
|
||||
| Candidate after owner restoration | 170 | 0 | 0 |
|
||||
|
||||
Run counts overlap and must not be added. Source-only filtering excludes build output. Existing pinned external dependencies are read-only; workspace dependency links point into this managed checkout. The initial discovery export was not used as PR qualification.
|
||||
|
||||
## Owner mutation controls
|
||||
|
||||
Eight deliberate changes to the real production owners each failed exactly one retained keeper at its intended assertion. Each owner was restored byte for byte in `finally` before the next run.
|
||||
|
||||
| Deliberate owner regression | Failed observation |
|
||||
| --- | --- |
|
||||
| Omit atomic runtime-file publication | Exact persisted runtime object: null instead of the written object. |
|
||||
| Seed an absent destination without the Codex opt-in flag | Default call: exit 10 instead of 20. |
|
||||
| Keep an absent destination despite the Codex opt-in flag | Seed call: exit 20 instead of 10. |
|
||||
| Erase the decoded Grok ISO expiry | ISO row: exit 20 instead of 10. |
|
||||
| Write the wrong identity to the company default home | Actual stored account ID differs. |
|
||||
| Return the wrong account ID after real promotion | Migrated result account ID differs. |
|
||||
| Return the wrong account-home path after real promotion | Migrated exact result path differs. |
|
||||
| Report false after first account-home creation | Migrated creation receipt is false instead of true. |
|
||||
|
||||
Name-filtered mutation runs skip the unrelated cases in their complete owner file. Those intentional filter skips are separate from the four complete-file controls, which have zero skips. The harness counts one failed case, rather than assuming Vitest's nested suite count equals one. No timeout, mock result, fixture receipt or setup error supplies a mutation failure.
|
||||
|
||||
[The adjacent controls record](2026-10-12-test-audit-r12-runtime-auth-controls.json) retains exact original/candidate test hashes, restored owner hashes, run/log digests, production edits and actual failing keeper assertions. Compile, build and current-head CI/review results are reported separately on the draft PR.
|
||||
@@ -0,0 +1,93 @@
|
||||
# 2026-10-12 Git credential proof ownership map
|
||||
|
||||
The Git credential owner now holds all 14 existing redaction input/output assertions. The workspace suite no longer repeats that direct helper contract through a compatibility re-export. Runtime workspace code already imports the canonical helper directly, so the obsolete heartbeat import/re-export shim can be removed.
|
||||
|
||||
Parent: public PR #16079 at `2e17a5ed43ecb0002f7f0e2fe5c1017f35d7ffab`. Branch: `codex/test-audit-r13-git-credentials`.
|
||||
|
||||
## Scope and retained contract
|
||||
|
||||
| Item | Before | After |
|
||||
|---|---:|---:|
|
||||
| Direct redaction input/output assertions | 14 | 14 |
|
||||
| Direct redaction helper calls | 14 | 14 |
|
||||
| Registered keeper/workspace cases | 206 | 201 |
|
||||
| Test source lines in the changed files | 3,705 | 3,687 |
|
||||
| Obsolete heartbeat shim source lines | 6 | 0 |
|
||||
|
||||
The net reduction is 18 test source lines and six production shim lines. This is proof ownership and seam cleanup. It does not remove helper execution or establish a CI time reduction.
|
||||
|
||||
Removed workspace declarations at parent lines 949–986 preserve their complete bodies inside the four existing keeper declarations at `server/src/__tests__/git-credentials.test.ts:282`. Their five wrappers and the heartbeat import are removed. The six-line import/comment/re-export block at `server/src/services/heartbeat.ts:323–328` is the only production change.
|
||||
|
||||
All original input/output pairs were compared as a multiset after resolving the local string constants. Both sides contain exactly 14 pairs and have this canonical SHA-256: `78ff3b460616782e2380217c22ba00dbe757203a9e34d1c38d1ea294a4c5a9e0`.
|
||||
|
||||
## Assertion relocation map
|
||||
|
||||
| Original file and assertion line | Candidate keeper assertion line | Protected case |
|
||||
|---|---:|---|
|
||||
| git-credentials.test.ts:284 | 284 | HTTPS userinfo |
|
||||
| git-credentials.test.ts:290 | 296 | SSH userinfo |
|
||||
| git-credentials.test.ts:293 | 299 | scp-style remote remains unchanged |
|
||||
| git-credentials.test.ts:299 | 311 | URL query with token and reference |
|
||||
| git-credentials.test.ts:302 | 314 | arbitrary query parameter name |
|
||||
| git-credentials.test.ts:308 | 326 | credential-free error text |
|
||||
| heartbeat-workspace-session.test.ts:951 | 287 | quoted HTTPS userinfo in a Git error |
|
||||
| heartbeat-workspace-session.test.ts:957 | 290 | bare username and multiple URLs |
|
||||
| heartbeat-workspace-session.test.ts:963 | 302 | SSH and git+ssh userinfo in one error |
|
||||
| heartbeat-workspace-session.test.ts:966 | 305 | scp-style remote with surrounding text |
|
||||
| heartbeat-workspace-session.test.ts:972 | 317 | quoted query in a Git error |
|
||||
| heartbeat-workspace-session.test.ts:975 | 320 | query with trailing error text |
|
||||
| heartbeat-workspace-session.test.ts:982 | 328 | credential-free URL error |
|
||||
| heartbeat-workspace-session.test.ts:984 | 330 | plain text with no URL |
|
||||
|
||||
## Scoped declaration ledger
|
||||
|
||||
| Parent declaration | Mark | Keeper and retained contract |
|
||||
|---|---|---|
|
||||
| git-credentials.test.ts:283 — masks URL userinfo | R | Keeps HTTPS userinfo and absorbs quoted errors, bare usernames, and multiple URLs. |
|
||||
| git-credentials.test.ts:289 — non-HTTP userinfo and scp remotes | R | Keeps SSH/scp cases and absorbs git+ssh and surrounding-message cases. |
|
||||
| git-credentials.test.ts:298 — complete query masking | R | Keeps arbitrary query keys and absorbs quote/trailing-message boundaries. |
|
||||
| git-credentials.test.ts:307 — credential-free text | R | Keeps plain errors and absorbs credential-free URL/text examples. |
|
||||
| heartbeat-workspace-session.test.ts:950 — URL userinfo | C | Move its unchanged assertion into the existing userinfo keeper. |
|
||||
| heartbeat-workspace-session.test.ts:956 — bare usernames and multiple URLs | C | Move its unchanged assertion into the existing userinfo keeper. |
|
||||
| heartbeat-workspace-session.test.ts:962 — non-HTTP userinfo and scp remotes | C | Move both unchanged assertions into the non-HTTP keeper. |
|
||||
| heartbeat-workspace-session.test.ts:971 — complete query masking | C | Move both unchanged assertions into the query keeper. |
|
||||
| heartbeat-workspace-session.test.ts:980 — credential-free text | C | Move both unchanged assertions and their local constants into the plain-text keeper. |
|
||||
| heartbeat.ts:323–328 — scrubber re-export shim | D | No runtime importer remains through heartbeat. Workspace code uses the canonical Git credential owner. |
|
||||
|
||||
Other keeper and workspace declarations stay unchanged. This is a scoped ledger, not a claim that all 206 cases were deletion candidates.
|
||||
|
||||
## Production callers and history
|
||||
|
||||
The only production callers are in `server/src/services/heartbeat/workspaces.ts`: import at line 47 and calls at lines 602, 3545, and 3553. These callers already import `scrubGitCredentialText` from `git-credentials.ts`; their code stays unchanged. Repository search found no other heartbeat re-export consumer after the workspace test import was removed.
|
||||
|
||||
#10719 introduced the workspace redaction proof. #10720 established the Git credential owner. #10722 removed a duplicate implementation and retained the heartbeat re-export so older imports, including the workspace tests, still worked. The canonical implementation and its production callers now remain; the obsolete test-only import path is removed.
|
||||
|
||||
## Exact-parent verification
|
||||
|
||||
| Phase | Result |
|
||||
|---|---|
|
||||
| Keeper/workspace baseline | 206/206 passed |
|
||||
| Candidate | 201/201 passed |
|
||||
| Production owner mutation | 24 passed; two intended keeper failures |
|
||||
| Production owner restored byte for byte | 201/201 passed |
|
||||
| Real managed-checkout caller sibling | 19/19 passed |
|
||||
|
||||
Mutation: change the userinfo mask regex from `/gi` to `/i` in `server/src/services/git-credentials.ts:111`. The helper leaks credentials in later URLs. The keeper fails in the two migrated multi-URL assertions at candidate lines 290 and 302, whose assertions finish at lines 292 and 304. The production helper was restored to its exact parent bytes before the final replay.
|
||||
|
||||
Run from `server/`:
|
||||
|
||||
```sh
|
||||
node ../node_modules/vitest/vitest.mjs run --config vitest.config.ts src/__tests__/git-credentials.test.ts src/__tests__/heartbeat-workspace-session.test.ts
|
||||
node ../node_modules/vitest/vitest.mjs run --config vitest.config.ts src/__tests__/heartbeat-managed-clone-credentials.test.ts
|
||||
```
|
||||
|
||||
Repo-wide typecheck, the full Vitest suite, and build were not run locally for this narrow owner consolidation. Draft PR checks must pass before a merge-ready handoff.
|
||||
|
||||
## Source hashes
|
||||
|
||||
| File | Parent SHA-256 | Candidate SHA-256 |
|
||||
|---|---|---|
|
||||
| server/src/__tests__/git-credentials.test.ts | `e236377145742b8074be8d61ff11b50812815436c5d188133f95475cd731384d` | `29e12200d45e8a17098bcc8571d82fc2ea3f68422b7cc04271a79b52b4f32ae0` |
|
||||
| server/src/__tests__/heartbeat-workspace-session.test.ts | `ad26f3a88c25da94502889fd3f0f158b81bd1bed03332880a383a45437703761` | `93480b2a883bb0ad075cbf143abe302225ddfd01e333431999c95e378cd8a6c3` |
|
||||
| server/src/services/heartbeat.ts | `0fb791d721262d024ef2935b3045f4a5489adfa5853d2d54dab15c639d26fe54` | `9ac217e97e5319c850924f083df32be43df11f33f8970ff8efb4a5f776a0c1d9` |
|
||||
| server/src/services/git-credentials.ts | `0e8bc7031cc7000db3d9828eb84fbd2c22d19657bfcdc5e287f95cc01e05934f` | `0e8bc7031cc7000db3d9828eb84fbd2c22d19657bfcdc5e287f95cc01e05934f` |
|
||||
@@ -0,0 +1,62 @@
|
||||
# Environment runtime test audit — 2026-10-12
|
||||
|
||||
Parent: #16083 (`codex/test-audit-r14-browser`), exact head
|
||||
`1113d4c9eaa49311c9b269c96b79bbae06d42e25`.
|
||||
|
||||
## Change and keeper
|
||||
|
||||
`server/src/__tests__/environment-runtime.test.ts:1405–1420` repeats the local
|
||||
projectless acquisition already executed by the lifecycle test at1371. Both
|
||||
use the same `seedEnvironment()` call, runtime, suite hooks and acquisition
|
||||
input. The seed/acquire statements are AST-identical. Both tests originated
|
||||
in `70679a33216` (Add sandbox environment support, #4415).
|
||||
|
||||
Move the unique `lease.executionWorkspaceId` null assertion into the keeper
|
||||
at1383. Keep the exact `leaseContext` equality, which already checks its two
|
||||
null fields. Every original keeper statement remains unchanged and ordered.
|
||||
The keeper also releases the lease and checks the durable released row.
|
||||
|
||||
One file: +2/-17 test lines, net15 fewer lines. One registered case and one
|
||||
redundant seed/acquisition/setup/cleanup cycle disappear (162→161 cases).
|
||||
No production behavior changes. Provider, SSH, company access, secret
|
||||
resolution/redaction, timeout precedence, session and recovery cases remain.
|
||||
|
||||
## Exact-parent proof
|
||||
|
||||
| Phase | Passed | Failed | Filtered/pending |
|
||||
|---|---|---|---|
|
||||
| Full baseline | 162 | 0 | 0 |
|
||||
| Full candidate | 161 | 0 | 0 |
|
||||
| Unmutated keeper control | 1 | 0 | 160 |
|
||||
| Production mutation, intended keeper | 0 | 1 | 160 |
|
||||
| Owner restored, full candidate | 161 | 0 | 0 |
|
||||
| All source reverted, full parent | 162 | 0 | 0 |
|
||||
|
||||
Proof-only production mutation at `environment-runtime.ts:4047`: acquisition
|
||||
returns `lease: { ...lease, executionWorkspaceId: lease.executionWorkspaceId
|
||||
?? input.environment.id }`. This real return-mapping fault leaves context and
|
||||
DB setup intact. The relocated assertion at1383:49 rejects the environment
|
||||
UUID because a projectless lease must return null. Production bytes were
|
||||
restored before the full candidate replay. The mutation is not in this PR.
|
||||
|
||||
The first reverted run had a suite-level startup failure (3 pure tests passed,
|
||||
159 DB cases pending, no assertion failure). JSON-only output omitted the
|
||||
hook diagnostic. An unchanged-source rerun with the default reporter passed
|
||||
162/162. The failed attempt remains in the temporary receipt. No CI speedup
|
||||
is claimed from the single local samples.
|
||||
|
||||
## Hashes and receipts
|
||||
|
||||
- Parent test SHA-256: `a7370481526c91044a020cf0e56a5ab4f27a7289c6ee8bcf94e414d7b70ab35a`.
|
||||
- Candidate test SHA-256: `39069f6d8261886b9c9521a7e3e5026051d90287b5e14c471d589fde384832e7`.
|
||||
- Production owner SHA-256: `77da04770ef8e87dd868f90a65c10ae2322a95ad1d8d78e73facb1be3443c0f8`.
|
||||
- Identical seed/acquisition AST SHA-256: `d25af395dc9775474276cefbedd2d736ddfca7c57263db5543c603e52b6f96d3`.
|
||||
|
||||
Full read: 7,223 lines, 100 declarations, 162 cases, two suites and four hooks.
|
||||
The full ledger, case mapping, patch, preservation proof and JSON/log receipts
|
||||
are in `/private/tmp/test-audit-environment-runtime/`.
|
||||
|
||||
Local verification uses the full owner with the unchanged server Vitest
|
||||
configuration. Full repository test/build preflight is deferred to isolated
|
||||
CI because this audit worktree shares dependency symlinks and preflight
|
||||
rewrites workspace package links. CI must pass before a green handoff.
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"parentPR": 16086,
|
||||
"parentBranch": "codex/test-audit-r15-environment-runtime",
|
||||
"parentHead": "c0414ac02e625a1e635857895b74581c37201500",
|
||||
"result": "PROVEN",
|
||||
"registrationsBefore": 399,
|
||||
"registrationsAfter": 398,
|
||||
"declarationsBefore": 249,
|
||||
"declarationsAfter": 248,
|
||||
"retiredCase": "heartbeat orphaned process recovery re-enqueues continuation for stranded in-progress work with no active run",
|
||||
"keeper": "heartbeat orphaned process recovery re-enqueues stranded in-progress work after a failed/process_lost run before escalating",
|
||||
"deletedTestLOC": 31,
|
||||
"movedAssertionLOC": 5,
|
||||
"netTestLOCDelta": -26,
|
||||
"candidateTestSHA256": "645fd53b52e513e97c83f1e074dc1bded0749be18fb086138d154c19ea18d1d6",
|
||||
"parentSourceHashes": {
|
||||
"server/src/__tests__/heartbeat-process-recovery.test.ts": "bf7c888eaba44a56d12823bb948026d5db44f2249a75a6cf083343edbd7c0673",
|
||||
"server/src/services/heartbeat/retries.ts": "9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a",
|
||||
"server/src/services/heartbeat.ts": "9ac217e97e5319c850924f083df32be43df11f33f8970ff8efb4a5f776a0c1d9",
|
||||
"server/src/services/recovery/service.ts": "c414df876e7df64e701a752513db8e5cf7365589294d50da0e1396788a9207db"
|
||||
},
|
||||
"sourceRestoredBeforeBranchCreation": true,
|
||||
"controls": [
|
||||
{
|
||||
"phase": "baseline",
|
||||
"exitCode": 0,
|
||||
"tests": 399,
|
||||
"passed": 399,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"failures": [],
|
||||
"ownerSHA256": "9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a",
|
||||
"testSHA256": "bf7c888eaba44a56d12823bb948026d5db44f2249a75a6cf083343edbd7c0673"
|
||||
},
|
||||
{
|
||||
"phase": "candidate",
|
||||
"exitCode": 0,
|
||||
"tests": 398,
|
||||
"passed": 398,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"failures": [],
|
||||
"ownerSHA256": "9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a",
|
||||
"testSHA256": "645fd53b52e513e97c83f1e074dc1bded0749be18fb086138d154c19ea18d1d6"
|
||||
},
|
||||
{
|
||||
"phase": "revert",
|
||||
"exitCode": 0,
|
||||
"tests": 399,
|
||||
"passed": 399,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"failures": [],
|
||||
"ownerSHA256": "9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a",
|
||||
"testSHA256": "bf7c888eaba44a56d12823bb948026d5db44f2249a75a6cf083343edbd7c0673"
|
||||
},
|
||||
{
|
||||
"phase": "mutation-keeper",
|
||||
"exitCode": 1,
|
||||
"tests": 398,
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"pending": 397,
|
||||
"failures": [
|
||||
{
|
||||
"name": "heartbeat orphaned process recovery re-enqueues stranded in-progress work after a failed/process_lost run before escalating",
|
||||
"messages": [
|
||||
"AssertionError: expected { \u2026(78) } to match object { status: 'scheduled_retry', \u2026(1) }\n(84 matching properties omitted from actual)\n at Proxy.<anonymous> (file://<dependency-checkout>/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2050:10)\n at Proxy.<anonymous> (file://<dependency-checkout>/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file://<dependency-checkout>/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at server/src/__tests__/heartbeat-process-recovery.test.ts:10386:24\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://<dependency-checkout>/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
],
|
||||
"ownerSHA256": "cff836de7a98e47e845ad7f7393e4c98fcfea3fecb0ba5bdb9fcc92fc9673c84",
|
||||
"testSHA256": "645fd53b52e513e97c83f1e074dc1bded0749be18fb086138d154c19ea18d1d6"
|
||||
},
|
||||
{
|
||||
"phase": "restoration",
|
||||
"exitCode": 0,
|
||||
"tests": 398,
|
||||
"passed": 398,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"failures": [],
|
||||
"ownerSHA256": "9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a",
|
||||
"testSHA256": "645fd53b52e513e97c83f1e074dc1bded0749be18fb086138d154c19ea18d1d6"
|
||||
}
|
||||
],
|
||||
"temporaryMutation": {
|
||||
"owner": "server/src/services/heartbeat/retries.ts",
|
||||
"line": 1049,
|
||||
"before": "scheduledRetryAttempt: schedule.attempt",
|
||||
"after": "scheduledRetryAttempt: schedule.attempt + 1",
|
||||
"keeperAssertionLine": 10386,
|
||||
"selection": "Only exact failed/process_lost keeper selected; all other cases are unselected.",
|
||||
"expectedAttempt": 1,
|
||||
"observedAttempt": 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
# Heartbeat recovery test audit — 2026-10-12
|
||||
|
||||
Parent: #16086 (`codex/test-audit-r15-environment-runtime`), exact head
|
||||
`c0414ac02e625a1e635857895b74581c37201500`.
|
||||
|
||||
## Change and keeper
|
||||
|
||||
The standalone `re-enqueues continuation for stranded in-progress work with no active run`
|
||||
case at `heartbeat-process-recovery.test.ts:11192–11221` repeats the existing
|
||||
`re-enqueues stranded in-progress work after a failed/process_lost run before escalating`
|
||||
table row at 10360.
|
||||
|
||||
Both call `seedStrandedIssueFixture` with `status: in_progress` and
|
||||
`runStatus: failed`. The standalone test omits `runErrorCode`; the helper at 1132–1136
|
||||
persists `process_lost` as its default. The table supplies that same value.
|
||||
Every other supplied argument, default, and owner action matches. Generated
|
||||
record IDs vary between independent fixture runs.
|
||||
|
||||
Move five unique assertion lines into the keeper: a truthy retry ID and
|
||||
`{ status: scheduled_retry, scheduledRetryAttempt: 1 }`. Existing keeper checks
|
||||
retain zero dispatch, one continuation, no escalation, exact issue IDs, two
|
||||
agent runs, `transient_failure`, and absent `modelProfile`. The keeper also
|
||||
checks source task/run binding, no recovery children, and eventual settlement.
|
||||
The failed/adapter_failed and timed_out/adapter_timed_out rows remain and gain
|
||||
the durable retry assertions.
|
||||
|
||||
One test file: +5/−31 lines, net 26 fewer test lines. One registered execution
|
||||
is retired (399→398). This is execution retirement, with no production change.
|
||||
Crash/restart, ownership, cancellation, persistence, retry-budget, and failure
|
||||
isolation contracts remain. No CI time saving is claimed from local samples.
|
||||
|
||||
## Exact-parent sequential proof
|
||||
|
||||
| Phase | Passed | Failed | Filtered/pending |
|
||||
|---|---|---|---|
|
||||
| Full baseline | 399 | 0 | 0 |
|
||||
| Full candidate | 398 | 0 | 0 |
|
||||
| Full revert | 399 | 0 | 0 |
|
||||
| Temporary production mutation, exact keeper | 0 | 1 | 397 |
|
||||
| Owner restored, full candidate | 398 | 0 | 0 |
|
||||
|
||||
The mutation changes the real scheduler write at `heartbeat/retries.ts:1049`
|
||||
from `schedule.attempt` to `schedule.attempt + 1`. The exact keeper fails at
|
||||
the moved retry-state assertion, candidate line 10386, expecting attempt 1 and receiving 2. Only that keeper is
|
||||
selected; other cases are filtered. The persisted attempt feeds durable
|
||||
failure-retry accounting in `execution-recovery-attempt.ts:40–49`.
|
||||
The recovery/scheduler owner is not mocked. The mutation is absent from this PR.
|
||||
After restoring the owner, all 398 candidate cases pass. Both source files were
|
||||
then restored byte-for-byte and the checkout was clean before branch creation.
|
||||
The final patch is the exact candidate bytes that passed restoration.
|
||||
|
||||
Runtime-title comparison removes only the standalone registration and adds
|
||||
no cases. The original full-file ledger contains 249 declarations / 399 runtime
|
||||
cases; the candidate has 248 / 398. All declarations were reviewed. Near-overlaps
|
||||
with different queued wakes, timer claims, continuation ancestry, or crash points
|
||||
were retained.
|
||||
|
||||
```sh
|
||||
PAPERCLIP_HOME=/private/tmp/r16-hbr/h \
|
||||
PAPERCLIP_CONFIG=/private/tmp/r16-hbr/h/config.json \
|
||||
PAPERCLIP_INSTANCE_ID=heartbeat-recovery-audit TMPDIR=/private/tmp/r16-hbr/t NODE_ENV=test \
|
||||
node node_modules/vitest/vitest.mjs run --project @paperclipai/server \
|
||||
server/src/__tests__/heartbeat-process-recovery.test.ts --exclude '**/dist/**'
|
||||
```
|
||||
|
||||
Full repository typecheck/build/test preflight is delegated to isolated CI,
|
||||
as in the parent audit. The local worktree uses shared external dependency
|
||||
symlinks, and preflight rewrites workspace links. Every CI gate must pass
|
||||
before a green handoff. The full owner uses the checked-in server configuration
|
||||
and its real embedded Postgres.
|
||||
|
||||
## Hashes and receipts
|
||||
|
||||
- Parent test SHA-256: `bf7c888eaba44a56d12823bb948026d5db44f2249a75a6cf083343edbd7c0673`.
|
||||
- Candidate test SHA-256: `645fd53b52e513e97c83f1e074dc1bded0749be18fb086138d154c19ea18d1d6`.
|
||||
- Restored production retry owner SHA-256: `9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a`.
|
||||
|
||||
Machine receipts are in `2026-10-12-test-audit-r16-heartbeat-recovery-controls.json`.
|
||||
Full JSON/log output, patch, exact keeper map, and whole-file ledger are in
|
||||
`/private/tmp/r16-heartbeat-recovery/`. The earlier read-only discovery is in
|
||||
`/private/tmp/heartbeat-recovery-full-audit/`. This parent replay uses fresh
|
||||
sequential controls; it does not reuse the earlier parent proof.
|
||||
|
||||
Model: OpenAI `gpt-6.1-sol`; the host does not expose its context-window size.
|
||||
@@ -0,0 +1,41 @@
|
||||
# IssueDetail test audit — 2026-10-12
|
||||
|
||||
Parent: #16088, `codex/test-audit-r16-heartbeat-recovery`, exact head
|
||||
`a454638931ee8a2e40347aef1ab4fbf17d8acf9f`.
|
||||
|
||||
## Retained contracts
|
||||
|
||||
- The empty-subtask keeper retains the default task thread checks after the
|
||||
first flush, and default file-viewer/plan-query checks after the second.
|
||||
Its explicit empty list matches the shared beforeEach default.
|
||||
- The first-task properties-button keeper uses the same first-task input.
|
||||
It now checks closePanel before the toggle, then still proves opt-in opening.
|
||||
- Three repeated renders disappear: 140→137 cases, 109→106 declarations,
|
||||
+14/-57 test lines (43 fewer). All 17 parameterized declarations remain.
|
||||
The full ledger and AST proof remain in temporary receipts, not this PR.
|
||||
|
||||
## Exact-parent proof
|
||||
|
||||
| Full owner phase | Passed | Failed |
|
||||
|---|---|---|
|
||||
| Baseline | 140 | 0 |
|
||||
| Candidate | 137 | 0 |
|
||||
| Reverted test | 140 | 0 |
|
||||
| Default thread forced to legacy | 108 | 29 |
|
||||
| File viewer forced on | 136 | 1 |
|
||||
| Plan panel forced to render/query | 136 | 1 |
|
||||
| Panel close paths removed | 135 | 2 |
|
||||
| Production restored, candidate | 137 | 0 |
|
||||
|
||||
Each production mutation fails its intended moved keeper assertion at lines
|
||||
2463, 2470, 2473 or 4299. Each owner restoration is byte-for-byte. No cases
|
||||
are skipped. `pnpm check:token-gates`, UI typecheck and diff-check pass.
|
||||
The normal command from ui is `pnpm exec vitest run src/pages/IssueDetail.test.tsx`.
|
||||
|
||||
Parent test SHA-256: `06b026c4bd296848011d1cc30f89711453a9db0c1e4c743fa22ae653b3e942d7`.
|
||||
Candidate SHA-256: `530b6046ce0c15ece4304bc49f711af6fc95ebd62ef5b0aa0515b96ce2d94995`.
|
||||
Restored owner SHA-256: `17b28ad871c2f00fa2d1153d50f151ebd0bed68c85d5a0f2a50164a2f1a612b2`.
|
||||
Receipts: `/private/tmp/test-audit-r17-issue-detail-replay/receipt.json`.
|
||||
No production change is included. No CI speedup is claimed from local samples.
|
||||
Full repository preflight stays in isolated CI because local dependency links
|
||||
are shared; it would rewrite those links. CI must pass before green handoff.
|
||||
@@ -0,0 +1,530 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/AgentConfigForm.render.test.tsx",
|
||||
"base": "e3effbb9e5c66b838f67fd487987c8bf1007e413",
|
||||
"originalSha256": "57282ae3afe8c55ac118f3a658152d8df500a87b15383c8f6b0530a7fdcfb8a0",
|
||||
"candidateSha256": "132112f3d2bb74d0844673a39ce31ffca1306e0b82d9846f3e0cafaf83e81708",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 4,
|
||||
"rootRendersRemoved": 4,
|
||||
"netTestLinesRemoved": 44,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "keeps secret access out of the main Configuration content",
|
||||
"keeper": "hides the environment override when Local is the only configured environment",
|
||||
"exactSetupStatements": 2,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(result.container.textContent).not.toContain(\"Secret access\");"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "starts a login session for the effective sandbox and shows the code and the authentication URL",
|
||||
"keeper": "shows a Cancel affordance while a login is active and cancels the session",
|
||||
"exactSetupStatements": 5,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(\"company-1\", \"codex_local\", {\n environmentId: \"sandbox-1\",\n });",
|
||||
"expect(result.container.textContent).toContain(\"WXYZ-1234\");",
|
||||
"expect(result.container.textContent).toContain(\"https://auth.example.test/device\");"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "shows the authorization URL and a browser-code input for a Claude sandbox",
|
||||
"keeper": "cancels an active Claude login and returns to its start state",
|
||||
"exactSetupStatements": 6,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(mockAgentsApi.startClaudeSetupTokenLogin).toHaveBeenCalledWith(\"company-1\", {\n environmentId: \"sandbox-1\",\n });",
|
||||
"expect(result.container.textContent).toContain(\"https://claude.example.test/authorize\");",
|
||||
"expect(\n result.container.querySelector('input[aria-label=\"Browser code\"]'),\n ).toBeTruthy();",
|
||||
"expect(result.container.textContent).not.toContain(\"not encrypted\");"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "keeps the non-path ACP controls visible when the policy hides the engine choice",
|
||||
"keeper": "hides the host path and execution-engine fields for claude_local when the policy is on",
|
||||
"exactSetupStatements": 6,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(labels).toContain(\"ACP session mode\");",
|
||||
"expect(labels).toContain(\"ACP non-interactive permissions\");"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "084bdbc359e510397ef64daeb24e259efbc62c8b5d2c56d3e09d7f2534f155aa",
|
||||
"mutationVerifierSha256": "ca8463047109d38a447d93fb94a19bb8efe9b1df440e0abf91ab31a8c0a358a2",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 99,
|
||||
"passed": 99,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "16a733e7e1de263ce88f1fbd89b94d4844aab7b6e7eca1fd670899fd670e263f",
|
||||
"logSha256": "3f6f04966d8971e70a782681fccde2afd1aee5099fd8f376197109ed2babd2e5"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 95,
|
||||
"passed": 95,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "7fdabb69061f6d6f072c17224832171a225ac9a992d53c94d050c277eb467120",
|
||||
"logSha256": "2717b82c2589f41dad5d9e926a7a37dd4fd79594acc1e9e6d9614a89ae2b5841"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "secrets",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "hides the environment override when Local is the only configured environment",
|
||||
"mutation": {
|
||||
"before": "if (!isCreate && props.content === \"secrets\")",
|
||||
"after": "if (!isCreate && (props.content === \"secrets\" || props.content === undefined))",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(result.container.textContent).not.toContain(\"Secret access\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "24cf0c3e870ddc964fe327426491a4bbdfde043206191df578686ffb089d2f8f",
|
||||
"logSha256": "10c8a0e3b2aafe33ab63fce0881fdf2b066d1cee507ccc589db6076701da4161",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "a9af939b306874f9bc7ef93c6954f3a234aafbb252c14672097aa33732c86f90",
|
||||
"logSha256": "7fa30fef5ccdfa2cb797df92c4b207dff1eac29c99b9dcefa7a977278a4c3593",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Secret accessSecrets this agent can r\u2026' not to contain 'Secret access'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:822:46\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "codex-company",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "shows a Cancel affordance while a login is active and cancels the session",
|
||||
"mutation": {
|
||||
"before": "agentsApi.startAdapterAuthLogin(companyId, adapterType, { environmentId, aiConnection })",
|
||||
"after": "agentsApi.startAdapterAuthLogin(\"wrong-company\", adapterType, { environmentId, aiConnection })",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "fabccb095c86b4151cb80e0446d83e60074a4da76e7f0a36d4fef0246a483696",
|
||||
"logSha256": "3397bb075079467542110406669cb26177cbbcdee8c6e6c56a5f52ccaaafd30f",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "332cf3fce408da458c33289abaf343fbf76ffedc68d37ee532a5c43ffc09f93e",
|
||||
"logSha256": "81892e052e638ba655789fcac2b9bf95b243d1bd628c86a72053ebd7c596a432",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ Array(3) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n- \"company-1\",\n+ \"wrong-company\",\n \"codex_local\",\n {\n+ \"aiConnection\": undefined,\n \"environmentId\": \"sandbox-1\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:1593:49\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "codex-adapter",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "shows a Cancel affordance while a login is active and cancels the session",
|
||||
"mutation": {
|
||||
"before": "agentsApi.startAdapterAuthLogin(companyId, adapterType, { environmentId, aiConnection })",
|
||||
"after": "agentsApi.startAdapterAuthLogin(companyId, \"wrong-adapter\", { environmentId, aiConnection })",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "3acb41404a916f7bc11b1ff698bfbaaa22017c67835907e59283b7b1df5ea07c",
|
||||
"logSha256": "6530c744b0788868f9123b062307dfd1fac3f40740e193943a81da360fb19f3a",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b3cbd3bfee156e965bed6924beefc3f0e6009785f3dcade569f03b88fd5cba55",
|
||||
"logSha256": "90eeb1ecbbce2406915a5123c24d1311016fc1fe3d878af795781da3c352a071",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ Array(3) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n \"company-1\",\n- \"codex_local\",\n+ \"wrong-adapter\",\n {\n+ \"aiConnection\": undefined,\n \"environmentId\": \"sandbox-1\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:1593:49\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "codex-environment",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "shows a Cancel affordance while a login is active and cancels the session",
|
||||
"mutation": {
|
||||
"before": "agentsApi.startAdapterAuthLogin(companyId, adapterType, { environmentId, aiConnection })",
|
||||
"after": "agentsApi.startAdapterAuthLogin(companyId, adapterType, { environmentId: \"wrong-sandbox\", aiConnection })",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "3afd47d7705415ceb4ad8b7ce1415bd61b0b202d65732881660d75e75910c536",
|
||||
"logSha256": "874b11b544fc038fba5e9ff57b156229acbbefb1db8410bf1bebc6794d1e5f20",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "2750457c4f575d786dade8a250f50520e08b1e351c95d2562900a06ffc9ab769",
|
||||
"logSha256": "845ff3e01a9b6640496b2f895ca31e169639a65e3bf8e94bb3a383026e232b8b",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ Array(3) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n \"company-1\",\n \"codex_local\",\n {\n- \"environmentId\": \"sandbox-1\",\n+ \"aiConnection\": undefined,\n+ \"environmentId\": \"wrong-sandbox\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:1593:49\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "codex-code",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "shows a Cancel affordance while a login is active and cancels the session",
|
||||
"mutation": {
|
||||
"before": "{prompt.code}</span>",
|
||||
"after": "{\"missing code\"}</span>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(result.container.textContent).toContain(\"WXYZ-1234\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "f985ab64892d06589b60759cb68183f52c9128056b79c93e69880cb5ff8c3ad1",
|
||||
"logSha256": "dba2f7e6b14ed30859608119e88ac17f20c3c6de3ad93727a357d3860dc5a592",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "3a7ecc1e9c5e40c93c8e92f9e3930a4d0a01a2f84f01ffed53541c3403be4737",
|
||||
"logSha256": "77c403024242dedc9cfa765484486337c97a35133cea348ed4ec57caf26a760d",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'IdentityNameTitleReports toChoose man\u2026' to contain 'WXYZ-1234'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:1596:42\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "codex-url",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "shows a Cancel affordance while a login is active and cancels the session",
|
||||
"mutation": {
|
||||
"before": "{prompt.url}</span>",
|
||||
"after": "{\"missing url\"}</span>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(result.container.textContent).toContain(\"https://auth.example.test/device\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "0a11ca18e53fda28ea9a2956ee7254ab1b9f40f32f772b530df169adb06569b9",
|
||||
"logSha256": "1772bbd6667cd1f32383f0671a186bc263054ef434de07933bac4feda1e3d4b6",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "3cca5f95b518a31622ebf3f17758c4617fa40476623210da56eee539c5a1efb6",
|
||||
"logSha256": "1bac45eb7f9b150cdc6a99067d62e588baeca3af41d148fe897d0a0fa5949332",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'IdentityNameTitleReports toChoose man\u2026' to contain 'https://auth.example.test/device'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:1597:42\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "claude-company",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "cancels an active Claude login and returns to its start state",
|
||||
"mutation": {
|
||||
"before": "agentsApi.startClaudeSetupTokenLogin(companyId, {",
|
||||
"after": "agentsApi.startClaudeSetupTokenLogin(\"wrong-company\", {",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.startClaudeSetupTokenLogin).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "40ec576f564d239009569ca0033715f58f5b255f76ef22da20b134c2e490218f",
|
||||
"logSha256": "075d5fe2d62f1b80f60d63e0ce37e540fffdf2e666b01a21740264e6d4075478",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "0bf3dff32509866e86a80d67029ee81a1437d335045a91cb6cdf7f6ce59b5982",
|
||||
"logSha256": "7ae4dcd73616fa424d373204986a05e798f573b5659daef4ac58bc6f7b9d9fdf",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'company-1', \u2026(1) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n- \"company-1\",\n+ \"wrong-company\",\n {\n+ \"aiConnection\": undefined,\n \"environmentId\": \"sandbox-1\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:2782:54\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "claude-environment",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "cancels an active Claude login and returns to its start state",
|
||||
"mutation": {
|
||||
"before": "agentsApi.startClaudeSetupTokenLogin(companyId, {\n environmentId,",
|
||||
"after": "agentsApi.startClaudeSetupTokenLogin(companyId, {\n environmentId: \"wrong-sandbox\",",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.startClaudeSetupTokenLogin).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "4250b6cc6d66dc4df5753f403ef6894ceacdbc94bbf7812c5788ae143201d617",
|
||||
"logSha256": "b76c385cbee9d1e26130b8e0c8c5f61fb2b31523e2ca65e2fbbf7303037ff179",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "7dfc3e5efa6eeb0c719d717b901b31fca6ca4a5cff1eb817382fa932a90f983b",
|
||||
"logSha256": "56c3a19a6f86d4cc69918c47afd81bc4b4d887f788c135c7b956ea4fc3c0903c",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'company-1', \u2026(1) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n \"company-1\",\n {\n- \"environmentId\": \"sandbox-1\",\n+ \"aiConnection\": undefined,\n+ \"environmentId\": \"wrong-sandbox\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:2782:54\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "claude-url",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "cancels an active Claude login and returns to its start state",
|
||||
"mutation": {
|
||||
"before": "{authorizationUrl}</span>",
|
||||
"after": "{\"missing authorization url\"}</span>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(result.container.textContent).toContain(\"https://claude.example.test/authorize\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "8ee00e28a8630374d9bfe8c85d96a44121121377b89b7f845ee4b32d659833f5",
|
||||
"logSha256": "35255166875e6184b0a10b1abc4c6f42a281d4217eeac6ba2d1bbda83620aaf9",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b2aca78cbef836af30a95188def37939d9ea2d0af743392abe804ae1bf118605",
|
||||
"logSha256": "bb383cfabd0dc16036f31324b47ad392159a9eb7db75c622e2c2e132c2d1ee63",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'IdentityNameTitleReports toChoose man\u2026' to contain 'https://claude.example.test/authorize'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:2787:42\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "claude-input",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "cancels an active Claude login and returns to its start state",
|
||||
"mutation": {
|
||||
"before": "aria-label=\"Browser code\"",
|
||||
"after": "aria-label=\"Missing browser input\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "result.container.querySelector('input[aria-label=\"Browser code\"]')",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "cf8cb1ded0e9999bb966c94c1c38aa7a147d217a06ff4ebe647c4f03ed3e550f",
|
||||
"logSha256": "dc8c2389ba9b411989fc0d60a27e489829b21de1001c94b908555c337dfc685e",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "1bffa0d67d82af2e69b851eea8a1d4b2c319f85cad4b9b9fdf36dc95c143bde5",
|
||||
"logSha256": "13f2b561842f8803f084be9723c7042abf5c0be74b625c99effb180f0c57dc4d",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected null to be truthy\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:2790:7\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "claude-advisory",
|
||||
"owner": "ui/src/components/AgentConfigForm.tsx",
|
||||
"sourceSha256": "bf999102f39bfc762747ce5d598e1e6ec2f926ac5fb28de55fa1baf5493f28b1",
|
||||
"title": "cancels an active Claude login and returns to its start state",
|
||||
"mutation": {
|
||||
"before": "{transportInsecure && (",
|
||||
"after": "{true && (",
|
||||
"replacementCount": 2
|
||||
},
|
||||
"intendedGuard": "expect(result.container.textContent).not.toContain(\"not encrypted\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "ce03b65cc51ee03992fec6dfa438798f57053b914aca0cb56a791a43817a04b7",
|
||||
"logSha256": "1e6753c737dd67dafe5f8cc4f7ec40be3be0875a4b97f0668e8d457a31462d4f",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "7f3ed2737dd31b0caf900567545c59d50c2cbf9aca876fd35080f13c87fb8757",
|
||||
"logSha256": "aca8606f2a3f8176814074b81c913ec3f702e44141cd2131e28c14de2ce695c7",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'IdentityNameTitleReports toChoose man\u2026' not to contain 'not encrypted'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:2793:46\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "acp-mode",
|
||||
"owner": "ui/src/adapters/claude-local/config-fields.tsx",
|
||||
"sourceSha256": "c7e5aa4093bf30d31d5d3b1de092abe767965b7c79143965f92f8c250bf916f6",
|
||||
"title": "hides the host path and execution-engine fields for claude_local when the policy is on",
|
||||
"mutation": {
|
||||
"before": "label=\"ACP session mode\"",
|
||||
"after": "label=\"Missing ACP mode\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(labels).toContain(\"ACP session mode\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "f6011ba4b3d6f34b47bf64dd76424cfb674b81a6663a58df2dd3994b9f87fca7",
|
||||
"logSha256": "0174f7e7bcb488d93a15306214bb579be7049bfba08e3a048c3183f79360b6b0",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "294e714633d60e3121afe528b1c461d32bf13e4407cbfbd0da2581fc6d22f1ad",
|
||||
"logSha256": "90a2ade05e125113eabeb530edec54a30e21dabd097407bb285139f4d87a35af",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected [ Array(17) ] to include 'ACP session mode'\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2077:15)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:3599:20\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "acp-permissions",
|
||||
"owner": "ui/src/adapters/claude-local/config-fields.tsx",
|
||||
"sourceSha256": "c7e5aa4093bf30d31d5d3b1de092abe767965b7c79143965f92f8c250bf916f6",
|
||||
"title": "hides the host path and execution-engine fields for claude_local when the policy is on",
|
||||
"mutation": {
|
||||
"before": "label=\"ACP non-interactive permissions\"",
|
||||
"after": "label=\"Missing ACP permissions\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(labels).toContain(\"ACP non-interactive permissions\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "f859cc875b6f5c83be73f63cf9122d2fe928ad225c6d815a9ae29f6fed365766",
|
||||
"logSha256": "06269344f536044578526368812f6c89c02f8208b9258beb773d14043859720b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b8b76b1c053101b532aec230f5356cbea28900d0c40fb012e3a8cc9b93a1ff2d",
|
||||
"logSha256": "590f6908806038ca072a41f61516323dcba661886afbab66c0e1cf280c596060",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected [ Array(17) ] to include 'ACP non-interactive permissions'\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2077:15)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AgentConfigForm.render.test.tsx:3600:20\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Test audit, Round 7 UI — Agent configuration follow-up
|
||||
|
||||
This separate packet removes **4 registered executions, 4 root renders and 44 net test lines** from `ui/src/components/AgentConfigForm.render.test.tsx`. It adds no helper and changes no production lines. Audit base: `e3effbb9e5c66b838f67fd487987c8bf1007e413`. Prior committed evidence remains byte-identical. Render/execution savings are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Observations retained |
|
||||
|---|---|---|
|
||||
| `keeps secret access out of the main Configuration content` | `hides the environment override when Local is the only configured environment` | expect(result.container.textContent).not.toContain("Secret access"); |
|
||||
| `starts a login session for the effective sandbox and shows the code and the authentication URL` | `shows a Cancel affordance while a login is active and cancels the session` | expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith("company-1", "codex_local", { environmentId: "sandbox-1", });; expect(result.container.textContent).toContain("WXYZ-1234");; expect(result.container.textContent).toContain("https://auth.example.test/device"); |
|
||||
| `shows the authorization URL and a browser-code input for a Claude sandbox` | `cancels an active Claude login and returns to its start state` | expect(mockAgentsApi.startClaudeSetupTokenLogin).toHaveBeenCalledWith("company-1", { environmentId: "sandbox-1", });; expect(result.container.textContent).toContain("https://claude.example.test/authorize");; expect( result.container.querySelector('input[aria-label="Browser code"]'), ).toBeTruthy();; expect(result.container.textContent).not.toContain("not encrypted"); |
|
||||
| `keeps the non-path ACP controls visible when the policy hides the engine choice` | `hides the host path and execution-engine fields for claude_local when the policy is on` | expect(labels).toContain("ACP session mode");; expect(labels).toContain("ACP non-interactive permissions"); |
|
||||
|
||||
Each of four pairs uses identical beforeEach/afterEach hooks and render/helper input expressions. The Local-only configuration keeper absorbs the Secret access exclusion. The Codex and Claude cancellation keepers retain their respective original provider-specific start API and displayed-code/submitted-browser-code observations before any Cancel click; their providers and lifecycle remain separate. Real runTest/startLogin helpers click Test/Sign in under the original QueryClient/Toast/Tooltip tree with fresh clients and roots. The managed-sandbox-only keeper absorbs both visible non-path ACP labels after the same Advanced/Advanced Run Policy toggles. AgentConfigForm.tsx passes the policy into real ClaudeLocalAdvancedFields from adapters/claude-local/config-fields.tsx, which retains non-path mode/permission fields while host paths are hidden. Existing enabled/disabled/loading, stored-token, terminal-state, submit, cancel-error and timeout cases remain unchanged. Two retired login cases avoid repeated environment-test/start-login API paths; polling request counts and time savings are not claimed.
|
||||
|
||||
The AST verifier checks exact setup, shared enclosing hooks and matched cleanup where present, reconstructs candidate text solely from the declared registrations/assertion migrations, and requires exact byte equality plus full-program AST equality outside those edits. No fixture, helper, provider, action order, wait, assertion strength, timer or teardown changes. The IssueChatThread keeper is an existing complete subset proof; InlineEditor’s identical not-null guard is retained once. Every other case stays byte-identical. Default CI includes this file in `@paperclipai/ui` under `general-workspaces-a`, once across the two file shards; workflow/config unchanged.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 99 | 99 | 0 | 0 |
|
||||
| Candidate | 95 | 95 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/AgentConfigForm.render.test.tsx`.
|
||||
|
||||
All 13 isolated positive controls passed. All 13 deliberate behavior regressions failed at their intended retained/migrated assertion, rather than setup, timeout or a different guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| secrets | `expect(result.container.textContent).not.toContain("Secret access")` |
|
||||
| codex-company | `expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(` |
|
||||
| codex-adapter | `expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(` |
|
||||
| codex-environment | `expect(mockAgentsApi.startAdapterAuthLogin).toHaveBeenCalledWith(` |
|
||||
| codex-code | `expect(result.container.textContent).toContain("WXYZ-1234")` |
|
||||
| codex-url | `expect(result.container.textContent).toContain("https://auth.example.test/device")` |
|
||||
| claude-company | `expect(mockAgentsApi.startClaudeSetupTokenLogin).toHaveBeenCalledWith(` |
|
||||
| claude-environment | `expect(mockAgentsApi.startClaudeSetupTokenLogin).toHaveBeenCalledWith(` |
|
||||
| claude-url | `expect(result.container.textContent).toContain("https://claude.example.test/authorize")` |
|
||||
| claude-input | `result.container.querySelector('input[aria-label="Browser code"]')` |
|
||||
| claude-advisory | `expect(result.container.textContent).not.toContain("not encrypted")` |
|
||||
| acp-mode | `expect(labels).toContain("ACP session mode")` |
|
||||
| acp-permissions | `expect(labels).toContain("ACP non-interactive permissions")` |
|
||||
|
||||
Exactly one production owner was temporarily changed per control. Every owner was restored byte for byte after each mutation and matched pinned Git bytes. Filtered controls select one keeper; unselected tests do not count as full-suite evidence. [Controls JSON](2026-10-12-test-audit-r7-ui-agentconfig-controls.json) records exact owner/source hashes, migration/input proof, complete-file run/log digests, mutation bodies, intended assertion failures and restoration receipts. Temporary raw verifiers and logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad `pnpm test:run` was intentionally stopped without completion and is not claimed as passed. Separate packet results are focused proof, not a completed combined delivery-head replay or pending full CI. No push, rebase, PR or merge is included here.
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/AppErrorBoundary.test.tsx",
|
||||
"base": "c39262305b6b48a5decd564af8369083cd972b33",
|
||||
"originalSha256": "1cc53b4a6978c3c5e98761467208d8e2e9a165163956a64a33bb84cea2778327",
|
||||
"candidateSha256": "51fce35eb3b3b2dd0e1aacdd09f14dcd208e2e6b557ab0e4de9a16606fa3b778",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1,
|
||||
"netTestLinesRemoved": 20,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "renders a reload prompt instead of a blank page when the shell throws in render",
|
||||
"keeper": "reports one captured error and keeps the reload prompt",
|
||||
"setupAstEqual": true,
|
||||
"setupBindingNormalization": null,
|
||||
"guards": [
|
||||
"expect(container.textContent).toContain(\"Paperclip hit an error\");",
|
||||
"expect(container.textContent).toContain(\"Maximum update depth exceeded\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "0f1b173c8a399bb8c3e831edac6805634304a749faffcfff7b8b1900da3d34a3",
|
||||
"mutationVerifierSha256": "a05f5ba9071c0ce7ce8eac1bc0f6c7dd83401c4e720816389d779f8919312064",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "c1a2a5f2da21b5a730f174b628b23c1b01e682b2b8cc10ab8f332dbcede339bd",
|
||||
"logSha256": "475145ce3b58595c9e76cbed628598ecc97a3d5cc835b1e374a10507e72ba9f7",
|
||||
"total": 6,
|
||||
"passed": 6,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "b27bec00f8765cc28254633e2752f96f15d6f0734cd59b4e0c10267ba138ff46",
|
||||
"logSha256": "bba83ddd4f7aae20381ef90bfd2f17f6b44818e71994c5f716ea17df8c10c057",
|
||||
"total": 5,
|
||||
"passed": 5,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "title",
|
||||
"owner": "ui/src/components/AppErrorBoundary.tsx",
|
||||
"sourceSha256": "72bfc237fd0760e8432a9948e3423b6cab6881507f28815240f585f46e3ceb7c",
|
||||
"title": "reports one captured error and keeps the reload prompt",
|
||||
"mutation": {
|
||||
"before": "Paperclip hit an error",
|
||||
"after": "Unexpected title",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).toContain(\"Paperclip hit an error\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "85d36ca8369f85ceb5e9173db5670b2d5ccf3c843cf26b3b1fb0ddb73a59a029",
|
||||
"logSha256": "088a6c3c7dafb9546d8c32db0829797a2917cba4ce2e78c0215529c99d9096f2",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b82c5bc57625cae251acafe46aa64021129848550e43cb66c19996b10f7ced0f",
|
||||
"logSha256": "5f8f8ad95575b5920ae4574c4c04d7d7a6b3db3262c293973953b4f56f2f6604",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Unexpected titleSomething went wrong \u2026' to contain 'Paperclip hit an error'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AppErrorBoundary.test.tsx:53:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "detail",
|
||||
"owner": "ui/src/components/AppErrorBoundary.tsx",
|
||||
"sourceSha256": "72bfc237fd0760e8432a9948e3423b6cab6881507f28815240f585f46e3ceb7c",
|
||||
"title": "reports one captured error and keeps the reload prompt",
|
||||
"mutation": {
|
||||
"before": "{error.message}",
|
||||
"after": "{\"hidden detail\"}",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).toContain(\"Maximum update depth exceeded\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "a11dcafca2602e4baed9feff4925dcca4c09ceb46a36b5baf782742274fe467d",
|
||||
"logSha256": "23301c06f7a3c46599867040f2fe947f36fe6bf9c33c66e6c0d465c9b7a97746",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "3d0fdd9f0f6fa788decca2bdf816bee3bcbe4f2d5836ba6582e8e2860369d974",
|
||||
"logSha256": "31c8775a1049760ef218a4d1bb14df2dde621652cd58f1b53c24651a4dfa0d3a",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Paperclip hit an errorSomething went \u2026' to contain 'Maximum update depth exceeded'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/AppErrorBoundary.test.tsx:54:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Test audit, Round 7 UI — App error boundary follow-up
|
||||
|
||||
This isolated follow-up removes **1 registered executions, 1 root renders and 20 net test lines** from `ui/src/components/AppErrorBoundary.test.tsx`. It changes no production lines and adds no helper. The pinned audit base is `c39262305b6b48a5decd564af8369083cd972b33`. Prior UI proof remains unchanged. Registration/render reductions are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact migrations
|
||||
|
||||
| Retired registration | Keeper | Original observations retained |
|
||||
|---|---|---|
|
||||
| `renders a reload prompt instead of a blank page when the shell throws in render` | `reports one captured error and keeps the reload prompt` | expect(container.textContent).toContain("Paperclip hit an error");; expect(container.textContent).toContain("Maximum update depth exceeded"); |
|
||||
|
||||
main.tsx wraps the app in AppErrorBoundary. The paired tests create the same root and render the same BoomRender throw under the real boundary, with identical hooks/unmount. Move title and original error detail before retained capture/count/metadata/reload-button checks. The retired reload-button expression already matches the retained expression exactly. Effect errors, healthy children and DOM insertion/translation errors remain distinct and unchanged.
|
||||
|
||||
The verifier reconstructs the exact candidate text from only the declared removals and insertions, requires byte equality, and compares the entire program AST after the same migrations. Every unrelated source byte, hook, mock, test body and binding remains unchanged, outside the audit receipt’s explicitly declared duplicate-title or blank-line cleanup where applicable. Setup ASTs are equal except the explicitly normalized attachment callback binding described above where applicable. No tests are skipped, softened, suppressed or added. Each file remains included in `@paperclipai/ui` under CI `general-workspaces-a`; Vitest file sharding selects it once across the two shards, with no workflow/config change.
|
||||
|
||||
## Complete-file evidence
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 6 | 6 | 0 | 0 |
|
||||
| Candidate | 5 | 5 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/AppErrorBoundary.test.tsx`.
|
||||
|
||||
All 2 separately selected positive keeper controls passed. Every deliberate behavior mutation failed at the corresponding intended keeper assertion, not setup, timeout or another guard:
|
||||
|
||||
| Mutation | Intended assertion |
|
||||
|---|---|
|
||||
| title | `expect(container.textContent).toContain("Paperclip hit an error")` |
|
||||
| detail | `expect(container.textContent).toContain("Maximum update depth exceeded")` |
|
||||
|
||||
Every mutated production owner was restored byte for byte after each mutation and checked against pinned Git bytes. Name-filtered controls select exactly one keeper; their unselected tests are not full-suite evidence. [Compact controls](2026-10-12-test-audit-r7-ui-appboundary-controls.json) preserve source/owner hashes, exact migration map, body verifier digests, complete run/log digests, intended failure receipts and restoration checks. Temporary AST/receipt scripts initially needed corrections for chained assertions or statement position; those verifier attempts produced no accepted test evidence.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion; it is not claimed as a pass. These focused runs do not substitute for pending delivery-head CI. No push, rebase, pull request or merge is included in this packet.
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/pages/apps/AppDetail.test.tsx",
|
||||
"base": "81cd7ab0ca0bdc55ea0927bc44fcf6e764a530ed",
|
||||
"originalSha256": "35c4d4828daa80d37b8319fa1dfd4c049c03e26a780c6ce3b1e64990f32be9c6",
|
||||
"candidateSha256": "f41c56cc4bae6e496067c93b58419ed3acfc6938f7a1ea7169ac2b1fa9ae3e61",
|
||||
"retired": "removes the separate always-installed controls from Permissions",
|
||||
"keeper": "renders searchable action groups with three-way permission toggles",
|
||||
"identicalInitialSetup": true,
|
||||
"migratedExactAssertions": [
|
||||
"expect(container.textContent).toContain(\"Which agents can use this connection?\");",
|
||||
"expect(container.textContent).not.toContain(\"Always installed\");",
|
||||
"expect(putConnectionInstallsMock).not.toHaveBeenCalled();"
|
||||
],
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"completeRenderCallsRemoved": 1,
|
||||
"netTestLinesRemoved": 7
|
||||
},
|
||||
"verifierSha256": "8252a8caeedbbbc3b6b630376b8207b4f78021b719ac601e061d394535ecea0e",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "74eb47f37400e2409108c4a88bbbd1f0c25d12d0e7f9c8576aad598758472ea3",
|
||||
"total": 78,
|
||||
"passed": 78,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "50a8a2e03f8521f125011f82c0ee816b55e1bd1d751e9d3fd2fe3fd71fad2314",
|
||||
"total": 77,
|
||||
"passed": 77,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "appdetail-old-install-copy",
|
||||
"owner": "ui/src/pages/apps/app-detail/PermissionsPanel.tsx",
|
||||
"title": "renders searchable action groups with three-way permission toggles",
|
||||
"sourceSha": "b3b96c2e2fbafee17e0b01c9127c30bd4a0a787f2481a264001fd6c2bce8e379",
|
||||
"replacements": 1,
|
||||
"mutation": {
|
||||
"before": ">Which agents can use this connection?</h2>",
|
||||
"after": ">Which agents can use this connection?<span>Always installed</span></h2>"
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"caseStatus": "passed",
|
||||
"jsonSha256": "a97f5dd88c5febcf4fe906f34ea7efd0672781b95b2757aeb9cbcb0bd9247edf",
|
||||
"logSha256": "26c7c2d6e1c6edca6e56e0f21ecbe3897c28c3f7ad1300dd07886fe905d0c025"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"caseStatus": "failed",
|
||||
"intendedAssertionFailure": "AssertionError: expected 'GitHubConnected2 actions availableUnv\u2026' not to contain 'Always installed'",
|
||||
"jsonSha256": "a444c1fbc181dabecbcf67ce1ea7da5659d2bf3cc85a5983fc02fd49418ea66d",
|
||||
"logSha256": "feef1c4c056f2bba80e2bda438edc27e099da834f11d3c5b1ccaf1e13da73b0d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "appdetail-install-on-mount",
|
||||
"owner": "ui/src/pages/apps/AppDetail.tsx",
|
||||
"title": "renders searchable action groups with three-way permission toggles",
|
||||
"sourceSha": "ee8ab6e844ce11bf5d13954f0a73de3e42de61a933deb01ad9e40cb76e3b93d1",
|
||||
"replacements": 1,
|
||||
"mutation": {
|
||||
"before": " const activeTab: AppTabKey | null = isAppTabKey(tab) ? tab : null;",
|
||||
"after": " useEffect(() => { void toolsApi.putConnectionInstalls(connectionId, []); }, [connectionId]);\n const activeTab: AppTabKey | null = isAppTabKey(tab) ? tab : null;"
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"caseStatus": "passed",
|
||||
"jsonSha256": "b87e88f3d6a624d21ce9795889d002fb0bb8a08ad7b27bd5cad6420b03ec6f04",
|
||||
"logSha256": "0d0d9d88fd6da2c652fb4119fb2ed3cef7c624514e8b0501f3daf62578ec99ac"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"caseStatus": "failed",
|
||||
"intendedAssertionFailure": "AssertionError: expected \"vi.fn()\" to not be called at all, but actually been called 1 times",
|
||||
"jsonSha256": "7d04f5db02f84793fe53914e4f8cfc98f7644766146861857afcf586809b6d16",
|
||||
"logSha256": "cc98a64e983162d6330263ea4433cc6b17c4925b3b9806794996dafb9b7acd0c"
|
||||
}
|
||||
}
|
||||
],
|
||||
"allProductionOwnersRestored": true,
|
||||
"productionLinesChanged": 0,
|
||||
"priorUiProofUnchanged": {
|
||||
"commit": "81cd7ab0ca0bdc55ea0927bc44fcf6e764a530ed",
|
||||
"reportSha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8",
|
||||
"controlsSha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
"ciInclusion": "@paperclipai/ui project in general-workspaces-a; complete file remains included; no routing changes",
|
||||
"netCombinedTestLinesRemoved": 371,
|
||||
"combinedRegisteredInvocationsRemoved": 2
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Test audit, Round 7 UI — AppDetail follow-up
|
||||
|
||||
This follow-up removes **one duplicate registered invocation, one complete render, and seven net test lines** from `ui/src/pages/apps/AppDetail.test.tsx`. It adds no helper and changes zero production lines. Combined with the [approved first UI batch](2026-10-12-test-audit-r7-ui.md), the branch removes **371 net test lines and two registered invocations**. The original UI report, controls and test bytes remain unchanged.
|
||||
|
||||
The follow-up audit base is local UI commit `81cd7ab0ca0bdc55ea0927bc44fcf6e764a530ed`. This file has the same original bytes at Round 6 parent `e868e6645ce0d598d756d3458f0b3d71afd6db4a`.
|
||||
|
||||
## Exact retirement and keeper
|
||||
|
||||
Retired: `removes the separate always-installed controls from Permissions`.
|
||||
|
||||
Keeper: `renders searchable action groups with three-way permission toggles`.
|
||||
|
||||
Both cases use the same enclosing beforeEach, connection, catalog, profiles, grants, install state and spies. Their two pre-assertion statements are byte-identical: select the permissions tab, then await the same complete AppDetail render. Neither case changes a fixture or takes a user action before its assertions. Both permission observations were updated by [PR #12802](https://github.com/paperclipai/paperclip/pull/12802).
|
||||
|
||||
All three retired observations move unchanged into the keeper, immediately after render and before its existing assertions:
|
||||
|
||||
- The agent-access question is visible.
|
||||
- The old Always installed copy is absent.
|
||||
- The connection-install mutation API has not been called.
|
||||
|
||||
The keeper also retains its original fixed action-group counts, action names, review prompt, search input, selected Allowed/Ask first radio states, two Test actions, old-copy exclusions and card-layout exclusion. Viewer gates, picked-agent mode, required installed agents, access mutations, AI credential routing, task-grant removal and failure paths remain separate cases.
|
||||
|
||||
App.tsx mounts AppDetail at `apps/:connectionId/:tab`. AppDetail's actual permissions path renders AgentAccessSection and ActionsSection in PermissionsPanel. AgentAccessSection supplies the question and access controls. The install API is used by the explicit AI-connection save path, so a no-write-on-read check is a separate observation from absent UI copy. Both fixed guards remain in the keeper. The grouped action-control sibling suite remains unchanged.
|
||||
|
||||
The test stays in the `@paperclipai/ui` project and the `general-workspaces-a` lane. This cut removes a collected test and its full query-client/root render; it changes no suite inclusion or CI routing. No wall-time improvement is claimed.
|
||||
|
||||
## Proof
|
||||
|
||||
| Complete AppDetail file | Registered cases | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 78 | 78 | 0 | 0 |
|
||||
| Candidate | 77 | 77 | 0 | 0 |
|
||||
|
||||
Run from `ui/`: `pnpm exec vitest run src/pages/apps/AppDetail.test.tsx`.
|
||||
|
||||
The body audit checks the two identical setup statements, copies the three exact assertion ASTs, and compares full original/candidate programs outside the declared removal and insertion. It also reconstructs the exact candidate text from only those source edits and requires byte equality. Every other source byte remains unchanged, including all mock factories, beforeEach, render helper, spies, waits, cleanup and original keeper assertions. No withPermissions helper exists in these pinned file bytes; no such setup was added or changed.
|
||||
|
||||
Both positive keeper controls passed. Two deliberately broken production owners failed at the intended migrated guard:
|
||||
|
||||
| Production regression | Direct keeper failure |
|
||||
|---|---|
|
||||
| Reintroduce Always installed copy beside the access question in PermissionsPanel | Fixed negative old-copy assertion. |
|
||||
| Write connection installs from an AppDetail mount effect | Fixed no-install-API-call assertion. |
|
||||
|
||||
Each negative control selected only the keeper. Its unselected cases do not count as complete-file proof. Neither mutation failed on setup, timeout or an unrelated action assertion. Both production owners were restored byte for byte and checked against pinned Git source.
|
||||
|
||||
[Follow-up controls](2026-10-12-test-audit-r7-ui-appdetail-controls.json) retain exact original/candidate hashes, the unchanged prior proof hashes, body-audit digest, complete-file results, mutation descriptions, intended failures, run/log digests and source-restoration receipts. Raw verifiers and logs remain temporary audit packets; their digests do not retain complete payloads. Latest delivery-head replay, typecheck, token gates, build and CI status belong in the PR. The broad local default run was stopped before completion as recorded by the coordinator; it is not a full-suite pass.
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/IssueAttachmentsSection.test.tsx",
|
||||
"base": "c39262305b6b48a5decd564af8369083cd972b33",
|
||||
"originalSha256": "938aead11f9ccd2246049fe7ebe82800c3bdc8ff7c17fc20017d03ecc659d73a",
|
||||
"candidateSha256": "e7cc76d20708169e8eadba692d6f9bc9e7ba32cb18075e72c8aaceb420705a94",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1,
|
||||
"netTestLinesRemoved": 22,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "renders video attachments through the same player used for artifact outputs",
|
||||
"keeper": "lets video attachments open the shared media gallery",
|
||||
"setupAstEqual": false,
|
||||
"setupBindingNormalization": "Inline fresh vi.fn becomes existing keeper fresh named vi.fn; provider/attachment/delete callback/flush unchanged",
|
||||
"guards": [
|
||||
"const video = container.querySelector(\"video\");",
|
||||
"expect(video?.getAttribute(\"src\")).toBe(\"/api/attachments/video-attachment/content\");",
|
||||
"expect(video?.getAttribute(\"controls\")).not.toBeNull();",
|
||||
"expect(fetchSpy).not.toHaveBeenCalled();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "0f1b173c8a399bb8c3e831edac6805634304a749faffcfff7b8b1900da3d34a3",
|
||||
"mutationVerifierSha256": "a05f5ba9071c0ce7ce8eac1bc0f6c7dd83401c4e720816389d779f8919312064",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "41996a845a818368c4fe0a7a4b31474ddc1b35c56c9aff524dcfae3218a1112c",
|
||||
"logSha256": "cf1b9e033afd854462df31d4d65f4e604f317f6473c1f75c25b91ccf46e3e92c",
|
||||
"total": 8,
|
||||
"passed": 8,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "e9db5edd68d2e89898848aa90dabf75957fb703813c429472f80fbae92d194b4",
|
||||
"logSha256": "67fe4596626962ac86f547b251f992158d5a28063a2c62962816433c3e837ed2",
|
||||
"total": 7,
|
||||
"passed": 7,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "source",
|
||||
"owner": "ui/src/components/IssueAttachmentsSection.tsx",
|
||||
"sourceSha256": "2ba7827325ed63d57fad24f31144e54c48c0618584c0cd013b26acf93e7f525b",
|
||||
"title": "lets video attachments open the shared media gallery",
|
||||
"mutation": {
|
||||
"before": "<OutputVideoPlayer src={attachment.contentPath} title={filename} />",
|
||||
"after": "<OutputVideoPlayer src=\"/wrong-video.webm\" title={filename} />",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(video?.getAttribute(\"src\")).toBe(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "26bb673ede3119522a715a31928f8066c41c6538e78139e5a1dde1e879964fd5",
|
||||
"logSha256": "5f31a93058981f8a4a0b9c109b851aad78153f734eb723cc04998fb3ffca555b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "41c490f0613aaf4958ad6ccc613dcf31053f2f528b1c7b2a252230470bc7d8ae",
|
||||
"logSha256": "984bfe91fe92dcb4518504ae6fe21205466e3c775c2dde6806934471d73ab7d5",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected '/wrong-video.webm' to be '/api/attachments/video-attachment/con\u2026' // Object.is equality\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/IssueAttachmentsSection.test.tsx:206:40\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "controls",
|
||||
"owner": "ui/src/components/IssueAttachmentsSection.tsx",
|
||||
"sourceSha256": "2ba7827325ed63d57fad24f31144e54c48c0618584c0cd013b26acf93e7f525b",
|
||||
"title": "lets video attachments open the shared media gallery",
|
||||
"mutation": {
|
||||
"before": "<OutputVideoPlayer src={attachment.contentPath} title={filename} />",
|
||||
"after": "<video src={attachment.contentPath} />",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(video?.getAttribute(\"controls\")).not.toBeNull()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "5f63925596e95dc930e831668536812e50bb21f3c49e5d6d72458b4fe8e9558d",
|
||||
"logSha256": "9685b1259ff56dade8aee65c3388084bb032c0a1bd584e7635cf64a90ae7ef34",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "738045b821839a3fe5b3ff8ab0c8abd6508462105a7ffde2ae24cd2a76d0f1e5",
|
||||
"logSha256": "19e35c954f8972b9d86f893ee59877ad1ee28eaff8856c22fdeac4f4f49ebfab",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected null not to be null\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/IssueAttachmentsSection.test.tsx:207:49\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "fetch",
|
||||
"owner": "ui/src/components/IssueAttachmentsSection.tsx",
|
||||
"sourceSha256": "2ba7827325ed63d57fad24f31144e54c48c0618584c0cd013b26acf93e7f525b",
|
||||
"title": "lets video attachments open the shared media gallery",
|
||||
"mutation": {
|
||||
"before": " const filename = attachmentFilename(attachment);\n return (\n <Card id={`attachment-${attachment.id}`} className=\"block scroll-mt-20 overflow-hidden py-0\">",
|
||||
"after": " const filename = attachmentFilename(attachment);\n void fetch(attachment.contentPath);\n return (\n <Card id={`attachment-${attachment.id}`} className=\"block scroll-mt-20 overflow-hidden py-0\">",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(fetchSpy).not.toHaveBeenCalled()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "fa3a3edf2518552cf54db294e33be650bbf026a1b9505f8dfd4b7baba5de5d8d",
|
||||
"logSha256": "1ba024b01f756729fdfbc1a7f9b535b8cd16a650bc1da99d8ea9d5a79521f796",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b43dea15a78ffa00aa10d67a79932d6b8bba5a6a57d8dfb828d8693e277e019b",
|
||||
"logSha256": "130e9a50e1cb3db653a4324ce9aa86b785d9275436606793f823073f6432ab0d",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to not be called at all, but actually been called 1 times\n\nReceived:\n\n 1st vi.fn() call:\n\n Array [\n \"/api/attachments/video-attachment/content\",\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2214:51)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/IssueAttachmentsSection.test.tsx:208:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Test audit, Round 7 UI — Video attachments follow-up
|
||||
|
||||
This isolated follow-up removes **1 registered executions, 1 root renders and 22 net test lines** from `ui/src/components/IssueAttachmentsSection.test.tsx`. It changes no production lines and adds no helper. The pinned audit base is `c39262305b6b48a5decd564af8369083cd972b33`. Prior UI proof remains unchanged. Registration/render reductions are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact migrations
|
||||
|
||||
| Retired registration | Keeper | Original observations retained |
|
||||
|---|---|---|
|
||||
| `renders video attachments through the same player used for artifact outputs` | `lets video attachments open the shared media gallery` | const video = container.querySelector("video");; expect(video?.getAttribute("src")).toBe("/api/attachments/video-attachment/content");; expect(video?.getAttribute("controls")).not.toBeNull();; expect(fetchSpy).not.toHaveBeenCalled(); |
|
||||
|
||||
IssueAttachmentsSection.tsx classifies the same webm attachment into VideoAttachmentCard, which passes contentPath to the actual shared OutputVideoPlayer; OutputVideoPlayer supplies native controls. Gallery forwarding stays in AttachmentActions. Both tests use the same makeAttachment overrides, fresh query client/root/fetchSpy hooks, delete callback presence and act/flush lifecycle. The retired inline fresh vi.fn for onImageClick matches the existing keeper fresh named vi.fn after binding normalization; the keeper binding remains unchanged. Move video source, controls and no-fetch checks before gallery click. MIME fallback, non-video/non-markdown, generic download and read-only tests remain distinct.
|
||||
|
||||
The verifier reconstructs the exact candidate text from only the declared removals and insertions, requires byte equality, and compares the entire program AST after the same migrations. Every unrelated source byte, hook, mock, test body and binding remains unchanged, outside the audit receipt’s explicitly declared duplicate-title or blank-line cleanup where applicable. Setup ASTs are equal except the explicitly normalized attachment callback binding described above where applicable. No tests are skipped, softened, suppressed or added. Each file remains included in `@paperclipai/ui` under CI `general-workspaces-a`; Vitest file sharding selects it once across the two shards, with no workflow/config change.
|
||||
|
||||
## Complete-file evidence
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 8 | 8 | 0 | 0 |
|
||||
| Candidate | 7 | 7 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/IssueAttachmentsSection.test.tsx`.
|
||||
|
||||
All 3 separately selected positive keeper controls passed. Every deliberate behavior mutation failed at the corresponding intended keeper assertion, not setup, timeout or another guard:
|
||||
|
||||
| Mutation | Intended assertion |
|
||||
|---|---|
|
||||
| source | `expect(video?.getAttribute("src")).toBe(` |
|
||||
| controls | `expect(video?.getAttribute("controls")).not.toBeNull()` |
|
||||
| fetch | `expect(fetchSpy).not.toHaveBeenCalled()` |
|
||||
|
||||
Every mutated production owner was restored byte for byte after each mutation and checked against pinned Git bytes. Name-filtered controls select exactly one keeper; their unselected tests are not full-suite evidence. [Compact controls](2026-10-12-test-audit-r7-ui-attachments-controls.json) preserve source/owner hashes, exact migration map, body verifier digests, complete run/log digests, intended failure receipts and restoration checks. Temporary AST/receipt scripts initially needed corrections for chained assertions or statement position; those verifier attempts produced no accepted test evidence.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion; it is not claimed as a pass. These focused runs do not substitute for pending delivery-head CI. No push, rebase, pull request or merge is included in this packet.
|
||||
@@ -0,0 +1,401 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/SidebarCompanyMenu.test.tsx",
|
||||
"base": "2e76f5157240ee8b2d42adbdcd4c345aea07c013",
|
||||
"originalSha256": "ad89f1174a840f7e10a04c180dcdfcbb972e66e96fd45a335d4af90da553f693",
|
||||
"candidateSha256": "3b14e39deb667f2b9f79981767b6f47852ed4a8f3ee51fe39c4bd9efbd61d5d2",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1,
|
||||
"netTestLinesRemoved": 28,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "uses company-centric create copy without the chat flag",
|
||||
"keeper": "toggles company order editing without selecting a company",
|
||||
"exactLeadingStatements": 7,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": true,
|
||||
"kind": "menu",
|
||||
"exactMigratedStatements": [
|
||||
"expect(trigger?.classList).toContain(\"px-4\");",
|
||||
"expect(trigger?.classList).toContain(\"has-[>svg]:px-4\");",
|
||||
"expect(trigger?.classList).toContain(\"hover:bg-sidebar-accent\");",
|
||||
"expect(trigger?.classList).toContain(\"hover:text-sidebar-accent-foreground\");",
|
||||
"expect(trigger?.classList).toContain(\"dark:hover:bg-sidebar-accent\");",
|
||||
"expect(trigger?.classList).toContain(\"dark:hover:text-sidebar-accent-foreground\");",
|
||||
"expect(trigger?.classList).not.toContain(\"hover:bg-background\");",
|
||||
"expect(trigger?.classList).not.toContain(\"dark:hover:bg-background\");",
|
||||
"expect(trigger?.classList).not.toContain(\"dark:hover:bg-accent/50\");",
|
||||
"expect(document.body.textContent).toContain(\"Create organization\");",
|
||||
"expect(document.body.textContent).not.toContain(\"Add company...\");"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [
|
||||
"act(() => {\n trigger?.dispatchEvent(new PointerEvent(\"pointerdown\", { bubbles: true, button: 0 }));\n trigger?.dispatchEvent(new MouseEvent(\"click\", { bubbles: true }));\n });",
|
||||
"await flushReact();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "ff07f5e2e308cfc0f26584a6cd6599bcd98665e79d7f4782ed5bb36b35cb8cb9",
|
||||
"mutationVerifierSha256": "676b973d4838ed130c5ae92d4bc50fb32a65309af8c858450ec34bcce13729e9",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 16,
|
||||
"passed": 16,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "b7e2061f311e86d6750c2c9b0e6aee6bd6e026ee7acd10483c3d0cb8de863ac8",
|
||||
"logSha256": "bce8c6310d9cbd08d01f813b90d9115241d8809314b74211530ef05f6c3cfd27"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 15,
|
||||
"passed": 15,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "122222983e6eb17dfcc0745805ae2662a4ebf0781b4811cff488cfaa65f5dd79",
|
||||
"logSha256": "46a93fb15df0d8faae9d94c9c261e1901ec12456bc0eb7610aa0c80cc4c1b58b"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "trigger-inset",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": "gap-2 px-4 text-left",
|
||||
"after": "gap-2 px-2 text-left",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(trigger?.classList).toContain(\"px-4\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "965e51bf2e7303dc08ed66d5758ef80bfb18f8852e967fb12dd9c05776422dd6",
|
||||
"logSha256": "3d45f3d012ad3ffbe563e1150356387730e5e6d2c169a25b537bb7add31124fb",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "f20cf3f9ecda2e5fe608bcd28d9bd68e030c6b72b3bc1bc4edce7e895cfe3ba6",
|
||||
"logSha256": "80548eebd34f5ed3546ce6dc68d47a8e6452ecae6a06a36b6e4e3dc34598cdda",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium transition-(--tp-color-background-color-border-color-box-shadow-opacity) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-(length:--rad-3) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive py-2 h-9 min-w-0 flex-1 justify-start gap-2 px-2 text-left hover:bg-sidebar-accent hover:text-sidebar-accent-foreground has-[>svg]:px-4 dark:hover:bg-sidebar-accent dark:hover:text-sidebar-accent-foreground\" to contain \"px-4\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:429:32\n at runNextTicks (node:internal/process/task_queues:65:5)\n at listOnTimeout (node:internal/timers:567:9)\n at processTimers (node:internal/timers:541:7)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "icon-inset",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": "has-[>svg]:px-4",
|
||||
"after": "has-[>svg]:px-2",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(trigger?.classList).toContain(\"has-[>svg]:px-4\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "ea88a40c8b8bf6350d02344b57ed5d99783dc5f153b7abb4b3a344c994b7a0f3",
|
||||
"logSha256": "6edb24fc26547ae0367a8d95a2e35530798bb07ce5f71d9f41b86001c4ee6c64",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "fc33c2e6d3e034a617bf247b3795f136b4e5d200b3a4323101df92ce7cc1d7ab",
|
||||
"logSha256": "77d5d26f406f4c778dcd6a6c476577151cefd36b3e0e9b896bd21bfc4d435886",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium transition-(--tp-color-background-color-border-color-box-shadow-opacity) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-(length:--rad-3) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive py-2 h-9 min-w-0 flex-1 justify-start gap-2 px-4 text-left hover:bg-sidebar-accent hover:text-sidebar-accent-foreground has-[>svg]:px-2 dark:hover:bg-sidebar-accent dark:hover:text-sidebar-accent-foreground\" to contain \"has-[>svg]:px-4\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:430:32\n at runNextTicks (node:internal/process/task_queues:65:5)\n at listOnTimeout (node:internal/timers:567:9)\n at processTimers (node:internal/timers:541:7)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "hover-background",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": "text-left hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||
"after": "text-left hover:bg-accent hover:text-sidebar-accent-foreground",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(trigger?.classList).toContain(\"hover:bg-sidebar-accent\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "6927fb926814f3a97c2a4c93ff715eebb7b55c7b26cf78395352cd6cf72a1d94",
|
||||
"logSha256": "3a867b13b6c1a885838cda58afc327eac62f0eba9a60def7ec1d5d9b0a58b07b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "cdfc31c2e7474765edcda5c384aedb266d4f7ba38a6593c6da5d08a3329c98f4",
|
||||
"logSha256": "e5d367e9396f285f008d50cc7745f3cbb4e49757bc887a4ec7847bf56cb0826c",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium transition-(--tp-color-background-color-border-color-box-shadow-opacity) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-(length:--rad-3) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive py-2 h-9 min-w-0 flex-1 justify-start gap-2 px-4 text-left hover:bg-accent hover:text-sidebar-accent-foreground has-[>svg]:px-4 dark:hover:bg-sidebar-accent dark:hover:text-sidebar-accent-foreground\" to contain \"hover:bg-sidebar-accent\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:431:32\n at runNextTicks (node:internal/process/task_queues:65:5)\n at listOnTimeout (node:internal/timers:567:9)\n at processTimers (node:internal/timers:541:7)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "hover-foreground",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground has-",
|
||||
"after": "hover:bg-sidebar-accent hover:text-foreground has-",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(trigger?.classList).toContain(\"hover:text-sidebar-accent-foreground\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "395cc772d310d912806b746111d7f557091468b89d37d178a6a8f941926d113b",
|
||||
"logSha256": "63ca60c22b27299275ea041bb23a009690f69e53a01857e34a63d222c0f790a5",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "7cee44772f8452a6a3236cdee1fd3b702a5b345bda5b14786125a5e63f4a7532",
|
||||
"logSha256": "260cb917636d6fa2a351a3dc1118c2c59fbca81f1d819e25ebc78468f6de5ca9",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium transition-(--tp-color-background-color-border-color-box-shadow-opacity) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-(length:--rad-3) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive py-2 h-9 min-w-0 flex-1 justify-start gap-2 px-4 text-left hover:bg-sidebar-accent hover:text-foreground has-[>svg]:px-4 dark:hover:bg-sidebar-accent dark:hover:text-sidebar-accent-foreground\" to contain \"hover:text-sidebar-accent-foreground\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:432:32\n at runNextTicks (node:internal/process/task_queues:65:5)\n at listOnTimeout (node:internal/timers:567:9)\n at processTimers (node:internal/timers:541:7)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "dark-background",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": " dark:hover:bg-sidebar-accent ",
|
||||
"after": " dark:hover:bg-accent ",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(trigger?.classList).toContain(\"dark:hover:bg-sidebar-accent\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "e1c8a5b52bbd8abdeeebdc2257fff6c94ee10d92fd5933af3eb2ad9e73187086",
|
||||
"logSha256": "a11984915dd20c240e728db80d958100d62effb1d398de52e4b19116fc4df06a",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "6ef6c5847ced0672654141ddf703fe5da59b8e5698679983659f76b271836c34",
|
||||
"logSha256": "2289f05b931304d15b3e256a6b7c12d1d5b2377e938ca3d0bd2be14d6fa5ff93",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium transition-(--tp-color-background-color-border-color-box-shadow-opacity) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-(length:--rad-3) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive py-2 h-9 min-w-0 flex-1 justify-start gap-2 px-4 text-left hover:bg-sidebar-accent hover:text-sidebar-accent-foreground has-[>svg]:px-4 dark:hover:bg-accent dark:hover:text-sidebar-accent-foreground\" to contain \"dark:hover:bg-sidebar-accent\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:433:32\n at runNextTicks (node:internal/process/task_queues:65:5)\n at listOnTimeout (node:internal/timers:567:9)\n at processTimers (node:internal/timers:541:7)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "dark-foreground",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": "dark:hover:text-sidebar-accent-foreground\"",
|
||||
"after": "dark:hover:text-foreground\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(trigger?.classList).toContain(\"dark:hover:text-sidebar-accent-foreground\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "b8dec44374759a2d23864d8773b523144f17251cc417c6ba3a5a01ad84c74d54",
|
||||
"logSha256": "38199b184a9191ec1f9690b5ee93a55372041e1bda5cccc283dd1606e5ff8c22",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "11c7a74f5e2910ad7245ce8dc6d0659d1c611c534fb0549b36f2a3e35fb3f5e3",
|
||||
"logSha256": "d5fa2d5d892bc50fda4e5129d56424e33cc6ad64038c8b00ccc9a2071e24024b",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium transition-(--tp-color-background-color-border-color-box-shadow-opacity) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-(length:--rad-3) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive py-2 h-9 min-w-0 flex-1 justify-start gap-2 px-4 text-left hover:bg-sidebar-accent hover:text-sidebar-accent-foreground has-[>svg]:px-4 dark:hover:bg-sidebar-accent dark:hover:text-foreground\" to contain \"dark:hover:text-sidebar-accent-foreground\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:434:32\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "create-copy",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": ">Create organization</span>",
|
||||
"after": ">New organization</span>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(document.body.textContent).toContain(\"Create organization\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "094026d954d8a04e6c3f4a843905605b64087a7f1ffa32c0df6838da7b40abed",
|
||||
"logSha256": "dddb890d92209e044b1140d2438e28a02b814b8681fb96ab0e77e60c7026cf12",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "c4cb06631eace0851f3d31084c403ddb990f22f55f03096031361e195aba6211",
|
||||
"logSha256": "a7ecc001952a578b96aefa0a2c6f6ae7c87a01c8c64fb02102a50dad8c2f067a",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'AAcme LabsOrganizationsEditAAcme Labs\u2026' to contain 'Create organization'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:445:39\n at runNextTicks (node:internal/process/task_queues:65:5)\n at processTimers (node:internal/timers:538:9)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "legacy-create-copy",
|
||||
"owner": "ui/src/components/SidebarCompanyMenu.tsx",
|
||||
"sourceSha256": "2ea2c865a3e9817c140d3bf269a0c4f45d85a957f11b64d1a84e88926fe1b97a",
|
||||
"title": "toggles company order editing without selecting a company",
|
||||
"mutation": {
|
||||
"before": ">Create organization</span>",
|
||||
"after": ">Create organization Add company...</span>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(document.body.textContent).not.toContain(\"Add company...\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "4b450bb9ee4240877a53fa40ba1279d27a2c5d2eefbbd1ec6d66739b5a142c9f",
|
||||
"logSha256": "53756ff97bc7913aab6c0a3bc303fd7151fb7cde78616841a3f9a07ef9e84d7b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "93eb614e6f297f934fb4491e2b55a5d4a2f3216774b8d981a20d2b4a27b01a47",
|
||||
"logSha256": "220100c51d3b4e1ad9a020c6bb94af8f60b159cd1a5ef32cfcd67e04a5c77dfa",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'AAcme LabsOrganizationsEditAAcme Labs\u2026' not to contain 'Add company...'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/SidebarCompanyMenu.test.tsx:446:43\n at runNextTicks (node:internal/process/task_queues:65:5)\n at processTimers (node:internal/timers:538:9)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig-controls.json",
|
||||
"sha256": "cfb4357610beaa464cdb26848f74a7b8d967d6409c63dbd6821c92886a9cf980"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig.md",
|
||||
"sha256": "ea6d5dfc8cedcd653edd55ca9d77568431413a351f4de5db77ca6ac8f22b051a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental-controls.json",
|
||||
"sha256": "9c72abee7ee1a937fdbd6ccee870320308bc7ce06b4fc1e338cd9bd3b0efba6b"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental.md",
|
||||
"sha256": "93b7583a8ebba39dc97e390918fd62029955a00beda22756f55bd6374acc3737"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline-controls.json",
|
||||
"sha256": "af9d3f115e6e0518c2c0a2e11b8913e7aa82a35c848f114e6750884323601e17"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline.md",
|
||||
"sha256": "52d8e27970bd1c091ae7edf403b2331e921081da9198270df65ff779a8efc1bc"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat-controls.json",
|
||||
"sha256": "45d86fa901c8ab40bc3fe98d266e3ba1a7bbc93edb7bc2aae60ccd9bfa130c37"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat.md",
|
||||
"sha256": "15e84a553c5a468aea013941f68d3aa517bfb5a13ae296a90e1a39586f85de8e"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail-controls.json",
|
||||
"sha256": "a56f183f1c6b940662db0c4bfb62457eda0cec453c2019b439ec716ed9d2a3e1"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail.md",
|
||||
"sha256": "e137af5b991c936a167d6ad6f3c710b99688e939f839b163b203259fc998ded8"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# Test audit, Round 7 UI — Company menu opening follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 rendered scenarios and 28 net test lines** from `ui/src/components/SidebarCompanyMenu.test.tsx`. It adds no helper and changes no production lines. Audit base: `2e76f5157240ee8b2d42adbdcd4c345aea07c013`. All prior committed proof remains byte-identical. Registration/render savings are per complete-suite invocation; no measured runtime or CI-speed claim is made. A service-worker retirement counts only initializer setup, never a render.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Exact observations migrated |
|
||||
|---|---|---|
|
||||
| `uses company-centric create copy without the chat flag` | `toggles company order editing without selecting a company` | expect(trigger?.classList).toContain("px-4");; expect(trigger?.classList).toContain("has-[>svg]:px-4");; expect(trigger?.classList).toContain("hover:bg-sidebar-accent");; expect(trigger?.classList).toContain("hover:text-sidebar-accent-foreground");; expect(trigger?.classList).toContain("dark:hover:bg-sidebar-accent");; expect(trigger?.classList).toContain("dark:hover:text-sidebar-accent-foreground");; expect(trigger?.classList).not.toContain("hover:bg-background");; expect(trigger?.classList).not.toContain("dark:hover:bg-background");; expect(trigger?.classList).not.toContain("dark:hover:bg-accent/50");; expect(document.body.textContent).toContain("Create organization");; expect(document.body.textContent).not.toContain("Add company..."); |
|
||||
|
||||
The original fresh QueryClient/provider/root setup, two flushes, trigger query and presence assertion match exactly. Both pointerdown/click opening statements and following flush also match. All nine original trigger-class checks move before opening; both Create organization/Add company copy checks move after opening and before Edit. The keeper retains order editing, reorder-button/row guards and no selection/navigation assertions. Existing health-enabled actions, sign-out, loading/error, selection and other menu cases stay distinct. The six exact trigger-class mutations cover the inset and light/dark hover styling behaviors; the three negative legacy-color guards also remain in the keeper. Adding a conflicting class was rejected as mutation evidence because tailwind-merge removed the required positive token before the negative guard. No token or production style change is included.
|
||||
|
||||
The verifier requires matching setup statements, enclosing hooks and explicit teardown where present. It reconstructs candidate source solely from the declared registrations/assertion migrations and requires exact byte equality plus full-program AST equality outside those edits. Existing shared variable bindings, mock arguments and provider/component inputs remain the same. No other fixture, helper, action order, wait, assertion strength, timer or cleanup changes. Default CI includes the file in `@paperclipai/ui` under `general-workspaces-a`, once across its two file shards, with no workflow/config change.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 16 | 16 | 0 | 0 |
|
||||
| Candidate | 15 | 15 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/SidebarCompanyMenu.test.tsx`.
|
||||
|
||||
All 8 separately selected positive controls passed. All 8 distinct production-owner regressions failed at intended retained/migrated assertions, not setup, timeout or another guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| trigger-inset | `expect(trigger?.classList).toContain("px-4")` |
|
||||
| icon-inset | `expect(trigger?.classList).toContain("has-[>svg]:px-4")` |
|
||||
| hover-background | `expect(trigger?.classList).toContain("hover:bg-sidebar-accent")` |
|
||||
| hover-foreground | `expect(trigger?.classList).toContain("hover:text-sidebar-accent-foreground")` |
|
||||
| dark-background | `expect(trigger?.classList).toContain("dark:hover:bg-sidebar-accent")` |
|
||||
| dark-foreground | `expect(trigger?.classList).toContain("dark:hover:text-sidebar-accent-foreground")` |
|
||||
| create-copy | `expect(document.body.textContent).toContain("Create organization")` |
|
||||
| legacy-create-copy | `expect(document.body.textContent).not.toContain("Add company...")` |
|
||||
|
||||
Exactly one production owner changed per control, and every owner was restored byte for byte after each mutation and checked against pinned Git bytes. Literal keeper-name filters are escaped; unselected cases are not full-suite evidence. The initial first-install receipt verifier mistook emit( for it( and rejected its receipt despite the intended test failure. The declaration-boundary matcher was corrected and that control rerun; the rejected verifier attempt supplies no accepted evidence.
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-company-menu-controls.json) preserves source/owner hashes, exact before/after migrations, matched setup and variable-binding proof, complete-file run/log digests, intended assertion failures and restoration checks. Temporary raw scripts/logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion and is not claimed as passed. These separate focused packets precede delivery-head replay and full CI. This packet includes no push, rebase, PR or merge.
|
||||
@@ -0,0 +1,692 @@
|
||||
{
|
||||
"base": "e868e6645ce0d598d756d3458f0b3d71afd6db4a",
|
||||
"scope": [
|
||||
"ui/src/pages/InviteLanding.test.tsx",
|
||||
"ui/src/pages/Agents.test.tsx",
|
||||
"ui/src/components/Layout.test.tsx"
|
||||
],
|
||||
"testLinesRemoved": 364,
|
||||
"productionLinesChanged": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"sourceHelperCallsExpanded": 60,
|
||||
"speedClaim": null,
|
||||
"expansionNormalization": [
|
||||
"AST locations",
|
||||
"comments",
|
||||
"raw literal formatting",
|
||||
"whitespace-only JSX text"
|
||||
],
|
||||
"expansionVerifierSha256": "c97239d6b978cc11bb2f8ed11e0521ac107c37c6af5183dbd934d58b94418b3c",
|
||||
"expansions": [
|
||||
{
|
||||
"file": "ui/src/pages/InviteLanding.test.tsx",
|
||||
"originalSha": "2283067e52a3409f4c1a21e017e273649989559e7013a0d1442ebea268989bdd",
|
||||
"candidateSha": "84ab29d3d382710df5d39d8e6d84fb2cf916d10e2470e6432d54ac2875a55153",
|
||||
"expansions": 18,
|
||||
"wholeProgramAstEqualOutsideMigration": true,
|
||||
"migration": null,
|
||||
"helperSync": true,
|
||||
"parameterBindingArguments": [
|
||||
"root",
|
||||
"queryClient"
|
||||
],
|
||||
"freshJsxPerCall": true,
|
||||
"expandedAstSha": "a95408adf3b302b35d13a5a40d1b0cddacff4232bff02629a275f63698b3f489"
|
||||
},
|
||||
{
|
||||
"file": "ui/src/pages/Agents.test.tsx",
|
||||
"originalSha": "f7a84d1d31119880d6b11dc956d3121930ba4e92a2df7832d32676fc48cb2313",
|
||||
"candidateSha": "e2ab9c0de428585bee37b40df1ee6c4a3c823882582d006abadbfedbdd3e50ff",
|
||||
"expansions": 16,
|
||||
"wholeProgramAstEqualOutsideMigration": true,
|
||||
"migration": {
|
||||
"retired": "uses the built-in agents route segment as the built-in filter",
|
||||
"keeper": "shows and filters built-in agents when the experimental flag is enabled",
|
||||
"copiedExactRetiredSetup": true,
|
||||
"migratedCountGuard": "1 agent",
|
||||
"originalKeeperOtherAssertionsUnchanged": true
|
||||
},
|
||||
"helperSync": true,
|
||||
"parameterBindingArguments": [
|
||||
"root",
|
||||
"queryClient"
|
||||
],
|
||||
"freshJsxPerCall": true,
|
||||
"expandedAstSha": "2b0ff05182aa4cee5f94040fcff0321cb4ba7bce0e6a4d08bbaccf65e38873bb"
|
||||
},
|
||||
{
|
||||
"file": "ui/src/components/Layout.test.tsx",
|
||||
"originalSha": "d522929332ccbd413d56d5c9b7d7cd820344b58d6baa17b70002ecd387039093",
|
||||
"candidateSha": "89b6c599f2cecd570831e1a5d3d673bc1188052ca8eeedb6013892d25491f791",
|
||||
"expansions": 26,
|
||||
"wholeProgramAstEqualOutsideMigration": true,
|
||||
"migration": null,
|
||||
"helperSync": true,
|
||||
"parameterBindingArguments": [
|
||||
"root",
|
||||
"queryClient"
|
||||
],
|
||||
"freshJsxPerCall": true,
|
||||
"expandedAstSha": "150995d2bb95bb3c7afa57ce23f38b664f518c2fe7ed1ca039475f24fb6f6f9f"
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"label": "owners baseline",
|
||||
"jsonSha256": "8995c66ecc3951722950526f9c3d1b6de551b8ac6cc153576d1315c43e0c7a5c",
|
||||
"cases": 71,
|
||||
"passed": 71,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"path": "ui/src/pages/Agents.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 21
|
||||
},
|
||||
{
|
||||
"path": "ui/src/pages/InviteLanding.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 18
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/Layout.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "siblings baseline",
|
||||
"jsonSha256": "e4326b65ac0c85207df2c0a055a5ca7c34138fead744cf9ebd7c9ea060f27291",
|
||||
"cases": 79,
|
||||
"passed": 79,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"path": "ui/src/hooks/useCompanyInviteAccess.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 10
|
||||
},
|
||||
{
|
||||
"path": "ui/src/lib/company-selection.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"path": "ui/src/lib/shell-navigation.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 6
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/ActiveAgentsPanel.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 8
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/PluginAppShellOverlays.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 6
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/SidebarAgents.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 26
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/SidebarShell.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 9
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/access/InvitesSection.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "owners candidate",
|
||||
"jsonSha256": "3bf91a48289f6db06c9ca0fdaf552866c39cebc20dec89602fb2ad8faaceb498",
|
||||
"cases": 70,
|
||||
"passed": 70,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"path": "ui/src/pages/Agents.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 20
|
||||
},
|
||||
{
|
||||
"path": "ui/src/pages/InviteLanding.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 18
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/Layout.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "all complete candidate",
|
||||
"jsonSha256": "d17ff01fc3d46e1679832f3d589a4907cbed8be3f5012ae7e7079a93bfb5a118",
|
||||
"cases": 149,
|
||||
"passed": 149,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"files": [
|
||||
{
|
||||
"path": "ui/src/hooks/useCompanyInviteAccess.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 10
|
||||
},
|
||||
{
|
||||
"path": "ui/src/pages/Agents.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 20
|
||||
},
|
||||
{
|
||||
"path": "ui/src/pages/InviteLanding.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 18
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/ActiveAgentsPanel.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 8
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/Layout.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 32
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/PluginAppShellOverlays.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 6
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/SidebarAgents.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 26
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/SidebarShell.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 9
|
||||
},
|
||||
{
|
||||
"path": "ui/src/lib/company-selection.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 11
|
||||
},
|
||||
{
|
||||
"path": "ui/src/lib/shell-navigation.test.ts",
|
||||
"status": "passed",
|
||||
"cases": 6
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/access/InvitesSection.test.tsx",
|
||||
"status": "passed",
|
||||
"cases": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "builtin-filter",
|
||||
"owner": "ui/src/pages/Agents.tsx",
|
||||
"title": "shows and filters built-in agents when the experimental flag is enabled",
|
||||
"sourceSha": "a734799d3b015ddc04424754ff30eb2eb872db66e8847c4603132093fbb5e9b2",
|
||||
"replacements": 1,
|
||||
"mutation": {
|
||||
"before": "if (tab === \"builtin\") return builtInAgentIds.has(a.id);",
|
||||
"after": "if (tab === \"builtin\") return true;"
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"status": "passed",
|
||||
"jsonSha256": "9189086e19c4ca525e7c8692bfc3778bdb273647ee2eae0d1c9258c3904f68e6",
|
||||
"logSha256": "91eb9ad0112c20347e1229c9d40002be1a6fc7f3c6c2b15ff82ba9fb8a84621c"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"status": "failed",
|
||||
"jsonSha256": "c3710d64d3205aff35c8645d4c5675c49d2d1e28ac6f6bd3c2cfb49231386842",
|
||||
"logSha256": "3c0ec61eae54edafcce43bfed5d791aa0d65ff8e6c99969a3e378e537942c8bf",
|
||||
"intendedFailure": "AssertionError: expected 'AllActivePausedErrorBuilt-inNew Agent\u2026' to contain '1 agent'"
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "invite-aria-invalid",
|
||||
"owner": "ui/src/pages/InviteLanding.tsx",
|
||||
"title": "renders invite auth errors in an alert region referenced by the inputs",
|
||||
"sourceSha": "626dc7c372a74ff436f1f96f675ecf55bd0b2ae60107ec61bc88cec8c6fefe18",
|
||||
"replacements": 3,
|
||||
"mutation": {
|
||||
"before": "aria-invalid={authFeedback?.tone === \"error\" ? true : undefined}",
|
||||
"after": "aria-invalid={undefined}"
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"status": "passed",
|
||||
"jsonSha256": "f5401b7b6c33dea587d00dedb8fe64c3baaa04f45fcb54ebd3225b0339de4291",
|
||||
"logSha256": "802bc10c23fbeb70cae9effbef913145a90a28dd893550be3c6941ffbd1d4396"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"status": "failed",
|
||||
"jsonSha256": "58d01e3723ff05867f87f82e2fa857d4d1d3d5f81dd50d04603d7da3b148c060",
|
||||
"logSha256": "c90dcb374262c2caf2ca5a27069a2cbe5a2a1b4615df05d82a4e82e129e58080",
|
||||
"intendedFailure": "AssertionError: expected null to be 'true' // Object.is equality"
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "settings-sidebar",
|
||||
"owner": "ui/src/components/Layout.tsx",
|
||||
"title": "replaces the app sidebar with settings navigation on Streamlined settings routes",
|
||||
"sourceSha": "4c236e0330ed4946572af9528f27d24c305bc3e73473814e921017199d4afa8a",
|
||||
"replacements": 1,
|
||||
"mutation": {
|
||||
"before": "const sharedSecondarySidebar = isCompanySettingsRoute ? (",
|
||||
"after": "const sharedSecondarySidebar = false ? ("
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"status": "passed",
|
||||
"jsonSha256": "c4cba1779379171ad2d0fc234e699944118ab453f3f87c4547d92e688d3f64ef",
|
||||
"logSha256": "5e6de08a8cda51850e9e237a76d993ceff3e32677ef38faaf2bffd0d40c6cf10"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"status": "failed",
|
||||
"jsonSha256": "a0cdaa7f1233d4f88d6abd92f61984343f41b27e2723b8ce28b16a8c97624366",
|
||||
"logSha256": "83b0ab380a65c7f9ec69f8ec36132dbafb6ef9cc2c4df220b20415144244eab3",
|
||||
"intendedFailure": "AssertionError: expected 'Skip to Main ContentPaperclipCompany \u2026' to contain 'Company settings sidebar'"
|
||||
}
|
||||
}
|
||||
],
|
||||
"caseLedger": [
|
||||
{
|
||||
"file": "ui/src/pages/Agents.test.tsx",
|
||||
"cases": [
|
||||
{
|
||||
"title": "omits the action bar from streamlined agent list rows",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "omits the action bar from production agent list rows",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "shows the configured model beside the adapter on the all agents page",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "switches between the preserved list and the interactive org chart with icon buttons",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "gives mobile agent names the full row width after the leading status indicator",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "uses the built-in agents route segment as the built-in filter",
|
||||
"decision": "C",
|
||||
"evidence": "Exact keeper retains fixture/count plus all stronger assertions"
|
||||
},
|
||||
{
|
||||
"title": "shows effective environment and sandbox provider beside agents",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "uses configured names for local-driver environments",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "reserves the environment column while environment metadata is loading",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "hides the environment column when there is only one configured environment",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "hides the environment column when environments are experimentally disabled",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "uses the instance default environment unless the agent overrides it",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "falls back to the raw sandbox provider config when capabilities omit a display name",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "does not show environment filter or grouping controls yet",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "hides built-in agent surfaces while the experimental flag is disabled",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "shows and filters built-in agents when the experimental flag is enabled",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "gives list-view rows a fixed-width title so meta columns align (PAP-86)",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "omits star and leave/join actions from streamlined agent index list and org views",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "omits star and leave/join actions from production agent index list and org views",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "does not dim left-membership agent names on mobile",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps invalid-org-chain agents visible with a warning marker",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "ui/src/pages/InviteLanding.test.tsx",
|
||||
"cases": [
|
||||
{
|
||||
"title": "keeps agent-invite onboarding on legacy adapters",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "defaults invite auth to account creation and guides existing users back to sign in",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "carries password-manager metadata and a11y attributes on the invite auth form",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "renders invite auth errors in an alert region referenced by the inputs",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "turns invalid sign-in responses into a clear invite-specific message",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "auto-accepts the invite after account creation and redirects into the company",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "shows the pending approval page with the company icon and non-clickable access instructions",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "auto-completes a previously accepted human invite after sign-in",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "asks unauthenticated users to sign in before completing an accepted human invite",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "redirects straight to the company after sign-in when the user already has access",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "re-reads membership for the account that just signed in",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "re-reads membership on mount for an already signed-in account",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the pending invite token when the session has lapsed",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "still reads the shared list as membership on a local_trusted instance",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "shows invite details instead of auto-redirecting for signed-in existing members",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "falls back to the generated company icon when the invite logo fails to load",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "normalizes the shared company cache envelope before checking membership",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "waits for the membership check before showing invite acceptance to signed-in users",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "ui/src/components/Layout.test.tsx",
|
||||
"cases": [
|
||||
{
|
||||
"title": "does not render the deployment explainer in the shared layout",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "scopes the Streamlined task-detail surface while preserving balanced horizontal gutters",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "collapses atomically when the pointer is still over the sidebar (no re-peek) \u2014 PAP-10676",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "opens the peek when hovering a collapsed rail (positive control for the hover sim)",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "replaces the app sidebar with settings navigation on Streamlined settings routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the global sidebar beside legacy settings navigation when Streamlined UI is off",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "renders a mobile company settings selector on company settings routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "replaces the company nav on instance settings routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "replaces the company nav with the shared settings sidebar on /PAP/company/export",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "replaces the company nav with the shared settings sidebar on /PAP/company/import",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the company nav beside Apps navigation on legacy tools routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "mounts the Apps secondary sidebar regardless of the retired experimental flag",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the M8 advanced-setup tabs",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on legacy developer tabs behind the Advanced door",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the browse surface",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the connections surface",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the vercel-connect surface",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the review surface",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the chat surface",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the Apps sidebar on the gateways list and detail routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "uses the app connection sidebar on app detail routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "uses the app detail sidebar on not-connected app routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps global navigation beside Skills, Agent, and Routine details",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps global navigation with agent configuration navigation in the legacy shell",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the global rail beside Skills navigation in the legacy shell",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps Agent and Routine collection routes in global navigation",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "renders a route-scoped plugin sidebar for a matching plugin page route",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the route-scoped plugin sidebar on nested plugin page routes",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "uses the route company context for plugin route sidebars on the first render",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "keeps the normal company sidebar when a plugin page route is ambiguous",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "clips horizontal overflow on the mobile layout root so the viewport can't scroll sideways",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
},
|
||||
{
|
||||
"title": "clips overflow on the desktop layout root",
|
||||
"decision": "R",
|
||||
"evidence": "Whole-program expansion retains original assertions and action order outside the one recorded keeper migration"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rawEvidenceAvailability": "Raw verifiers and run logs are temporary local audit packets; the committed report/JSON retain accepted decisions and digests, not complete raw payloads.",
|
||||
"helperCleanupNetLinesRemoved": 320,
|
||||
"duplicateRetirementNetLinesRemoved": 44
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/pages/InstanceExperimentalSettings.test.tsx",
|
||||
"base": "e3effbb9e5c66b838f67fd487987c8bf1007e413",
|
||||
"originalSha256": "52dac85dd5b2ac621da8b25a73a343a15068fde818e64d59d5cf22ab9eee1dd0",
|
||||
"candidateSha256": "4a1047181ad420553d9c9470f59f5df4287d9e37b06fc11cb4658625a758df65",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 2,
|
||||
"rootRendersRemoved": 2,
|
||||
"netTestLinesRemoved": 10,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "does not render the Pipelines experimental setting for now",
|
||||
"keeper": "does not render the Conference Room Chat experimental setting for now",
|
||||
"exactSetupStatements": 2,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(headings).not.toContain(\"Pipelines\");",
|
||||
"expect(container.querySelector('button[aria-label=\"Toggle pipelines experimental setting\"]')).toBeNull();"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "shows the cutoff-copy for the worktree run-execution toggle when off",
|
||||
"keeper": "renders and patches the worktree run-execution toggle when in a worktree",
|
||||
"exactSetupStatements": 2,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(container.textContent).toContain(\n \"Only tasks created after enabling will run automatically\",\n );",
|
||||
"expect(container.textContent).toContain(\"Toggling off and on resets the cutoff.\");",
|
||||
"expect(container.textContent).not.toContain(\"Running tasks created after\");",
|
||||
"expect(container.textContent).not.toContain(\"Execution is suppressed\");"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "084bdbc359e510397ef64daeb24e259efbc62c8b5d2c56d3e09d7f2534f155aa",
|
||||
"mutationVerifierSha256": "ca8463047109d38a447d93fb94a19bb8efe9b1df440e0abf91ab31a8c0a358a2",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 55,
|
||||
"passed": 55,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "77f407f50f91bcdacb518c7b87b6dc5b47b9161a9bc49bfab32a6ee228351d38",
|
||||
"logSha256": "a92310f5aab6de68b137ab9f6d8348fc0fe8640e37740ec0bfb493b3017edeb6"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 53,
|
||||
"passed": 53,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "09e8de86d6b8fcbe9fb5fb4c74ea6a5fb57c1135763c5b6054232409435a6bc3",
|
||||
"logSha256": "099ac5b8f1f678b23518f93833cf2924c4dd2833700f7f6a009f1065fb573bf3"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "pipelines-heading",
|
||||
"owner": "ui/src/pages/InstanceExperimentalSettings.tsx",
|
||||
"sourceSha256": "946dd94d28db014fe2dbfc0bb8d4c5a4b96c3face7fca7a067789119abe76570",
|
||||
"title": "does not render the Conference Room Chat experimental setting for now",
|
||||
"mutation": {
|
||||
"before": " {SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING ? (",
|
||||
"after": " <section><h2>Pipelines</h2></section>\n {SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING ? (",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(headings).not.toContain(\"Pipelines\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "7250f54890e55f4ad28063a359430027e325435ba39877b46b0d1a052196b8e5",
|
||||
"logSha256": "f9a5178102546567b9cd25aed172c9f25496cfb250af226edb0721e5293792ef",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "a1bfbd98a6faf2364a061415d99e76799b6d23ed8627c5e5e27de64901e6b108",
|
||||
"logSha256": "d498153df7fd34e0fe41d1c8996cc867553b949b266fe6e83bd1c72b76a0a6a2",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected [ 'Experimental features', \u2026(3) ] to not include 'Pipelines'\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2077:15)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/InstanceExperimentalSettings.test.tsx:319:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "pipelines-toggle",
|
||||
"owner": "ui/src/pages/InstanceExperimentalSettings.tsx",
|
||||
"sourceSha256": "946dd94d28db014fe2dbfc0bb8d4c5a4b96c3face7fca7a067789119abe76570",
|
||||
"title": "does not render the Conference Room Chat experimental setting for now",
|
||||
"mutation": {
|
||||
"before": " {SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING ? (",
|
||||
"after": " <button aria-label=\"Toggle pipelines experimental setting\" />\n {SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING ? (",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(container.querySelector('button[aria-label=\"Toggle pipelines experimental setting\"]')).toBeNull()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "a33d4d20281ca29d7e8ed3e27d4e7d5426053a5d0e2d95e1805dc33a0ae488fb",
|
||||
"logSha256": "26e0d1406c59c0ea2ef2c24b94df8a26c3a64ee31ba8a625e8a1079389687b26",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "7fc3b1c7422ea1f814176df613b12f3250ad66816b25ce78b0c7a6dc034c45b9",
|
||||
"logSha256": "09c205c0b6bb1b99738b5d9ae609402c623d2e9880502dc6ea07ee406a020c8b",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected <button \u2026(1)></button> to be null\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/InstanceExperimentalSettings.test.tsx:320:99\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "cutoff-copy",
|
||||
"owner": "ui/src/pages/InstanceExperimentalSettings.tsx",
|
||||
"sourceSha256": "946dd94d28db014fe2dbfc0bb8d4c5a4b96c3face7fca7a067789119abe76570",
|
||||
"title": "renders and patches the worktree run-execution toggle when in a worktree",
|
||||
"mutation": {
|
||||
"before": "Only tasks created after enabling will run automatically",
|
||||
"after": "The cutoff copy is missing",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).toContain(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "92c1be77e6c65655e99e26189954c5143e8c248be26f32d1b76257b97db781e4",
|
||||
"logSha256": "dd36eab0fb20ae3a948e91625ef5e8c29c7a0451623d6b50f91c89881f43b16a",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "ebdd5d754e22071cbc50f1246b16b38fc14349885f0f77da2f257e208b780211",
|
||||
"logSha256": "145903c50a5cb4eb8185d3c160f986a470c068259c54c74d7c05961df6cb828d",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'ExperimentalOpt into features that ar\u2026' to contain 'Only tasks created after enabling wil\u2026'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/InstanceExperimentalSettings.test.tsx:593:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "reset-copy",
|
||||
"owner": "ui/src/pages/InstanceExperimentalSettings.tsx",
|
||||
"sourceSha256": "946dd94d28db014fe2dbfc0bb8d4c5a4b96c3face7fca7a067789119abe76570",
|
||||
"title": "renders and patches the worktree run-execution toggle when in a worktree",
|
||||
"mutation": {
|
||||
"before": "Toggling off and on resets the cutoff.",
|
||||
"after": "The reset copy is missing",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).toContain(\"Toggling off and on resets the cutoff.\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "08e815451edaa0b5a916e5027dfb0cb72a723dfde98e234873404a4f9cd4ccce",
|
||||
"logSha256": "ba51c2a1cda7f0cc33a686e28fc9fc405737107133c3beea98fcf1c3696bd5c4",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "21d1d20cb6fd05663c4decd09baeb5533d2147017a7bbb14eee0657f295953b0",
|
||||
"logSha256": "c0efad555c6819cd6e7c91692ac64521de7f1045be075ca3330cc6dd1c43df84",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'ExperimentalOpt into features that ar\u2026' to contain 'Toggling off and on resets the cutoff.'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/InstanceExperimentalSettings.test.tsx:596:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "armed-banner",
|
||||
"owner": "ui/src/pages/InstanceExperimentalSettings.tsx",
|
||||
"sourceSha256": "946dd94d28db014fe2dbfc0bb8d4c5a4b96c3face7fca7a067789119abe76570",
|
||||
"title": "renders and patches the worktree run-execution toggle when in a worktree",
|
||||
"mutation": {
|
||||
"before": " {worktreeRunExecutionState.kind === \"armed\" ? (",
|
||||
"after": " <span>Running tasks created after</span>\n {worktreeRunExecutionState.kind === \"armed\" ? (",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).not.toContain(\"Running tasks created after\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "c9a437b67ada80258549e3e55e2839eb9247dca4e28eb31de8d5f5cea62aeae9",
|
||||
"logSha256": "4b4b08da42e012356f839eee58b1c22d7b897d5055171e9167dfc54e29d314b7",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "53682bf5e687c68851723b3729a8f0979c0e68800186b70cf5d9c4a2e1cc8f04",
|
||||
"logSha256": "7accc209bfd5438d9772a1d2f3d121b976f0579af35853d8367c527c80ada5af",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'ExperimentalOpt into features that ar\u2026' not to contain 'Running tasks created after'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/InstanceExperimentalSettings.test.tsx:598:39\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "fail-closed",
|
||||
"owner": "ui/src/pages/InstanceExperimentalSettings.tsx",
|
||||
"sourceSha256": "946dd94d28db014fe2dbfc0bb8d4c5a4b96c3face7fca7a067789119abe76570",
|
||||
"title": "renders and patches the worktree run-execution toggle when in a worktree",
|
||||
"mutation": {
|
||||
"before": " {worktreeRunExecutionState.kind === \"fail_closed\" ? (",
|
||||
"after": " <span>Execution is suppressed</span>\n {worktreeRunExecutionState.kind === \"fail_closed\" ? (",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).not.toContain(\"Execution is suppressed\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "0dccde3c027394a05076bc86035b085a2f420f102f65c234e6f536aaaf452e5e",
|
||||
"logSha256": "9db8b2995432fdd99a941e894df0148b4fe83d12a20a86d87acc594b874c8005",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "fbc4edb913cba46818b5d9dd073a1b02d804e07f8a50e49bd180e5ab6c86b609",
|
||||
"logSha256": "4f2dc5d037bea144ce679f1800079de085b153040ea553bae985bdc765e670fa",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'ExperimentalOpt into features that ar\u2026' not to contain 'Execution is suppressed'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/InstanceExperimentalSettings.test.tsx:599:39\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# Test audit, Round 7 UI — Experimental settings follow-up
|
||||
|
||||
This separate packet removes **2 registered executions, 2 root renders and 10 net test lines** from `ui/src/pages/InstanceExperimentalSettings.test.tsx`. It adds no helper and changes no production lines. Audit base: `e3effbb9e5c66b838f67fd487987c8bf1007e413`. Prior committed evidence remains byte-identical. Render/execution savings are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Observations retained |
|
||||
|---|---|---|
|
||||
| `does not render the Pipelines experimental setting for now` | `does not render the Conference Room Chat experimental setting for now` | expect(headings).not.toContain("Pipelines");; expect(container.querySelector('button[aria-label="Toggle pipelines experimental setting"]')).toBeNull(); |
|
||||
| `shows the cutoff-copy for the worktree run-execution toggle when off` | `renders and patches the worktree run-execution toggle when in a worktree` | expect(container.textContent).toContain( "Only tasks created after enabling will run automatically", );; expect(container.textContent).toContain("Toggling off and on resets the cutoff.");; expect(container.textContent).not.toContain("Running tasks created after");; expect(container.textContent).not.toContain("Execution is suppressed"); |
|
||||
|
||||
InstanceExperimentalSettings.tsx keeps the retired Pipelines/Conference Room settings absent and owns the worktree-run toggle and activation-state banners. Two initial page renders consolidate independently: hidden Pipelines heading/toggle checks join the hidden Conference Room keeper; all off-state cutoff/reset copy and armed/fail-closed banner exclusions join the existing worktree toggle keeper before its click. Runtime worktree metadata, shared settings fixture, renderPage helper and lifecycle match in each pair. Enabled/armed/mismatch/managed/runtime-specific and toggle-interaction cases remain unchanged.
|
||||
|
||||
The AST verifier checks exact setup, shared enclosing hooks and matched cleanup where present, reconstructs candidate text solely from the declared registrations/assertion migrations, and requires exact byte equality plus full-program AST equality outside those edits. No fixture, helper, provider, action order, wait, assertion strength, timer or teardown changes. The IssueChatThread keeper is an existing complete subset proof; InlineEditor’s identical not-null guard is retained once. Every other case stays byte-identical. Default CI includes this file in `@paperclipai/ui` under `general-workspaces-a`, once across the two file shards; workflow/config unchanged.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 55 | 55 | 0 | 0 |
|
||||
| Candidate | 53 | 53 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/pages/InstanceExperimentalSettings.test.tsx`.
|
||||
|
||||
All 6 isolated positive controls passed. All 6 deliberate behavior regressions failed at their intended retained/migrated assertion, rather than setup, timeout or a different guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| pipelines-heading | `expect(headings).not.toContain("Pipelines")` |
|
||||
| pipelines-toggle | `expect(container.querySelector('button[aria-label="Toggle pipelines experimental setting"]')).toBeNull()` |
|
||||
| cutoff-copy | `expect(container.textContent).toContain(` |
|
||||
| reset-copy | `expect(container.textContent).toContain("Toggling off and on resets the cutoff.")` |
|
||||
| armed-banner | `expect(container.textContent).not.toContain("Running tasks created after")` |
|
||||
| fail-closed | `expect(container.textContent).not.toContain("Execution is suppressed")` |
|
||||
|
||||
Exactly one production owner was temporarily changed per control. Every owner was restored byte for byte after each mutation and matched pinned Git bytes. Filtered controls select one keeper; unselected tests do not count as full-suite evidence. [Controls JSON](2026-10-12-test-audit-r7-ui-experimental-controls.json) records exact owner/source hashes, migration/input proof, complete-file run/log digests, mutation bodies, intended assertion failures and restoration receipts. Temporary raw verifiers and logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad `pnpm test:run` was intentionally stopped without completion and is not claimed as passed. Separate packet results are focused proof, not a completed combined delivery-head replay or pending full CI. No push, rebase, PR or merge is included here.
|
||||
@@ -0,0 +1,221 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/IssueFiltersPopover.test.tsx",
|
||||
"base": "2e76f5157240ee8b2d42adbdcd4c345aea07c013",
|
||||
"originalSha256": "bc64ad73ccd00e837e92a2ddf0309cf063e30e6a55704b0e8031a6c95d697688",
|
||||
"candidateSha256": "6bdb383ad113f555c1208bbdb1bda2203e027a059751fb9fa7b405d5e9c7f8ef",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1,
|
||||
"netTestLinesRemoved": 22,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "hides the Priority filter section while priority UI is off (PAP-411)",
|
||||
"keeper": "uses a scrollable popover and a three-column desktop grid",
|
||||
"exactLeadingStatements": 4,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": false,
|
||||
"kind": "normal",
|
||||
"exactMigratedStatements": [
|
||||
"expect(popoverContent?.textContent).toContain(\"Status\");",
|
||||
"expect(popoverContent?.textContent).not.toContain(\"Priority\");"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "ff07f5e2e308cfc0f26584a6cd6599bcd98665e79d7f4782ed5bb36b35cb8cb9",
|
||||
"mutationVerifierSha256": "676b973d4838ed130c5ae92d4bc50fb32a65309af8c858450ec34bcce13729e9",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 5,
|
||||
"passed": 5,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "04cc18d7e6470f2861ac1d76af11bef0e60492f45dd94685df4efe078436bee4",
|
||||
"logSha256": "05761c256ebb185006c0a3fc406347c46ef9543ba1ddcf5242e8f6e8f775356f"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 4,
|
||||
"passed": 4,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "4deabc223e9f57bacf81f0c7d87b621c1eb620f397b54c1b49a876b3603810d0",
|
||||
"logSha256": "d4a57beb3f938c21882056691d74fcb1bfc3db84656987cd3aaa5de8737fdbde"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "status",
|
||||
"owner": "ui/src/components/IssueFiltersPopover.tsx",
|
||||
"sourceSha256": "ea92ceec823ad162f6fd843d094c849b9cd535aba1721059f7ea6d8c780d734b",
|
||||
"title": "uses a scrollable popover and a three-column desktop grid",
|
||||
"mutation": {
|
||||
"before": ">Status</span>",
|
||||
"after": ">Missing status</span>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(popoverContent?.textContent).toContain(\"Status\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "03e26e0e193e289480f5ee00500ec4d4e0e4d1de112ef708d7754a712fdadacc",
|
||||
"logSha256": "de89d91b9b7e15774cc570b6fed343ace6fe6468c32f62045c4439061400b53e",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "81b7e1eb1de267f667c001ed838b65523fa8015f4265c84d82c19b9dd847d172",
|
||||
"logSha256": "8f981560c571bacd648edcc6322ceee073cad7039085186a8381aa62f447b2be",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'FiltersQuick filtersAllActiveBacklogD\u2026' to contain 'Status'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/IssueFiltersPopover.test.tsx:76:41\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "priority",
|
||||
"owner": "ui/src/components/IssueFiltersPopover.tsx",
|
||||
"sourceSha256": "ea92ceec823ad162f6fd843d094c849b9cd535aba1721059f7ea6d8c780d734b",
|
||||
"title": "uses a scrollable popover and a three-column desktop grid",
|
||||
"mutation": {
|
||||
"before": "{SHOW_TASK_PRIORITY_UI && (",
|
||||
"after": "{true && (",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(popoverContent?.textContent).not.toContain(\"Priority\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "2b9d14ef5dba8d78ca254915e80969abb778dce7b6648548cc9684b3c79b698c",
|
||||
"logSha256": "381ff350cd72e952c86246a8dd06867cd766e776fd6f471b71e220b312799866",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "d816a0329a953d6dd71e16eca02e95439534e24e753d50f1ea742ad43d9e9396",
|
||||
"logSha256": "3fc77abdcbcedc235471237fdbb6a7d36caf9aa765193a9c62ffee4d39b99bb3",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'FiltersQuick filtersAllActiveBacklogD\u2026' not to contain 'Priority'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/IssueFiltersPopover.test.tsx:77:45\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig-controls.json",
|
||||
"sha256": "cfb4357610beaa464cdb26848f74a7b8d967d6409c63dbd6821c92886a9cf980"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig.md",
|
||||
"sha256": "ea6d5dfc8cedcd653edd55ca9d77568431413a351f4de5db77ca6ac8f22b051a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental-controls.json",
|
||||
"sha256": "9c72abee7ee1a937fdbd6ccee870320308bc7ce06b4fc1e338cd9bd3b0efba6b"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental.md",
|
||||
"sha256": "93b7583a8ebba39dc97e390918fd62029955a00beda22756f55bd6374acc3737"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline-controls.json",
|
||||
"sha256": "af9d3f115e6e0518c2c0a2e11b8913e7aa82a35c848f114e6750884323601e17"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline.md",
|
||||
"sha256": "52d8e27970bd1c091ae7edf403b2331e921081da9198270df65ff779a8efc1bc"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat-controls.json",
|
||||
"sha256": "45d86fa901c8ab40bc3fe98d266e3ba1a7bbc93edb7bc2aae60ccd9bfa130c37"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat.md",
|
||||
"sha256": "15e84a553c5a468aea013941f68d3aa517bfb5a13ae296a90e1a39586f85de8e"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail-controls.json",
|
||||
"sha256": "a56f183f1c6b940662db0c4bfb62457eda0cec453c2019b439ec716ed9d2a3e1"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail.md",
|
||||
"sha256": "e137af5b991c936a167d6ad6f3c710b99688e939f839b163b203259fc998ded8"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Test audit, Round 7 UI — Issue filter popover follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 rendered scenarios and 22 net test lines** from `ui/src/components/IssueFiltersPopover.test.tsx`. It adds no helper and changes no production lines. Audit base: `2e76f5157240ee8b2d42adbdcd4c345aea07c013`. All prior committed proof remains byte-identical. Registration/render savings are per complete-suite invocation; no measured runtime or CI-speed claim is made. A service-worker retirement counts only initializer setup, never a render.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Exact observations migrated |
|
||||
|---|---|---|
|
||||
| `hides the Priority filter section while priority UI is off (PAP-411)` | `uses a scrollable popover and a three-column desktop grid` | expect(popoverContent?.textContent).toContain("Status");; expect(popoverContent?.textContent).not.toContain("Priority"); |
|
||||
|
||||
Both registered cases use identical streamlined props, filter state, agents/projects/labels and routine-visibility flag, root/act render and positive popover lookup. The existing rich scroll/layout keeper gains exact Status-presence and Priority-absence checks before its original layout assertions. Real IssueFiltersPopover.tsx gates the Priority section independently of Status. Option search, legacy presentation and interactive inbox-scope cases remain unchanged. Existing teardown is unchanged.
|
||||
|
||||
The verifier requires matching setup statements, enclosing hooks and explicit teardown where present. It reconstructs candidate source solely from the declared registrations/assertion migrations and requires exact byte equality plus full-program AST equality outside those edits. Existing shared variable bindings, mock arguments and provider/component inputs remain the same. No other fixture, helper, action order, wait, assertion strength, timer or cleanup changes. Default CI includes the file in `@paperclipai/ui` under `general-workspaces-a`, once across its two file shards, with no workflow/config change.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 5 | 5 | 0 | 0 |
|
||||
| Candidate | 4 | 4 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/IssueFiltersPopover.test.tsx`.
|
||||
|
||||
All 2 separately selected positive controls passed. All 2 distinct production-owner regressions failed at intended retained/migrated assertions, not setup, timeout or another guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| status | `expect(popoverContent?.textContent).toContain("Status")` |
|
||||
| priority | `expect(popoverContent?.textContent).not.toContain("Priority")` |
|
||||
|
||||
Exactly one production owner changed per control, and every owner was restored byte for byte after each mutation and checked against pinned Git bytes. Literal keeper-name filters are escaped; unselected cases are not full-suite evidence. The initial first-install receipt verifier mistook emit( for it( and rejected its receipt despite the intended test failure. The declaration-boundary matcher was corrected and that control rerun; the rejected verifier attempt supplies no accepted evidence.
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-filters-controls.json) preserves source/owner hashes, exact before/after migrations, matched setup and variable-binding proof, complete-file run/log digests, intended assertion failures and restoration checks. Temporary raw scripts/logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion and is not claimed as passed. These separate focused packets precede delivery-head replay and full CI. This packet includes no push, rebase, PR or merge.
|
||||
@@ -0,0 +1,180 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/InlineEditor.test.tsx",
|
||||
"base": "e3effbb9e5c66b838f67fd487987c8bf1007e413",
|
||||
"originalSha256": "50117c71ad5cf0319742d5aff24d3b36b03576d45f7d7eb5759574cf373afbe6",
|
||||
"candidateSha256": "d556cf16f5ca87e44174b4e680a66ce19b69eb3fc01834544795638b7e898f32",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1,
|
||||
"netTestLinesRemoved": 15,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "marks multiline preview textboxes as multiline",
|
||||
"keeper": "enters multiline edit mode from the keyboard preview surface",
|
||||
"exactSetupStatements": 4,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 1,
|
||||
"migratedGuards": [
|
||||
"expect(preview?.getAttribute(\"aria-multiline\")).toBe(\"true\");",
|
||||
"expect(preview?.tabIndex).toBe(0);"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [
|
||||
"expect(preview).not.toBeNull();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "084bdbc359e510397ef64daeb24e259efbc62c8b5d2c56d3e09d7f2534f155aa",
|
||||
"mutationVerifierSha256": "ca8463047109d38a447d93fb94a19bb8efe9b1df440e0abf91ab31a8c0a358a2",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 10,
|
||||
"passed": 10,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "e2d0e45ee5a399f94e9dae9063e10eb82c4732e8735abb2910e7a99ce43b9e56",
|
||||
"logSha256": "ff89195de1b2aea03e77b06103dc733d634075241a30cddf8fcf83cf0523cb38"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 9,
|
||||
"passed": 9,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "9e64a19f6fe426eaa9976f6e57c1b3df624a90bc6985ed1b68ce2dedd7d8b138",
|
||||
"logSha256": "6001db7d0b44f5162dfb7f3277f7a41ae44c32d8c07c70055c0c69a0fca470a9"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "multiline",
|
||||
"owner": "ui/src/components/InlineEditor.tsx",
|
||||
"sourceSha256": "9b14c37ef76ab2f6372fd56e9b5b7d3125f50cb352dfec8c4a320cf91d77ef3c",
|
||||
"title": "enters multiline edit mode from the keyboard preview surface",
|
||||
"mutation": {
|
||||
"before": "aria-multiline=\"true\"",
|
||||
"after": "aria-multiline=\"false\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(preview?.getAttribute(\"aria-multiline\")).toBe(\"true\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "61dc2a3025f09b64b8598cd30c3417fb352519278c7c62b794dff2f236849c9d",
|
||||
"logSha256": "4b25b8b35d984b55fa024c6c36b2ec739ba1cac2383f325edba1787ec5b7b764",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "4341b22562ba43c8ab24ce4e90eb83cf99c143a89aa87785c0acb6b3935e1290",
|
||||
"logSha256": "42c7242f5b1887c6eee56ad0a6be72a429432bc514cdc8f6a6725ced42caad80",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'false' to be 'true' // Object.is equality\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/InlineEditor.test.tsx:221:53\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "tab-index",
|
||||
"owner": "ui/src/components/InlineEditor.tsx",
|
||||
"sourceSha256": "9b14c37ef76ab2f6372fd56e9b5b7d3125f50cb352dfec8c4a320cf91d77ef3c",
|
||||
"title": "enters multiline edit mode from the keyboard preview surface",
|
||||
"mutation": {
|
||||
"before": "tabIndex={0}",
|
||||
"after": "tabIndex={-1}",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(preview?.tabIndex).toBe(0)",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "b6064902d779a1e4e32ed95b27fcae92c6a23f52818983552ff628bf3bb57d04",
|
||||
"logSha256": "09f4dbfc349e5dfafac95f9a3be763025bd88b509d4c3e4cdb7b9aec091f21d2",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "a97cb51553559f3ab8e7ccc074a9c71fc29b06acd0c3d098861c57432fda2452",
|
||||
"logSha256": "561db47d821ce123ed915b2f6428cdc2990d4ea4bfebefb78c8875f822ce8617",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected -1 to be +0 // Object.is equality\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/InlineEditor.test.tsx:222:31\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Test audit, Round 7 UI — Inline editor accessibility follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 root renders and 15 net test lines** from `ui/src/components/InlineEditor.test.tsx`. It adds no helper and changes no production lines. Audit base: `e3effbb9e5c66b838f67fd487987c8bf1007e413`. Prior committed evidence remains byte-identical. Render/execution savings are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Observations retained |
|
||||
|---|---|---|
|
||||
| `marks multiline preview textboxes as multiline` | `enters multiline edit mode from the keyboard preview surface` | expect(preview?.getAttribute("aria-multiline")).toBe("true");; expect(preview?.tabIndex).toBe(0); |
|
||||
|
||||
InlineEditor.tsx exposes a focusable multiline preview textbox and enters edit mode on Enter. Both tests use identical non-empty value, multiline prop, fresh resolved onSave mock, real root, act render, preview query and unmount. The keyboard keeper already has the same not-null assertion; that exact duplicate is retained once. Exact aria-multiline and tabIndex guards move before Enter, preserving keyboard editing and preview/editor assertions. Focus, nullable save, click-entry, prop-sync and blur behavior remain distinct and unchanged.
|
||||
|
||||
The AST verifier checks exact setup, shared enclosing hooks and matched cleanup where present, reconstructs candidate text solely from the declared registrations/assertion migrations, and requires exact byte equality plus full-program AST equality outside those edits. No fixture, helper, provider, action order, wait, assertion strength, timer or teardown changes. The IssueChatThread keeper is an existing complete subset proof; InlineEditor’s identical not-null guard is retained once. Every other case stays byte-identical. Default CI includes this file in `@paperclipai/ui` under `general-workspaces-a`, once across the two file shards; workflow/config unchanged.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 10 | 10 | 0 | 0 |
|
||||
| Candidate | 9 | 9 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/InlineEditor.test.tsx`.
|
||||
|
||||
All 2 isolated positive controls passed. All 2 deliberate behavior regressions failed at their intended retained/migrated assertion, rather than setup, timeout or a different guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| multiline | `expect(preview?.getAttribute("aria-multiline")).toBe("true")` |
|
||||
| tab-index | `expect(preview?.tabIndex).toBe(0)` |
|
||||
|
||||
Exactly one production owner was temporarily changed per control. Every owner was restored byte for byte after each mutation and matched pinned Git bytes. Filtered controls select one keeper; unselected tests do not count as full-suite evidence. [Controls JSON](2026-10-12-test-audit-r7-ui-inline-controls.json) records exact owner/source hashes, migration/input proof, complete-file run/log digests, mutation bodies, intended assertion failures and restoration receipts. Temporary raw verifiers and logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad `pnpm test:run` was intentionally stopped without completion and is not claimed as passed. Separate packet results are focused proof, not a completed combined delivery-head replay or pending full CI. No push, rebase, PR or merge is included here.
|
||||
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/IssueChatThread.test.tsx",
|
||||
"base": "e3effbb9e5c66b838f67fd487987c8bf1007e413",
|
||||
"originalSha256": "226728e901d702ff59651f4b0d1a6fa33b34654a8410c58f2f42cebf7022113e",
|
||||
"candidateSha256": "4c8db9834c9bd6bd3ca77420d6dc5ec872903d35f6168dc177a5247372867593",
|
||||
"retired": "does not auto-scroll to the latest comment on initial load",
|
||||
"keeper": "can keep the page at the top on initial load while preserving manual jump-to-latest",
|
||||
"sameEnclosingHooks": true,
|
||||
"exactSubsetPrefixStatements": 20,
|
||||
"exactSubsetCleanupStatements": 4,
|
||||
"keeperBytesUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1,
|
||||
"netTestLinesRemoved": 50,
|
||||
"newHelpers": 0,
|
||||
"retainedExactInitialGuards": [
|
||||
"expect(elementScrollToMock).not.toHaveBeenCalled();",
|
||||
"expect(scrollIntoViewMock).not.toHaveBeenCalled();"
|
||||
]
|
||||
},
|
||||
"verifierSha256": "d5589d82c62e06412eae150b239b5552526428b764362485070ac3846a95789a",
|
||||
"mutationVerifierSha256": "97072371ef809b8bdefbf09dcacd261989132a4135d67fcf857152dd179d2f03",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 100,
|
||||
"passed": 100,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "efc81cf6ef5ca2bc0f495d185bb10ea905dcb003b084fc20ea29d76f122ed7e2",
|
||||
"logSha256": "4d1be86a78cc5a8868d9fc99e53b4ddaa1d94deac818cea48952b887ca3cbca0"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 99,
|
||||
"passed": 99,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "82badc1cc88a0e72db6dace6d9bae69d239d53eb9a187c9b286a6a6c4bfb75e5",
|
||||
"logSha256": "0eb343b1ddd666c246c4df358a8b9210cfe97f9ecef7e8abe756103b4e16a7a3"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "initial-auto-scroll",
|
||||
"owner": "ui/src/components/IssueChatThread.tsx",
|
||||
"sourceSha256": "23624e4e25b60c9ce8cc91fa548bfd46e18666723e5aa8d9f882846ded3bdf82",
|
||||
"title": "can keep the page at the top on initial load while preserving manual jump-to-latest",
|
||||
"mutation": {
|
||||
"before": " if (!autoScrollToLatestOnInitialLoad) return;",
|
||||
"after": " // Regression: ignore caller initial-scroll opt-out.",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": ".not.toHaveBeenCalled();",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "018d96099478a6400770da42da7a7af754e12a05c0ad3981b270fcd0d9317a7f",
|
||||
"logSha256": "34e23348df52acad5ab2e1c6374125ae9f746f21dd3ea6bc5d4155fceb71590d",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "5a6c5faccd5b873cf5ddf2ef18b14f12fc2b3d596c15c11ca606d84a76ecd1d6",
|
||||
"logSha256": "c87b37dcd855277f778930cf10f4ee89dd4c19627cebfd5d6452378c9ee44530",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to not be called at all, but actually been called 6 times\n\nReceived:\n\n 1st vi.fn() call:\n\n Array [\n Object {\n \"behavior\": \"smooth\",\n \"top\": 114916,\n },\n ]\n\n 2nd vi.fn() call:\n\n Array [\n Object {\n \"behavior\": \"auto\",\n \"top\": 114916,\n },\n ]\n\n 3rd vi.fn() call:\n\n Array [\n Object {\n \"behavior\": \"auto\",\n \"top\": 114916,\n },\n ]\n\n 4th vi.fn() call:\n\n Array [\n Object {\n \"behavior\": \"auto\",\n \"top\": 114916,\n },\n ]\n\n 5th vi.fn() call:\n\n Array [\n Object {\n \"behavior\": \"auto\",\n \"top\": 114916,\n },\n ]\n\n 6th vi.fn() call:\n\n Array [\n Object {\n \"behavior\": \"auto\",\n \"top\": 114916,\n },\n ]\n\n\nNumber of calls: 6\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2214:51)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/IssueChatThread.test.tsx:1217:37\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Test audit, Round 7 UI — Issue thread initial scroll follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 root renders and 50 net test lines** from `ui/src/components/IssueChatThread.test.tsx`. It adds no helper and changes no production lines. Audit base: `e3effbb9e5c66b838f67fd487987c8bf1007e413`. Prior committed evidence remains byte-identical. Render/execution savings are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Observations retained |
|
||||
|---|---|---|
|
||||
| `does not auto-scroll to the latest comment on initial load` | `can keep the page at the top on initial load while preserving manual jump-to-latest` | expect(elementScrollToMock).not.toHaveBeenCalled();; expect(scrollIntoViewMock).not.toHaveBeenCalled(); |
|
||||
|
||||
IssueChatThread.tsx keeps default initial viewport stable through the autoScrollToLatestOnInitialLoad opt-out, while an explicit Jump to latest action owns manual scrolling. The retired complete initial test is an exact AST subset of the keeper: all twenty setup/initial-judgment statements and all four cleanup statements match. Both use the exact same long-thread comments/runs/events/maps, fake timers, real scroll host, scrollTo mock, scrollIntoView prototype override and 500 ms timer advance. The keeper remains byte-identical, including both no-scroll guards before clicking. Hash routing, explicit auto-scroll and user-interruption cases remain byte-identical.
|
||||
|
||||
The AST verifier checks exact setup, shared enclosing hooks and matched cleanup where present, reconstructs candidate text solely from the declared registrations/assertion migrations, and requires exact byte equality plus full-program AST equality outside those edits. No fixture, helper, provider, action order, wait, assertion strength, timer or teardown changes. The IssueChatThread keeper is an existing complete subset proof; InlineEditor’s identical not-null guard is retained once. Every other case stays byte-identical. Default CI includes this file in `@paperclipai/ui` under `general-workspaces-a`, once across the two file shards; workflow/config unchanged.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 100 | 100 | 0 | 0 |
|
||||
| Candidate | 99 | 99 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/IssueChatThread.test.tsx`.
|
||||
|
||||
All 1 isolated positive controls passed. All 1 deliberate behavior regressions failed at their intended retained/migrated assertion, rather than setup, timeout or a different guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| initial-auto-scroll | `.not.toHaveBeenCalled();` |
|
||||
|
||||
Exactly one production owner was temporarily changed per control. Every owner was restored byte for byte after each mutation and matched pinned Git bytes. Filtered controls select one keeper; unselected tests do not count as full-suite evidence. [Controls JSON](2026-10-12-test-audit-r7-ui-issuechat-controls.json) records exact owner/source hashes, migration/input proof, complete-file run/log digests, mutation bodies, intended assertion failures and restoration receipts. Temporary raw verifiers and logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad `pnpm test:run` was intentionally stopped without completion and is not claimed as passed. Separate packet results are focused proof, not a completed combined delivery-head replay or pending full CI. No push, rebase, PR or merge is included here.
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/pages/IssueDetail.test.tsx",
|
||||
"base": "e3effbb9e5c66b838f67fd487987c8bf1007e413",
|
||||
"originalSha256": "3f70d91da4a6b272ee190b33fd17c3e09e04df3dd42d6ab4f2c9cb74e2bca0c4",
|
||||
"candidateSha256": "06b026c4bd296848011d1cc30f89711453a9db0c1e4c743fa22ae653b3e942d7",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1,
|
||||
"netTestLinesRemoved": 11,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "hides file viewer entry points by default",
|
||||
"keeper": "hides the plan decomposition panel by default",
|
||||
"exactSetupStatements": 4,
|
||||
"sameEnclosingHooks": true,
|
||||
"exactCleanupStatements": 0,
|
||||
"migratedGuards": [
|
||||
"expect(\n container.querySelector('[aria-label=\"Open file in this issue\"]'),\n ).toBeNull();"
|
||||
],
|
||||
"exactDuplicateGuardAlreadyKept": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"rootRendersRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "084bdbc359e510397ef64daeb24e259efbc62c8b5d2c56d3e09d7f2534f155aa",
|
||||
"mutationVerifierSha256": "ca8463047109d38a447d93fb94a19bb8efe9b1df440e0abf91ab31a8c0a358a2",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 141,
|
||||
"passed": 141,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "efc0588840631e46a0468684533c2a0df23ce43e24e9f165b69797ed4ba4f8f2",
|
||||
"logSha256": "c2d0323c3d9b9050231dcdd9ff68c0bf2796911ae563283d37d253b990973b87"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 140,
|
||||
"passed": 140,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "5710da0c420896d10b0e02a42310067ef3f56ae171831ea74c02943f4d38c9e4",
|
||||
"logSha256": "45b5a51b85ec8358643fd4b6bfe0851d77d1f97f6adf7c59fdec0dbed36d569f"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "file-viewer",
|
||||
"owner": "ui/src/pages/IssueDetail.tsx",
|
||||
"sourceSha256": "17b28ad871c2f00fa2d1153d50f151ebd0bed68c85d5a0f2a50164a2f1a612b2",
|
||||
"title": "hides the plan decomposition panel by default",
|
||||
"mutation": {
|
||||
"before": "instanceExperimentalSettings?.enableExperimentalFileViewer === true;",
|
||||
"after": "true;",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "container.querySelector('[aria-label=\"Open file in this issue\"]')",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "cfcf22e69da57c125f9622c1c692fbadf48181cf05804986c7f7d967f38295a0",
|
||||
"logSha256": "e8175ef85629eb1163704e686efcbb7c0e08ea9e0de852541945a0448d79223f",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b0e830d03ce630f19deae86869b4fa642ac41047da6d59fe4e363633d486e424",
|
||||
"logSha256": "b7a6464f68a800d0218d044694c0f8405e993f18ee831634a9c22b4b00204c86",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected <button \u2026(3)>\u2026(1)</button> to be null\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/IssueDetail.test.tsx:4195:7\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Test audit, Round 7 UI — Issue detail default feature gates follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 root renders and 11 net test lines** from `ui/src/pages/IssueDetail.test.tsx`. It adds no helper and changes no production lines. Audit base: `e3effbb9e5c66b838f67fd487987c8bf1007e413`. Prior committed evidence remains byte-identical. Render/execution savings are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Observations retained |
|
||||
|---|---|---|
|
||||
| `hides file viewer entry points by default` | `hides the plan decomposition panel by default` | expect( container.querySelector('[aria-label="Open file in this issue"]'), ).toBeNull(); |
|
||||
|
||||
IssueDetail.tsx derives fileViewerEnabled and showPlanDecompositionsSection independently from instance settings. The identical default issue/render/two-flush setup already proves the plan panel absent and no listAcceptedPlanDecompositions API request. It now also retains the exact file-entry-point absence guard. The actual IssuePlanDecompositionsSection owns its list API request only when mounted. Enabled file viewer, enabled plan decomposition, routing/hash and interactive viewer states remain distinct and unchanged.
|
||||
|
||||
The AST verifier checks exact setup, shared enclosing hooks and matched cleanup where present, reconstructs candidate text solely from the declared registrations/assertion migrations, and requires exact byte equality plus full-program AST equality outside those edits. No fixture, helper, provider, action order, wait, assertion strength, timer or teardown changes. The IssueChatThread keeper is an existing complete subset proof; InlineEditor’s identical not-null guard is retained once. Every other case stays byte-identical. Default CI includes this file in `@paperclipai/ui` under `general-workspaces-a`, once across the two file shards; workflow/config unchanged.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 141 | 141 | 0 | 0 |
|
||||
| Candidate | 140 | 140 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/pages/IssueDetail.test.tsx`.
|
||||
|
||||
All 1 isolated positive controls passed. All 1 deliberate behavior regressions failed at their intended retained/migrated assertion, rather than setup, timeout or a different guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| file-viewer | `container.querySelector('[aria-label="Open file in this issue"]')` |
|
||||
|
||||
Exactly one production owner was temporarily changed per control. Every owner was restored byte for byte after each mutation and matched pinned Git bytes. Filtered controls select one keeper; unselected tests do not count as full-suite evidence. [Controls JSON](2026-10-12-test-audit-r7-ui-issuedetail-controls.json) records exact owner/source hashes, migration/input proof, complete-file run/log digests, mutation bodies, intended assertion failures and restoration receipts. Temporary raw verifiers and logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad `pnpm test:run` was intentionally stopped without completion and is not claimed as passed. Separate packet results are focused proof, not a completed combined delivery-head replay or pending full CI. No push, rebase, PR or merge is included here.
|
||||
@@ -0,0 +1,549 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/OnboardingWizard.test.tsx",
|
||||
"base": "2e76f5157240ee8b2d42adbdcd4c345aea07c013",
|
||||
"originalSha256": "6215ee2f034686deaf2d6091590030dcdeccff1697422574e45e0351eaf5a7fe",
|
||||
"candidateSha256": "a2be5c51e61cffe1e6a01e281e073b766d7a2c18a2eb9795cfa0810d0cf9531d",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 5,
|
||||
"renderInvocationsRemoved": 5,
|
||||
"setupInvocationsRemoved": 5,
|
||||
"netTestLinesRemoved": 56,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "carries no token value in the hire payload",
|
||||
"keeper": "sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists",
|
||||
"exactLeadingStatements": 4,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": true,
|
||||
"kind": "privacy",
|
||||
"exactMigratedStatements": [
|
||||
"const binding = hireBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY] as\n | { type: string }\n | undefined;",
|
||||
"expect(binding?.type).toBe(\"user_secret_ref\");",
|
||||
"expect(JSON.stringify(hireBody)).not.toContain(\"secretId\");"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [
|
||||
"expect(mockAgentsApi.hire).toHaveBeenCalled();",
|
||||
"const hireArgs = mockAgentsApi.hire.mock.calls.at(-1) as unknown[];",
|
||||
"const hireBody = hireArgs[1] as { adapterConfig: { env?: Record<string, unknown> } };"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "sends the fixed binding in the environment test request when the status route returns 200",
|
||||
"keeper": "sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists",
|
||||
"exactLeadingStatements": 4,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": true,
|
||||
"kind": "probe",
|
||||
"exactMigratedStatements": [
|
||||
"expect(mockAgentsApi.testEnvironment).toHaveBeenCalled();",
|
||||
"const testArgs = mockAgentsApi.testEnvironment.mock.calls.at(-1) as unknown[];",
|
||||
"const testBody = testArgs[2] as { adapterConfig: { env?: Record<string, unknown> } };",
|
||||
"expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toEqual({\n type: \"user_secret_ref\",\n key: CLAUDE_OAUTH_TOKEN_ENV_KEY,\n version: \"latest\",\n required: true,\n });"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "sends no binding in the environment test request when the status route returns 404",
|
||||
"keeper": "sends no binding and no flag when the Claude status route returns 404",
|
||||
"exactLeadingStatements": 2,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": true,
|
||||
"kind": "probe",
|
||||
"exactMigratedStatements": [
|
||||
"expect(mockAgentsApi.testEnvironment).toHaveBeenCalled();",
|
||||
"const testArgs = mockAgentsApi.testEnvironment.mock.calls.at(-1) as unknown[];",
|
||||
"const testBody = testArgs[2] as { adapterConfig: { env?: Record<string, unknown> } };",
|
||||
"expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toBeUndefined();"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "creates the organization on Continue and lands on the agent step, no mission",
|
||||
"keeper": "shows no environment-check card on the model step, and no Mission row on review",
|
||||
"exactLeadingStatements": 3,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": true,
|
||||
"kind": "company",
|
||||
"exactMigratedStatements": [
|
||||
"expect(mockCompaniesApi.create).toHaveBeenCalledWith({ name: \"Initech\" });",
|
||||
"expect(document.body.textContent).not.toContain(\"Define your mission\");",
|
||||
"expect(document.body.textContent).not.toContain(\"Tell us about your team\");"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [
|
||||
"expect(document.body.textContent).toContain(\"Create your first agent\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "renders no 'Use saved login' control",
|
||||
"keeper": "starts the other source's login after backing out of the first",
|
||||
"exactLeadingStatements": 3,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": true,
|
||||
"kind": "normal",
|
||||
"exactMigratedStatements": [
|
||||
"expect(document.body.textContent).toContain(\"Sign in to Claude then come back and enter authorization code\");",
|
||||
"expect(document.body.textContent).not.toContain(\"Use saved login\");"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "ff07f5e2e308cfc0f26584a6cd6599bcd98665e79d7f4782ed5bb36b35cb8cb9",
|
||||
"mutationVerifierSha256": "676b973d4838ed130c5ae92d4bc50fb32a65309af8c858450ec34bcce13729e9",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 93,
|
||||
"passed": 93,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "647b2b743bba5c5fe87cf8b19b413bfa811867cb19cd2d0afd0e0e64f4989603",
|
||||
"logSha256": "b83f2bc12966793c3f000e579f0f938011b715572bc5caa2f23c144cfa23e7c5"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 88,
|
||||
"passed": 88,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "3d708b8681916868d4e03d569e89038082e25c899e0f95935d9ea1903a364ce9",
|
||||
"logSha256": "e015a19efabf22acd715d06ac5dda923a4f5bf11926089fe762db4e20e39f561"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "probe-present-200",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists",
|
||||
"mutation": {
|
||||
"before": "const result = await agentsApi.testEnvironment(\n createdCompanyId,\n adapterType,\n {\n adapterConfig: adapterConfigOverride ?? buildAdapterConfig(),\n ...(managedBindingForStep() ? { aiConnection: managedBindingForStep() } : {}),\n environmentId,\n }\n );",
|
||||
"after": "const result = { adapterType, status: \"pass\" as const, checks: [], testedAt: new Date(0).toISOString() };",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.testEnvironment).toHaveBeenCalled()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "31743b16d52bc74738877f45c4d52400bd9fc9e105fc59b7ec4804906969ec98",
|
||||
"logSha256": "d80825ed43906d24b6d325af1009b5d538e0cd9d8b52a5760c996f7cc7e1b34c",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "384a0396090744b528b532ad60ceece8882cc84eb1c554e1e59b0603942323dd",
|
||||
"logSha256": "9f2c7c7ba8a4331469c7b7bd262c2657daabe0dfddd5a106882d9ed08600d8a1",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called at least once\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2214:51)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:1044:45\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "probe-binding-200",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists",
|
||||
"mutation": {
|
||||
"before": "adapterConfig: adapterConfigOverride ?? buildAdapterConfig(),",
|
||||
"after": "adapterConfig: { ...(adapterConfigOverride ?? buildAdapterConfig()), env: {} },",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toEqual(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "81c27fbb8cba0dcf5b7bdf2ce2c97aab35616ff5e6f1a969aaf30e22c2593e11",
|
||||
"logSha256": "ca107f788a76c3d6646d58e82f4a9c6e2df72bb74e82de2597298c2502e33bff",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "46182474cf2dbeaf39f7bcad775b9b45aa0d5522998d11489c864642878d5ba5",
|
||||
"logSha256": "7ffcbe62fc7de0402029a8fc92840b7c5fc17ed5d4a72d8ceab5235967c47416",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected undefined to deeply equal { type: 'user_secret_ref', \u2026(3) }\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:1047:72\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "hire-reference-kind",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists",
|
||||
"mutation": {
|
||||
"before": "adapterConfig: hireAdapterConfig,",
|
||||
"after": "adapterConfig: { ...hireAdapterConfig, env: { ...(hireAdapterConfig.env ?? {}), [\"CLAUDE_CODE_OAUTH_TOKEN\"]: { type: \"plain\", value: \"wrong-kind\" } } },",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(binding?.type).toBe(\"user_secret_ref\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "7b4f481d6c0f365bf00f5713ff2203cff0642575c11ccb3911b9707ea8f54ef5",
|
||||
"logSha256": "628ee5cd5995a7b11df381f985db1757fca8ef85710603e06fd259cfb8515c0a",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "d231ee320ece943d41cefd9b1d01eb1d8b136c14be04b93e8b0e135e6eb7e294",
|
||||
"logSha256": "4b97d9537eabe304eadc0b82144acfaba7bc1088edc74ab915170cc6f23f907b",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'plain' to be 'user_secret_ref' // Object.is equality\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:1064:29\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "hire-private-id",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists",
|
||||
"mutation": {
|
||||
"before": "adapterConfig: hireAdapterConfig,",
|
||||
"after": "adapterConfig: hireAdapterConfig,\n secretId: \"leaked-reference-id\",",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(JSON.stringify(hireBody)).not.toContain(\"secretId\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "c31e4abc66b53c0e2a0a1d670e8a5c09b4e5737e118299a7d7b62eec720fa6d2",
|
||||
"logSha256": "5c3083595d2bf2f06f52668806ecf9e412059f1da5186ecb932532adbde2c655",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "b98037a4460a3e20efa63527204996fbbc8ecdb67d921af4ac32c04cca22d58a",
|
||||
"logSha256": "29bed62b4bf4fb4d9a9aa9309fc5ebcddc8c73edd704933df4963dd401073262",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected '{\"name\":\"Ada\",\"appearance\":{\"schemaVe\u2026' not to contain 'secretId'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:1065:44\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "probe-present-404",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "sends no binding and no flag when the Claude status route returns 404",
|
||||
"mutation": {
|
||||
"before": "const result = await agentsApi.testEnvironment(\n createdCompanyId,\n adapterType,\n {\n adapterConfig: adapterConfigOverride ?? buildAdapterConfig(),\n ...(managedBindingForStep() ? { aiConnection: managedBindingForStep() } : {}),\n environmentId,\n }\n );",
|
||||
"after": "const result = { adapterType, status: \"pass\" as const, checks: [], testedAt: new Date(0).toISOString() };",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockAgentsApi.testEnvironment).toHaveBeenCalled()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "4d38c478ca455f244954d02107699d6af5614dfb8c80f553da1caf15f0dafd5a",
|
||||
"logSha256": "0d6a237b79947cd1b028a87b34e2cc4d28755194e54ee73fffa6cbb99a530488",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "68f321ac5807be15bc85e58e1adeab127485cbb17d62e540ec0ff415ff8c5d60",
|
||||
"logSha256": "09d6f75954ccc52b7fefb1747f3d5d54a70f952c509e5971959de4b96e47515b",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called at least once\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2214:51)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:1085:45\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "probe-binding-404",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "sends no binding and no flag when the Claude status route returns 404",
|
||||
"mutation": {
|
||||
"before": "adapterConfig: adapterConfigOverride ?? buildAdapterConfig(),",
|
||||
"after": "adapterConfig: { ...(adapterConfigOverride ?? buildAdapterConfig()), env: { [\"CLAUDE_CODE_OAUTH_TOKEN\"]: { type: \"user_secret_ref\", key: \"CLAUDE_CODE_OAUTH_TOKEN\", version: \"latest\", required: true } } },",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toBeUndefined()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "3f40b447122e687985096e3af6f8e2a5a8cad624904d034874ace9a7925fbe09",
|
||||
"logSha256": "84548520fda7a059de5f0d4b835b3df59477c69ccc199b2f9eaa10f35c2d5402",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "2223afa8cb0fef009b6b58ef34d5a0d4cb587efb1842f151c362b5d43cfc3086",
|
||||
"logSha256": "4966efcc558d90d72e14d397901b306565dede76b0b908edbbbf223c48d9ac1d",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected { type: 'user_secret_ref', \u2026(3) } to be undefined\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:1088:72\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "create-company-name",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "shows no environment-check card on the model step, and no Mission row on review",
|
||||
"mutation": {
|
||||
"before": "companiesApi.create({ name: companyName.trim() })",
|
||||
"after": "companiesApi.create({ name: \"Wrong company\" })",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(mockCompaniesApi.create).toHaveBeenCalledWith({ name: \"Initech\" })",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "4bd6223a943a9f88351a1163a68267c20a57907c0f685a804f42134df7d418b9",
|
||||
"logSha256": "d6e0fc1cd39e843cef3ab559a562dfda70ea826456904fcb4199b41d22616682",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "43cb71ec93702eefdb0c1332d80e25d163629a5e27a1f67a8c4d2990013f01e0",
|
||||
"logSha256": "07b461cd0a99e77e8f3e8c668613a67eb3f19ab622628cfa6b6058fb8e1dbb31",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ { name: 'Initech' } ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n {\n- \"name\": \"Initech\",\n+ \"name\": \"Wrong company\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:462:39\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "initial-mission",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "shows no environment-check card on the model step, and no Mission row on review",
|
||||
"mutation": {
|
||||
"before": "\"Create your first agent\"",
|
||||
"after": "\"Create your first agent \u2014 Define your mission\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(document.body.textContent).not.toContain(\"Define your mission\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "59d05408e1ca40f733f09b3a0a4f3006c6717056a54e08f1e5da4543aa6b5446",
|
||||
"logSha256": "593772fcb849cf4afb7bbfc4a38164983b22229c9069663d65f059e7e60e43c7",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "cfd65b2c01b74f4e82db621b0987dbb7894ff6b7a9159f7ab4d2964bb0ee987b",
|
||||
"logSha256": "ba59a142a703fbcfb915dea3365a1e72283c9b7acdb28b7f486ee2949411ad04",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Step 2 of 4 Create your first agent \u2026' not to contain 'Define your mission'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:463:45\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "initial-team",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "shows no environment-check card on the model step, and no Mission row on review",
|
||||
"mutation": {
|
||||
"before": "\"Create your first agent\"",
|
||||
"after": "\"Create your first agent \u2014 Tell us about your team\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(document.body.textContent).not.toContain(\"Tell us about your team\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "a5a285c51d9f2d943310b9434bdcaa81a12d3c07384c86ae6b509f176359c359",
|
||||
"logSha256": "d37342c66155290e327442b45115f0174acce8c5f6ab05844f75c6382480b0fb",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "e80732591e8829b5712e5714a1195b44c95d00f8d211f16b37360892dde3f66e",
|
||||
"logSha256": "7ebb5382e5d3701c63233f2c9ffe01bb7a0add3adac6b160ed70b3cbd185f9cd",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Step 2 of 4 Create your first agent \u2026' not to contain 'Tell us about your team'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:464:45\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "auth-instruction",
|
||||
"owner": "ui/src/components/AdapterLoginChrome.tsx",
|
||||
"sourceSha256": "7fe56ca349b17410287074f89a42373b8d46280adb580a57067253ba45f00499",
|
||||
"title": "starts the other source's login after backing out of the first",
|
||||
"mutation": {
|
||||
"before": "? \" then come back and enter authorization code\"",
|
||||
"after": "? \" then return\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(document.body.textContent).toContain(\"Sign in to Claude then come back and enter authorization code\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "4154830e1db482e7b7be3bc29a90a305886135615e4931b4cc3ab94131a782c3",
|
||||
"logSha256": "425b312c4684680caf805e055b467180d71771928739fb2fb0bc884c5816db90",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "159827cd6b9ff90b04bd8fad88d06d70b981a49439d21d766a581845a3bc0905",
|
||||
"logSha256": "36c516fa3a6507b036edd82a58808b69ec8a0ec6bd5870d0103908edcb173473",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Step 2 of 3Ada Connect a modelPapercl\u2026' to contain 'Sign in to Claude then come back and \u2026'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:2839:41\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "saved-login",
|
||||
"owner": "ui/src/components/AdapterLoginChrome.tsx",
|
||||
"sourceSha256": "7fe56ca349b17410287074f89a42373b8d46280adb580a57067253ba45f00499",
|
||||
"title": "starts the other source's login after backing out of the first",
|
||||
"mutation": {
|
||||
"before": ": \" by providing the authorization code below\"}",
|
||||
"after": ": \" by providing the authorization code below\"}\n <button>Use saved login</button>",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(document.body.textContent).not.toContain(\"Use saved login\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "133ee3765d37cd8af631b422c60109dddbe698d3e6b699793552af48bcd4b8ba",
|
||||
"logSha256": "d692064dd8957fe9868fb507be75364c6db3412f727978f894cee91e3a1ba672",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "122fed92c9caa458fa52e0a4b84524f8bcb64545e0578597430f645b47adec69",
|
||||
"logSha256": "717f13481ecc00acbf9531e1e9a3c8c04c46d04f148f97b918d9137854a6593e",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Step 2 of 3Ada Connect a modelPapercl\u2026' not to contain 'Use saved login'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.test.tsx:2840:45\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig-controls.json",
|
||||
"sha256": "cfb4357610beaa464cdb26848f74a7b8d967d6409c63dbd6821c92886a9cf980"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig.md",
|
||||
"sha256": "ea6d5dfc8cedcd653edd55ca9d77568431413a351f4de5db77ca6ac8f22b051a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental-controls.json",
|
||||
"sha256": "9c72abee7ee1a937fdbd6ccee870320308bc7ce06b4fc1e338cd9bd3b0efba6b"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental.md",
|
||||
"sha256": "93b7583a8ebba39dc97e390918fd62029955a00beda22756f55bd6374acc3737"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline-controls.json",
|
||||
"sha256": "af9d3f115e6e0518c2c0a2e11b8913e7aa82a35c848f114e6750884323601e17"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline.md",
|
||||
"sha256": "52d8e27970bd1c091ae7edf403b2331e921081da9198270df65ff779a8efc1bc"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat-controls.json",
|
||||
"sha256": "45d86fa901c8ab40bc3fe98d266e3ba1a7bbc93edb7bc2aae60ccd9bfa130c37"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat.md",
|
||||
"sha256": "15e84a553c5a468aea013941f68d3aa517bfb5a13ae296a90e1a39586f85de8e"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail-controls.json",
|
||||
"sha256": "a56f183f1c6b940662db0c4bfb62457eda0cec453c2019b439ec716ed9d2a3e1"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail.md",
|
||||
"sha256": "e137af5b991c936a167d6ad6f3c710b99688e939f839b163b203259fc998ded8"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/OnboardingWizard.step.test.tsx",
|
||||
"base": "2e76f5157240ee8b2d42adbdcd4c345aea07c013",
|
||||
"originalSha256": "05f29524e705110bc020285744b4f1be3047ea8ed13eb1e5434013df54762019",
|
||||
"candidateSha256": "6f38d97fc25a6ad573477e3ec00a6509eeb1993c6af0812429b7861091d3ba88",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1,
|
||||
"netTestLinesRemoved": 9,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "opens on the agent step when the goals lookup fails outright",
|
||||
"keeper": "does not move an open wizard when a later refetch finds a mission",
|
||||
"exactLeadingStatements": 5,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": false,
|
||||
"kind": "subset",
|
||||
"exactMigratedStatements": [],
|
||||
"alreadyExactKeeperStatementsOrBindings": [
|
||||
"routerState.pathname = \"/PC1/onboarding\";",
|
||||
"mockGoalsApi.list.mockRejectedValue(new Error(\"goals unavailable\"));",
|
||||
"await render();",
|
||||
"await settle();",
|
||||
"expect(currentStep()).toBe(\"agent\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "ff07f5e2e308cfc0f26584a6cd6599bcd98665e79d7f4782ed5bb36b35cb8cb9",
|
||||
"mutationVerifierSha256": "676b973d4838ed130c5ae92d4bc50fb32a65309af8c858450ec34bcce13729e9",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 14,
|
||||
"passed": 14,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "97d067fe55e610e4c0d63f7ba0d7eab05ebe7d1a303248a616d01dd04f95015e",
|
||||
"logSha256": "6e010087718c28d08d3f4a426280526bc8f760ff28984d5205eb18fa41d5b752"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 13,
|
||||
"passed": 13,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "28eb77c49492b068be69a761fac2139788e49e78032473692dc37fbfbc0d56cd",
|
||||
"logSha256": "fb8dcd4aafa59b414e1394dca9682a3f50c70bf567bb26086498d4b4fca9eb60"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "initial-agent-step",
|
||||
"owner": "ui/src/components/OnboardingWizard.tsx",
|
||||
"sourceSha256": "22a2c8bfbe0aa02ea1ab13a95fad511760f4c2cf69c2e875af4f7c4bbd3c4861",
|
||||
"title": "does not move an open wizard when a later refetch finds a mission",
|
||||
"mutation": {
|
||||
"before": "setStep(initialStepRef.current);",
|
||||
"after": "setStep(4);",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(currentStep()).toBe(\"agent\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "fb177a7674f0ad536aee811dbf0dbf02e7d3cb4db65ad80e274901b3db53613d",
|
||||
"logSha256": "1afc3859f49727677ef5f0edfd5e77d15110136b154fe1c93b3bc2196b569daf",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "867f486c5b06f88c22e43d49aca66bd535b7c2d7b5e7b66c2e67db8b9a9b18c3",
|
||||
"logSha256": "cbc50ca1569ffab283e25d0106fcdbd4540d4006a6ba872aef20aefc5339972e",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'other' to be 'agent' // Object.is equality\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/OnboardingWizard.step.test.tsx:305:27\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig-controls.json",
|
||||
"sha256": "cfb4357610beaa464cdb26848f74a7b8d967d6409c63dbd6821c92886a9cf980"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig.md",
|
||||
"sha256": "ea6d5dfc8cedcd653edd55ca9d77568431413a351f4de5db77ca6ac8f22b051a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental-controls.json",
|
||||
"sha256": "9c72abee7ee1a937fdbd6ccee870320308bc7ce06b4fc1e338cd9bd3b0efba6b"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental.md",
|
||||
"sha256": "93b7583a8ebba39dc97e390918fd62029955a00beda22756f55bd6374acc3737"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline-controls.json",
|
||||
"sha256": "af9d3f115e6e0518c2c0a2e11b8913e7aa82a35c848f114e6750884323601e17"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline.md",
|
||||
"sha256": "52d8e27970bd1c091ae7edf403b2331e921081da9198270df65ff779a8efc1bc"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat-controls.json",
|
||||
"sha256": "45d86fa901c8ab40bc3fe98d266e3ba1a7bbc93edb7bc2aae60ccd9bfa130c37"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat.md",
|
||||
"sha256": "15e84a553c5a468aea013941f68d3aa517bfb5a13ae296a90e1a39586f85de8e"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail-controls.json",
|
||||
"sha256": "a56f183f1c6b940662db0c4bfb62457eda0cec453c2019b439ec716ed9d2a3e1"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail.md",
|
||||
"sha256": "e137af5b991c936a167d6ad6f3c710b99688e939f839b163b203259fc998ded8"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Test audit, Round 7 UI — Onboarding initial step follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 rendered scenarios and 9 net test lines** from `ui/src/components/OnboardingWizard.step.test.tsx`. It adds no helper and changes no production lines. Audit base: `2e76f5157240ee8b2d42adbdcd4c345aea07c013`. All prior committed proof remains byte-identical. Registration/render savings are per complete-suite invocation; no measured runtime or CI-speed claim is made. A service-worker retirement counts only initializer setup, never a render.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Exact observations migrated |
|
||||
|---|---|---|
|
||||
| `opens on the agent step when the goals lookup fails outright` | `does not move an open wizard when a later refetch finds a mission` | |
|
||||
|
||||
The retired complete five-statement body is an exact leading AST subset of the keeper, including the rejected goals mock, real route render, settle and agent-step assertion. The keeper is unchanged and then checks a later mission-cache update. Wizard opening/step effects and the real route resolver remain under test; explicit step, company change, reopen and in-flow behavior stay distinct. Existing global teardown remains unchanged.
|
||||
|
||||
The verifier requires matching setup statements, enclosing hooks and explicit teardown where present. It reconstructs candidate source solely from the declared registrations/assertion migrations and requires exact byte equality plus full-program AST equality outside those edits. Existing shared variable bindings, mock arguments and provider/component inputs remain the same. No other fixture, helper, action order, wait, assertion strength, timer or cleanup changes. Default CI includes the file in `@paperclipai/ui` under `general-workspaces-a`, once across its two file shards, with no workflow/config change.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 14 | 14 | 0 | 0 |
|
||||
| Candidate | 13 | 13 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/OnboardingWizard.step.test.tsx`.
|
||||
|
||||
All 1 separately selected positive controls passed. All 1 distinct production-owner regressions failed at intended retained/migrated assertions, not setup, timeout or another guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| initial-agent-step | `expect(currentStep()).toBe("agent")` |
|
||||
|
||||
Exactly one production owner changed per control, and every owner was restored byte for byte after each mutation and checked against pinned Git bytes. Literal keeper-name filters are escaped; unselected cases are not full-suite evidence. The initial first-install receipt verifier mistook emit( for it( and rejected its receipt despite the intended test failure. The declaration-boundary matcher was corrected and that control rerun; the rejected verifier attempt supplies no accepted evidence.
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-onboarding-step-controls.json) preserves source/owner hashes, exact before/after migrations, matched setup and variable-binding proof, complete-file run/log digests, intended assertion failures and restoration checks. Temporary raw scripts/logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion and is not claimed as passed. These separate focused packets precede delivery-head replay and full CI. This packet includes no push, rebase, PR or merge.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Test audit, Round 7 UI — Onboarding walkthroughs follow-up
|
||||
|
||||
This separate packet removes **5 registered executions, 5 rendered scenarios and 56 net test lines** from `ui/src/components/OnboardingWizard.test.tsx`. It adds no helper and changes no production lines. Audit base: `2e76f5157240ee8b2d42adbdcd4c345aea07c013`. All prior committed proof remains byte-identical. Registration/render savings are per complete-suite invocation; no measured runtime or CI-speed claim is made. A service-worker retirement counts only initializer setup, never a render.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Exact observations migrated |
|
||||
|---|---|---|
|
||||
| `carries no token value in the hire payload` | `sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists` | const binding = hireBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY] as \| { type: string } \| undefined;; expect(binding?.type).toBe("user_secret_ref");; expect(JSON.stringify(hireBody)).not.toContain("secretId"); |
|
||||
| `sends the fixed binding in the environment test request when the status route returns 200` | `sends the fixed Claude binding and applyStoredClaudeLogin when a stored login exists` | expect(mockAgentsApi.testEnvironment).toHaveBeenCalled();; const testArgs = mockAgentsApi.testEnvironment.mock.calls.at(-1) as unknown[];; const testBody = testArgs[2] as { adapterConfig: { env?: Record<string, unknown> } };; expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toEqual({ type: "user_secret_ref", key: CLAUDE_OAUTH_TOKEN_ENV_KEY, version: "latest", required: true, }); |
|
||||
| `sends no binding in the environment test request when the status route returns 404` | `sends no binding and no flag when the Claude status route returns 404` | expect(mockAgentsApi.testEnvironment).toHaveBeenCalled();; const testArgs = mockAgentsApi.testEnvironment.mock.calls.at(-1) as unknown[];; const testBody = testArgs[2] as { adapterConfig: { env?: Record<string, unknown> } };; expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toBeUndefined(); |
|
||||
| `creates the organization on Continue and lands on the agent step, no mission` | `shows no environment-check card on the model step, and no Mission row on review` | expect(mockCompaniesApi.create).toHaveBeenCalledWith({ name: "Initech" });; expect(document.body.textContent).not.toContain("Define your mission");; expect(document.body.textContent).not.toContain("Tell us about your team"); |
|
||||
| `renders no 'Use saved login' control` | `starts the other source's login after backing out of the first` | expect(document.body.textContent).toContain("Sign in to Claude then come back and enter authorization code");; expect(document.body.textContent).not.toContain("Use saved login"); |
|
||||
|
||||
Five registrations consolidate within identical enclosing hooks and setup expressions. The stored-login 200 keeper receives the complete probe-called, captured probe payload and full fixed-binding equality before its original hire observations. The original hire-body binding is reused through the same captured mock-call expression; its broader type annotation and applyStoredClaudeLogin checks stay unchanged. The retired explicit reference-kind check and complete hire-payload secretId exclusion are retained before the original stronger binding equality. The 404 keeper separately receives its probe-called/captured-body/undefined-binding guards. The organization-create keeper carries exact requested name and both initial mission/team exclusions before advancing. The source-switch keeper carries the Claude instruction anchor and no-legacy-saved-login guard before Back. The real wizard creates companies, probes adapter environments and submits hires through distinct local call sites; its onboarding login UI uses AdapterLoginChrome. No status, provider, stored-login, cancellation or navigation paths are combined. An initial hire-kind mutation referenced an identifier not bound in the wizard and failed before the API payload assertion; it was rejected and restored, then rerun using the actual literal CLAUDE_CODE_OAUTH_TOKEN key. Every other case remains byte-identical.
|
||||
|
||||
The verifier requires matching setup statements, enclosing hooks and explicit teardown where present. It reconstructs candidate source solely from the declared registrations/assertion migrations and requires exact byte equality plus full-program AST equality outside those edits. Existing shared variable bindings, mock arguments and provider/component inputs remain the same. No other fixture, helper, action order, wait, assertion strength, timer or cleanup changes. Default CI includes the file in `@paperclipai/ui` under `general-workspaces-a`, once across its two file shards, with no workflow/config change.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 93 | 93 | 0 | 0 |
|
||||
| Candidate | 88 | 88 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/OnboardingWizard.test.tsx`.
|
||||
|
||||
All 11 separately selected positive controls passed. All 11 distinct production-owner regressions failed at intended retained/migrated assertions, not setup, timeout or another guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| probe-present-200 | `expect(mockAgentsApi.testEnvironment).toHaveBeenCalled()` |
|
||||
| probe-binding-200 | `expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toEqual(` |
|
||||
| hire-reference-kind | `expect(binding?.type).toBe("user_secret_ref")` |
|
||||
| hire-private-id | `expect(JSON.stringify(hireBody)).not.toContain("secretId")` |
|
||||
| probe-present-404 | `expect(mockAgentsApi.testEnvironment).toHaveBeenCalled()` |
|
||||
| probe-binding-404 | `expect(testBody.adapterConfig.env?.[CLAUDE_OAUTH_TOKEN_ENV_KEY]).toBeUndefined()` |
|
||||
| create-company-name | `expect(mockCompaniesApi.create).toHaveBeenCalledWith({ name: "Initech" })` |
|
||||
| initial-mission | `expect(document.body.textContent).not.toContain("Define your mission")` |
|
||||
| initial-team | `expect(document.body.textContent).not.toContain("Tell us about your team")` |
|
||||
| auth-instruction | `expect(document.body.textContent).toContain("Sign in to Claude then come back and enter authorization code")` |
|
||||
| saved-login | `expect(document.body.textContent).not.toContain("Use saved login")` |
|
||||
|
||||
Exactly one production owner changed per control, and every owner was restored byte for byte after each mutation and checked against pinned Git bytes. Literal keeper-name filters are escaped; unselected cases are not full-suite evidence. The initial first-install receipt verifier mistook emit( for it( and rejected its receipt despite the intended test failure. The declaration-boundary matcher was corrected and that control rerun; the rejected verifier attempt supplies no accepted evidence.
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-onboarding-controls.json) preserves source/owner hashes, exact before/after migrations, matched setup and variable-binding proof, complete-file run/log digests, intended assertion failures and restoration checks. Temporary raw scripts/logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion and is not claimed as passed. These separate focused packets precede delivery-head replay and full CI. This packet includes no push, rebase, PR or merge.
|
||||
@@ -0,0 +1,303 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/RouteErrorBoundary.test.tsx",
|
||||
"base": "c39262305b6b48a5decd564af8369083cd972b33",
|
||||
"originalSha256": "aeca8c1302ed36fa9885d60b684b48fa67adc3f1a062e7b962be6a076bd93f57",
|
||||
"candidateSha256": "d4af25551dd6e7b20ffbd34500bf41603f6797187836ca7f554b898594266760",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"additionalDeclaredCleanup": "Remove duplicate original title assertion; identical migrated title guard remains before route change",
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 2,
|
||||
"rootRendersRemoved": 2,
|
||||
"netTestLinesRemoved": 31,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "reports one captured error and keeps the recovery button",
|
||||
"keeper": "recovers and renders children again after the route changes",
|
||||
"setupAstEqual": true,
|
||||
"setupBindingNormalization": null,
|
||||
"guards": [
|
||||
"expect(captureBrowserExceptionMock).toHaveBeenCalledTimes(1);",
|
||||
"expect(captureBrowserExceptionMock).toHaveBeenCalledWith(expect.any(Error), {\n boundary: \"route\", componentStack: expect.stringContaining(\"Boom\"),\n });",
|
||||
"expect(\n Array.from(container.querySelectorAll(\"button\")).some(\n (button) => button.textContent === \"Go back\",\n ),\n ).toBe(true);"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "renders a recoverable error card instead of a blank page when a child throws",
|
||||
"keeper": "recovers and renders children again after the route changes",
|
||||
"setupAstEqual": true,
|
||||
"setupBindingNormalization": null,
|
||||
"guards": [
|
||||
"expect(container.textContent).toContain(\"This page hit an error\");",
|
||||
"expect(container.textContent).toContain(\"Maximum update depth exceeded\");",
|
||||
"expect(container.querySelector(\"pre\")).not.toBeNull();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "0f1b173c8a399bb8c3e831edac6805634304a749faffcfff7b8b1900da3d34a3",
|
||||
"mutationVerifierSha256": "a05f5ba9071c0ce7ce8eac1bc0f6c7dd83401c4e720816389d779f8919312064",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "3782d05a0257e11e6618261f4f64121e86f253077e44c3191a3cf26b0f79cf24",
|
||||
"logSha256": "dd5e7a343956d6f41c5b87a19073a0377a0a1e45ada33495ccf0eb25b1e926bb",
|
||||
"total": 4,
|
||||
"passed": 4,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "d6f68de33e64d9b11586f8fd885bacffe13659d1bd03d85e4b9e8fcd3e41e07e",
|
||||
"logSha256": "c39520330407b89424c964598c0580454bbb24d5fb1568570547b027be8ba082",
|
||||
"total": 2,
|
||||
"passed": 2,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "capture-count",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": " captureBrowserException(error, { boundary: \"route\", componentStack: info.componentStack });",
|
||||
"after": " captureBrowserException(error, { boundary: \"route\", componentStack: info.componentStack });\n captureBrowserException(error, { boundary: \"route\", componentStack: info.componentStack });",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(captureBrowserExceptionMock).toHaveBeenCalledTimes(1)",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "8498654db6ea2733f761cef25c488c06ae35a9fdb32341915e5614beb9ee7077",
|
||||
"logSha256": "4040e9d53955f1d919fd7932dc8a411cde138a58cb27b07a02a9cd286cbaf504",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "23a988b53557b6aa392f8f110fa672f9c34faf8480776c49a66dc4a642fc1143",
|
||||
"logSha256": "abc91cb18b2edd6a6524b4fb006a50b36b76c40d6d5960480b18d283e958648c",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called 1 times, but got 2 times\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:80:41\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "capture-boundary",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": "boundary: \"route\", componentStack: info.componentStack",
|
||||
"after": "boundary: \"app\", componentStack: info.componentStack",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(captureBrowserExceptionMock).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "aca0b48e03ef4f1562c8f41ab3bd1a036ae095e80b5abe9e10d4cb4b31cc5ce4",
|
||||
"logSha256": "1983371a3c61ad944417f51bcd350a31e9e931be5a2796a0deea63cb222998f8",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "af6c302e83aad78e4fde8ea2968ac74d31a9604002046a3fef82505c0515a8c4",
|
||||
"logSha256": "8621424418d7a4f7ba3cb039a554be9e914e73fac1989bf28aa7dbc959ace28a",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ Any<Error>, \u2026(1) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n- Any<Error>,\n+ Error {\n+ \"message\": \"Maximum update depth exceeded\",\n+ },\n {\n- \"boundary\": \"route\",\n- \"componentStack\": StringContaining \"Boom\",\n+ \"boundary\": \"app\",\n+ \"componentStack\": \"\n+ at Boom (/Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:25:8)\n+ at RouteErrorBoundaryInner (/Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.tsx:14:1)\n+ at RouteErrorBoundary (/Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.tsx:103:31)\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:81:41\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "capture-stack",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": "boundary: \"route\", componentStack: info.componentStack",
|
||||
"after": "boundary: \"route\", componentStack: \"wrong-stack\"",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(captureBrowserExceptionMock).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "a3300f805575e44bf1080d21d4a5e340e8da1e6bee8e1b9f99b4afb2f2c5da23",
|
||||
"logSha256": "6142d202f3913c5768e0d391bf0f15cd1e3ead938d8ca6e003406110594b8365",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "3c03030c7ee7515ed74b20612ba4b1438cc211dda8d2d8ea8809213eee29f596",
|
||||
"logSha256": "1ab3c58e2732f4a56023fcaae9f286d6de2bb60ea52973c975e53a7f60bd9e92",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ Any<Error>, \u2026(1) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n- Any<Error>,\n+ Error {\n+ \"message\": \"Maximum update depth exceeded\",\n+ },\n {\n \"boundary\": \"route\",\n- \"componentStack\": StringContaining \"Boom\",\n+ \"componentStack\": \"wrong-stack\",\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:81:41\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "go-back",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": " Go back",
|
||||
"after": " Return",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": ").toBe(true);",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "cc46916e4056b5eda7134325b3167d1a9ad9826ba6e465ad3d3f431c0db810c6",
|
||||
"logSha256": "117b4d3c72c850183d3ab9628940f7a729dce79f55e0a777ef5927d9da441b1b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "4b561764a1a9d962fda0610d0794f71320c0a3523642ae8ff0e49f2a3524f2b0",
|
||||
"logSha256": "116d2d94a692a6a86a8acc74dd3b2aaa3d72443b6e87274164e0cf2ad6d200b5",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected false to be true // Object.is equality\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:88:7\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "title",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": "This page hit an error",
|
||||
"after": "Unexpected title",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).toContain(\"This page hit an error\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "f4f6f6cd80fd8ea6a668520c5e678cca1af4f2af117beed5c27aaedccd6a3888",
|
||||
"logSha256": "5f4ba767c3a107c3dbba86fc9959045a2d7d989c1a03a6f566eff3fc3130faa4",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "ee00df253bff2477b681d9c817ad323faf1d2e4b7f10fde13a1fe49f90530b65",
|
||||
"logSha256": "7ec386fd0c0fd4f37a8ac6bf5f7ad37f217f9be4a4647618750e92a937dac05e",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Unexpected titleSomething went wrong \u2026' to contain 'This page hit an error'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:90:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "detail",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": "{error.message}",
|
||||
"after": "{\"hidden detail\"}",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).toContain(\"Maximum update depth exceeded\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "cb7f79721a1f5f3a32b1d9ca0749875d97ad9b644e2663d84226ce12edd9124a",
|
||||
"logSha256": "b0ed753166122eafbd441172e5a2b47460fb08a2987cbacd53edb81861d33fd3",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "123f7949c426028edc343688cc50548d03399d5d56d5bb4a5dfcc7e5f7ed0ea9",
|
||||
"logSha256": "0f9c80ef5e76823ced425f4f7e11cd9adce0c776eebd99170564b2bd4bb035c7",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'This page hit an errorSomething went \u2026' to contain 'Maximum update depth exceeded'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:91:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "pre",
|
||||
"owner": "ui/src/components/RouteErrorBoundary.tsx",
|
||||
"sourceSha256": "cc0e78de2ea683aca27e1497c1aee355bb4ab9e22918f1282730be1ba78c3394",
|
||||
"title": "recovers and renders children again after the route changes",
|
||||
"mutation": {
|
||||
"before": "<pre className=",
|
||||
"after": "<div className=",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": true
|
||||
},
|
||||
"intendedGuard": "expect(container.querySelector(\"pre\")).not.toBeNull()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "21c50a1cc9340c311c63b5c2a030cc6345004e0a5a610be679b7ed3a7d1506d8",
|
||||
"logSha256": "d5be38cd7bd1f352f64d188d53c777f5eb7d18e9c3e11d9a43ee90a1809b72cb",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "44a7f4f7e9783b4e653d0db1512e2c40cc1d22502987517c18a36fd60152873e",
|
||||
"logSha256": "8d8849edac3006c90066a8e877ef863570e42f9cfe862f2a99e3d9b79ac829a1",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected null not to be null\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/RouteErrorBoundary.test.tsx:92:48\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20\n at new Promise (<anonymous>)\n at runWithTimeout (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3259:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3882:64"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0,
|
||||
"postControlPolish": {
|
||||
"controlCandidateSha256": "7d0c20e033b7f3daeccb0731ae544f213fccbfb297386786af014fdf385d5b93",
|
||||
"polishedCandidateSha256": "d4af25551dd6e7b20ffbd34500bf41603f6797187836ca7f554b898594266760",
|
||||
"difference": "Remove duplicate original title assertion; identical migrated title guard remains before route change",
|
||||
"allIntendedGuardStatementsAndLocationsUnchanged": true,
|
||||
"completePolishedFileReplayPassed": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# Test audit, Round 7 UI — Route error boundary follow-up
|
||||
|
||||
This isolated follow-up removes **2 registered executions, 2 root renders and 31 net test lines** from `ui/src/components/RouteErrorBoundary.test.tsx`. It changes no production lines and adds no helper. The pinned audit base is `c39262305b6b48a5decd564af8369083cd972b33`. Prior UI proof remains unchanged. Registration/render reductions are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact migrations
|
||||
|
||||
| Retired registration | Keeper | Original observations retained |
|
||||
|---|---|---|
|
||||
| `reports one captured error and keeps the recovery button` | `recovers and renders children again after the route changes` | expect(captureBrowserExceptionMock).toHaveBeenCalledTimes(1);; expect(captureBrowserExceptionMock).toHaveBeenCalledWith(expect.any(Error), { boundary: "route", componentStack: expect.stringContaining("Boom"), });; expect( Array.from(container.querySelectorAll("button")).some( (button) => button.textContent === "Go back", ), ).toBe(true); |
|
||||
| `renders a recoverable error card instead of a blank page when a child throws` | `recovers and renders children again after the route changes` | expect(container.textContent).toContain("This page hit an error");; expect(container.textContent).toContain("Maximum update depth exceeded");; expect(container.querySelector("pre")).not.toBeNull(); |
|
||||
|
||||
Layout.tsx and Layout.production.tsx wrap the routed Outlet with RouteErrorBoundary. Its inner boundary captures the thrown error and component stack, renders recovery UI and resets after pathname/search changes. Paired initial createRoot/Boom/act subtrees and unmount statements are AST-identical. All migrated guards run before route mutation and retain the original capture metadata, count, Go-back presence, title, detail and pre checks. The duplicate original title assertion is removed after proving equality with the migrated title guard; the separate actual Go-back click/navigation test remains unchanged.
|
||||
|
||||
The verifier reconstructs the exact candidate text from only the declared removals and insertions, requires byte equality, and compares the entire program AST after the same migrations. Every unrelated source byte, hook, mock, test body and binding remains unchanged, outside the audit receipt’s explicitly declared duplicate-title or blank-line cleanup where applicable. Setup ASTs are equal except the explicitly normalized attachment callback binding described above where applicable. No tests are skipped, softened, suppressed or added. Each file remains included in `@paperclipai/ui` under CI `general-workspaces-a`; Vitest file sharding selects it once across the two shards, with no workflow/config change.
|
||||
|
||||
## Complete-file evidence
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 4 | 4 | 0 | 0 |
|
||||
| Candidate | 2 | 2 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/RouteErrorBoundary.test.tsx`.
|
||||
|
||||
All 7 separately selected positive keeper controls passed. Every deliberate behavior mutation failed at the corresponding intended keeper assertion, not setup, timeout or another guard:
|
||||
|
||||
| Mutation | Intended assertion |
|
||||
|---|---|
|
||||
| capture-count | `expect(captureBrowserExceptionMock).toHaveBeenCalledTimes(1)` |
|
||||
| capture-boundary | `expect(captureBrowserExceptionMock).toHaveBeenCalledWith(` |
|
||||
| capture-stack | `expect(captureBrowserExceptionMock).toHaveBeenCalledWith(` |
|
||||
| go-back | `).toBe(true);` |
|
||||
| title | `expect(container.textContent).toContain("This page hit an error")` |
|
||||
| detail | `expect(container.textContent).toContain("Maximum update depth exceeded")` |
|
||||
| pre | `expect(container.querySelector("pre")).not.toBeNull()` |
|
||||
|
||||
Every mutated production owner was restored byte for byte after each mutation and checked against pinned Git bytes. Name-filtered controls select exactly one keeper; their unselected tests are not full-suite evidence. [Compact controls](2026-10-12-test-audit-r7-ui-route-controls.json) preserve source/owner hashes, exact migration map, body verifier digests, complete run/log digests, intended failure receipts and restoration checks. Temporary AST/receipt scripts initially needed corrections for chained assertions or statement position; those verifier attempts produced no accepted test evidence.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion; it is not claimed as a pass. These focused runs do not substitute for pending delivery-head CI. No push, rebase, pull request or merge is included in this packet.
|
||||
|
||||
The mutation controls preceded the final duplicate original-title removal. Exact byte reconstruction confirms that every intended migrated guard and its source location stayed unchanged; the final polished complete file passed again. The controls JSON records both candidate hashes.
|
||||
@@ -0,0 +1,192 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx",
|
||||
"base": "2e76f5157240ee8b2d42adbdcd4c345aea07c013",
|
||||
"originalSha256": "23857fdf6e5db9d28c63f37ce24f0fd86b5eb57645d4b69ee7c01c77dfef7c04",
|
||||
"candidateSha256": "07201fa2e9521bf6e235ee954ec4f7d72e9381d0083667130a9c5a907c7b1e41",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1,
|
||||
"netTestLinesRemoved": 25,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "opens the create-secret popover from the picker's + Create item (\u00a76.4, PAP-12476)",
|
||||
"keeper": "keeps the create-secret popover open when focus returns to a control inside the value cell (\u00a76.4, PAP-12492)",
|
||||
"exactLeadingStatements": 9,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": false,
|
||||
"kind": "normal",
|
||||
"exactMigratedStatements": [
|
||||
"expect(\n document.querySelector('input[aria-label=\"Secret name\"]'),\n \"create-secret name field should render\",\n ).toBeTruthy();"
|
||||
],
|
||||
"alreadyExactKeeperStatementsOrBindings": [],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 1,
|
||||
"setupInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "ff07f5e2e308cfc0f26584a6cd6599bcd98665e79d7f4782ed5bb36b35cb8cb9",
|
||||
"mutationVerifierSha256": "676b973d4838ed130c5ae92d4bc50fb32a65309af8c858450ec34bcce13729e9",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 28,
|
||||
"passed": 28,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "353ef0936ad65121e272049737cae95e8c3afcabaf78d7abb68fae773ab8af6f",
|
||||
"logSha256": "d5113caa5264644daf864032f6842fa4bfc514b3877e74d7d7d2d6fd11421c48"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 27,
|
||||
"passed": 27,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "139fedbdb9bfb03c4d5d9595a4249b12ecb948d731f96e02f0962b0321cdea37",
|
||||
"logSha256": "436156bfece2588699e879311e69a2a015701682a3786573df2aff1c29df5fa9"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "initial-secret-name",
|
||||
"owner": "ui/src/components/environment-variables-editor/CreateSecretPopover.tsx",
|
||||
"sourceSha256": "d971769d01a99a53aff654e6b837b9e2502eb0f8f05b9f4474e8a5b23d46f535",
|
||||
"title": "keeps the create-secret popover open when focus returns to a control inside the value cell (\u00a76.4, PAP-12492)",
|
||||
"mutation": {
|
||||
"before": "aria-label=\"Secret name\"",
|
||||
"after": "aria-label=\"Missing secret name\"",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "document.querySelector('input[aria-label=\"Secret name\"]')",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "c83e93ae3f862d9ba20f93d3e7174f9736d5ce00fa7c8e64bf49a7f0721aaddc",
|
||||
"logSha256": "d5ac329ef6d3cca724475b72d44c5db38ffdf6633a5f31a7151352389b3fab9c",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "c8944aa987dc601cec48de263d03c4fdf44e5da061bb55d21880c4590fee6d97",
|
||||
"logSha256": "7bcf8061f31e3d87a657e33bc2436ae72933b0236ab6ae7d5adfe552da2630f7",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: create-secret name field should render: expected null to be truthy\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx:700:7\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig-controls.json",
|
||||
"sha256": "cfb4357610beaa464cdb26848f74a7b8d967d6409c63dbd6821c92886a9cf980"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig.md",
|
||||
"sha256": "ea6d5dfc8cedcd653edd55ca9d77568431413a351f4de5db77ca6ac8f22b051a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental-controls.json",
|
||||
"sha256": "9c72abee7ee1a937fdbd6ccee870320308bc7ce06b4fc1e338cd9bd3b0efba6b"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental.md",
|
||||
"sha256": "93b7583a8ebba39dc97e390918fd62029955a00beda22756f55bd6374acc3737"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline-controls.json",
|
||||
"sha256": "af9d3f115e6e0518c2c0a2e11b8913e7aa82a35c848f114e6750884323601e17"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline.md",
|
||||
"sha256": "52d8e27970bd1c091ae7edf403b2331e921081da9198270df65ff779a8efc1bc"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat-controls.json",
|
||||
"sha256": "45d86fa901c8ab40bc3fe98d266e3ba1a7bbc93edb7bc2aae60ccd9bfa130c37"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat.md",
|
||||
"sha256": "15e84a553c5a468aea013941f68d3aa517bfb5a13ae296a90e1a39586f85de8e"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail-controls.json",
|
||||
"sha256": "a56f183f1c6b940662db0c4bfb62457eda0cec453c2019b439ec716ed9d2a3e1"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail.md",
|
||||
"sha256": "e137af5b991c936a167d6ad6f3c710b99688e939f839b163b203259fc998ded8"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Test audit, Round 7 UI — Secret picker popover follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 1 rendered scenarios and 25 net test lines** from `ui/src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx`. It adds no helper and changes no production lines. Audit base: `2e76f5157240ee8b2d42adbdcd4c345aea07c013`. All prior committed proof remains byte-identical. Registration/render savings are per complete-suite invocation; no measured runtime or CI-speed claim is made. A service-worker retirement counts only initializer setup, never a render.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Exact observations migrated |
|
||||
|---|---|---|
|
||||
| `opens the create-secret popover from the picker's + Create item (§6.4, PAP-12476)` | `keeps the create-secret popover open when focus returns to a control inside the value cell (§6.4, PAP-12492)` | expect( document.querySelector('input[aria-label="Secret name"]'), "create-secret name field should render", ).toBeTruthy(); |
|
||||
|
||||
The retired picker/create transition and keeper use the same secret-ref value, secrets fixture, fresh callbacks, editor render, combobox focus, Create-item lookup/click and flush. Nine leading statements already match, including the initial heading assertion. The exact initial Secret name input-presence check moves before the keeper drives its late in-anchor focus return. Row.tsx retains the outside-focus guard; CreateSecretPopover.tsx owns the actual name input. Later heading/input persistence checks, outside interactions, store-as-secret and secret lifecycle cases remain unchanged.
|
||||
|
||||
The verifier requires matching setup statements, enclosing hooks and explicit teardown where present. It reconstructs candidate source solely from the declared registrations/assertion migrations and requires exact byte equality plus full-program AST equality outside those edits. Existing shared variable bindings, mock arguments and provider/component inputs remain the same. No other fixture, helper, action order, wait, assertion strength, timer or cleanup changes. Default CI includes the file in `@paperclipai/ui` under `general-workspaces-a`, once across its two file shards, with no workflow/config change.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 28 | 28 | 0 | 0 |
|
||||
| Candidate | 27 | 27 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx`.
|
||||
|
||||
All 1 separately selected positive controls passed. All 1 distinct production-owner regressions failed at intended retained/migrated assertions, not setup, timeout or another guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| initial-secret-name | `document.querySelector('input[aria-label="Secret name"]')` |
|
||||
|
||||
Exactly one production owner changed per control, and every owner was restored byte for byte after each mutation and checked against pinned Git bytes. Literal keeper-name filters are escaped; unselected cases are not full-suite evidence. The initial first-install receipt verifier mistook emit( for it( and rejected its receipt despite the intended test failure. The declaration-boundary matcher was corrected and that control rerun; the rejected verifier attempt supplies no accepted evidence.
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-secret-popover-controls.json) preserves source/owner hashes, exact before/after migrations, matched setup and variable-binding proof, complete-file run/log digests, intended assertion failures and restoration checks. Temporary raw scripts/logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion and is not claimed as passed. These separate focused packets precede delivery-head replay and full CI. This packet includes no push, rebase, PR or merge.
|
||||
@@ -0,0 +1,197 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/lib/service-worker-updates.test.ts",
|
||||
"base": "2e76f5157240ee8b2d42adbdcd4c345aea07c013",
|
||||
"originalSha256": "536e1fd2e7c9c0641fa45a9c4b3a5a2d08819e6673c599cf25dc2c26403b19f2",
|
||||
"candidateSha256": "225c31546a4bedc53651e364723ef24865c7b0bdb8e73e4f4f201d9c9f39bf51",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 0,
|
||||
"setupInvocationsRemoved": 1,
|
||||
"netTestLinesRemoved": 10,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "does not reload on a first-ever install",
|
||||
"keeper": "reloads when a later deploy replaces the worker a first-visit tab installed",
|
||||
"exactLeadingStatements": 6,
|
||||
"sameEnclosingHooks": true,
|
||||
"explicitCleanupAstEqual": false,
|
||||
"kind": "subset",
|
||||
"exactMigratedStatements": [],
|
||||
"alreadyExactKeeperStatementsOrBindings": [
|
||||
"const { container, emit } = fakeContainer({ controlled: false });",
|
||||
"const { doc } = fakeDocument(\"hidden\");",
|
||||
"const reload = vi.fn();",
|
||||
"startServiceWorkerUpdates({ container, documentRef: doc, reload });",
|
||||
"emit(\"controllerchange\");",
|
||||
"expect(reload).not.toHaveBeenCalled();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"renderInvocationsRemoved": 0,
|
||||
"setupInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "ff07f5e2e308cfc0f26584a6cd6599bcd98665e79d7f4782ed5bb36b35cb8cb9",
|
||||
"mutationVerifierSha256": "676b973d4838ed130c5ae92d4bc50fb32a65309af8c858450ec34bcce13729e9",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"total": 9,
|
||||
"passed": 9,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "3b7fd48a3efcfb1e5cb4316e9a4bd1337765ad83f241269c30ad98407877a2bc",
|
||||
"logSha256": "17439460b27c125513f7429f55f7a702a1b4e8643b9be25f30758a49aae64ccc"
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"total": 8,
|
||||
"passed": 8,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"jsonSha256": "a64d06c0f236e73f3e446a515111f74c99f2f924ac381a6f8d4b88767b7cfba2",
|
||||
"logSha256": "855085927b57d14b77892447f94ba3bc875946aa11e0208f900a4e481f7e51c7"
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "first-install",
|
||||
"owner": "ui/src/lib/service-worker-updates.ts",
|
||||
"sourceSha256": "2a6334bf805d331093ab9970d638cb732f0bb8070ef1ae2abea422ef8ce872db",
|
||||
"title": "reloads when a later deploy replaces the worker a first-visit tab installed",
|
||||
"mutation": {
|
||||
"before": "let wasControlled = Boolean(container.controller);",
|
||||
"after": "let wasControlled = true;",
|
||||
"replacementCount": 1
|
||||
},
|
||||
"intendedGuard": "expect(reload).not.toHaveBeenCalled()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "a3511733ddddee2fa1b578225f0ec4e24544c824f76035e91c7a09517744f2a2",
|
||||
"logSha256": "0598496c0b9388dc737b62b28f24a48e5242f3b78dc2871a6809be676215569a",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "421853ba449e7c93190f57187b683e42cc281f6ce5e744f5f786e8ec36425699",
|
||||
"logSha256": "b8326ed6cb42630f0418ed7f75c8a43e88ee52de81b48c0315252ced142b166e",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to not be called at all, but actually been called 1 times\n\nReceived:\n\n 1st vi.fn() call:\n\n Array []\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2214:51)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/lib/service-worker-updates.test.ts:97:24\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:1628:35\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:26\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3321:20\n at new Promise (<anonymous>)\n at runWithCancel (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3316:10)\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:3301:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig-controls.json",
|
||||
"sha256": "cfb4357610beaa464cdb26848f74a7b8d967d6409c63dbd6821c92886a9cf980"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-agentconfig.md",
|
||||
"sha256": "ea6d5dfc8cedcd653edd55ca9d77568431413a351f4de5db77ca6ac8f22b051a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary-controls.json",
|
||||
"sha256": "bad49e7bc92dea919df6b980bfa45e7452984cf31a6fd4655b355ba0444ff9a6"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appboundary.md",
|
||||
"sha256": "cd6f173584db0d485004af6f85b1ce78fa65b19d8c6ef582b061f0d232c6c1ed"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments-controls.json",
|
||||
"sha256": "c4ff66a3226e1edafd286cad0ca45c9445795b46944cf70d606c59a754913f3a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-attachments.md",
|
||||
"sha256": "d38715393c95306d10e871a5a2c9a32f105fa1452871cd7ad030d86dbbdb7416"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental-controls.json",
|
||||
"sha256": "9c72abee7ee1a937fdbd6ccee870320308bc7ce06b4fc1e338cd9bd3b0efba6b"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-experimental.md",
|
||||
"sha256": "93b7583a8ebba39dc97e390918fd62029955a00beda22756f55bd6374acc3737"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline-controls.json",
|
||||
"sha256": "af9d3f115e6e0518c2c0a2e11b8913e7aa82a35c848f114e6750884323601e17"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-inline.md",
|
||||
"sha256": "52d8e27970bd1c091ae7edf403b2331e921081da9198270df65ff779a8efc1bc"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat-controls.json",
|
||||
"sha256": "45d86fa901c8ab40bc3fe98d266e3ba1a7bbc93edb7bc2aae60ccd9bfa130c37"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuechat.md",
|
||||
"sha256": "15e84a553c5a468aea013941f68d3aa517bfb5a13ae296a90e1a39586f85de8e"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail-controls.json",
|
||||
"sha256": "a56f183f1c6b940662db0c4bfb62457eda0cec453c2019b439ec716ed9d2a3e1"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-issuedetail.md",
|
||||
"sha256": "e137af5b991c936a167d6ad6f3c710b99688e939f839b163b203259fc998ded8"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route-controls.json",
|
||||
"sha256": "0a72a7770ec749f2c0507a74d395e65d70b722d0b9d2a767a2c066a67ed46382"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-route.md",
|
||||
"sha256": "9b5e1543163d6c9d0958aa3e377a593ec458f6674ce1ddb2bf3ca158786f09c4"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar-controls.json",
|
||||
"sha256": "ae9f24746e67432cf6247c5da130b9c1bba46fc85112c0610d3f18545dfce326"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-sidebar.md",
|
||||
"sha256": "5e267441c43aa8375fa5c54edcbd2879428d657708be9a2ab372aa66e4792886"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline-controls.json",
|
||||
"sha256": "6a86b07770f1cd8d758334fb61c46254197a7f533d24c056fc9a52c317488291"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-timeline.md",
|
||||
"sha256": "612dc3ec4c3d435888055ba20a7f28230ce89ea8b071b8c38d1ea2c147074a96"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Test audit, Round 7 UI — First service-worker takeover follow-up
|
||||
|
||||
This separate packet removes **1 registered executions, 0 rendered scenarios and 10 net test lines** from `ui/src/lib/service-worker-updates.test.ts`. It adds no helper and changes no production lines. Audit base: `2e76f5157240ee8b2d42adbdcd4c345aea07c013`. All prior committed proof remains byte-identical. Registration/render savings are per complete-suite invocation; no measured runtime or CI-speed claim is made. A service-worker retirement counts only initializer setup, never a render.
|
||||
|
||||
## Exact retirement map
|
||||
|
||||
| Retired registration | Keeper | Exact observations migrated |
|
||||
|---|---|---|
|
||||
| `does not reload on a first-ever install` | `reloads when a later deploy replaces the worker a first-visit tab installed` | |
|
||||
|
||||
The retired complete body is an exact prefix of the keeper: fresh uncontrolled fake container, hidden document, reload mock, real startServiceWorkerUpdates invocation, first controllerchange and no-reload assertion. The keeper remains byte-identical, then verifies that the later takeover reloads exactly once. This retirement removes one registration and initializer/observer setup, not a render. Initial-controlled and uncontrolled states, visibility, update checks and cleanup cases stay distinct. The mutation incorrectly treats a first-visit tab as already controlled; the retained first no-reload guard fails, so the boundary remains clear and meaningful.
|
||||
|
||||
The verifier requires matching setup statements, enclosing hooks and explicit teardown where present. It reconstructs candidate source solely from the declared registrations/assertion migrations and requires exact byte equality plus full-program AST equality outside those edits. Existing shared variable bindings, mock arguments and provider/component inputs remain the same. No other fixture, helper, action order, wait, assertion strength, timer or cleanup changes. Default CI includes the file in `@paperclipai/ui` under `general-workspaces-a`, once across its two file shards, with no workflow/config change.
|
||||
|
||||
## Complete owner-file results
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 9 | 9 | 0 | 0 |
|
||||
| Candidate | 8 | 8 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/lib/service-worker-updates.test.ts`.
|
||||
|
||||
All 1 separately selected positive controls passed. All 1 distinct production-owner regressions failed at intended retained/migrated assertions, not setup, timeout or another guard:
|
||||
|
||||
| Regression | Intended guard |
|
||||
|---|---|
|
||||
| first-install | `expect(reload).not.toHaveBeenCalled()` |
|
||||
|
||||
Exactly one production owner changed per control, and every owner was restored byte for byte after each mutation and checked against pinned Git bytes. Literal keeper-name filters are escaped; unselected cases are not full-suite evidence. The initial first-install receipt verifier mistook emit( for it( and rejected its receipt despite the intended test failure. The declaration-boundary matcher was corrected and that control rerun; the rejected verifier attempt supplies no accepted evidence.
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-service-worker-controls.json) preserves source/owner hashes, exact before/after migrations, matched setup and variable-binding proof, complete-file run/log digests, intended assertion failures and restoration checks. Temporary raw scripts/logs are identified by digests rather than committed in full.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion and is not claimed as passed. These separate focused packets precede delivery-head replay and full CI. This packet includes no push, rebase, PR or merge.
|
||||
@@ -0,0 +1,244 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/Sidebar.test.tsx",
|
||||
"base": "c39262305b6b48a5decd564af8369083cd972b33",
|
||||
"originalSha256": "9b463dd219ff0c4ef3a76f853feb29514e740102cf8f838a6bef981682d43f78",
|
||||
"candidateSha256": "5b9033240feffd2c58165893f7c8a3037982711826415da8a52f330a05000eba",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"registeredInvocationsRemoved": 4,
|
||||
"providerRootRendersRemoved": 4,
|
||||
"newHelpers": 0,
|
||||
"netTestLinesRemoved": 40,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "does not flash the Workspaces link while experimental settings are loading",
|
||||
"keeper": "keeps the simplified navigation while experimental settings are loading",
|
||||
"setupAstEqual": true,
|
||||
"unmountAstEqual": true,
|
||||
"guards": [
|
||||
"expect(container.textContent).not.toContain(\"Workspaces\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "reserves the Goals nav slot while experimental settings are loading",
|
||||
"keeper": "keeps the simplified navigation while experimental settings are loading",
|
||||
"setupAstEqual": true,
|
||||
"unmountAstEqual": true,
|
||||
"guards": [
|
||||
"expect([...container.querySelectorAll(\"nav a\")].map((a) => a.textContent?.trim())).not.toContain(\"Goals\");",
|
||||
"expect(container.querySelector('[data-testid=\"sidebar-goals-placeholder\"]')).not.toBeNull();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "does not flash the Conference Room item while experimental settings are loading (PAP-137)",
|
||||
"keeper": "keeps the simplified navigation while experimental settings are loading",
|
||||
"setupAstEqual": true,
|
||||
"unmountAstEqual": true,
|
||||
"guards": [
|
||||
"expect(container.textContent).not.toContain(\"Conference Room\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "does not flash the Pipelines nav item while experimental settings are loading",
|
||||
"keeper": "keeps the simplified navigation while experimental settings are loading",
|
||||
"setupAstEqual": true,
|
||||
"unmountAstEqual": true,
|
||||
"guards": [
|
||||
"expect(container.textContent).not.toContain(\"Pipelines\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "31de795f5ffc44365740f1788b497fe117c236778ce05d0a95cc2472c014ce71",
|
||||
"mutationVerifierSha256": "3783a894b558341f2a8ef0eddf7f774fb6ee46833161feca5def1279d7d4b887",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "38374087c2e9c98935c9b0e6e563c827127b21750f9797d3536271591bf793a5",
|
||||
"logSha256": "361d260b375e5252086dac55605788a1a54421715d38ddc00700442562e707eb",
|
||||
"total": 35,
|
||||
"passed": 35,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "ce7c9e0fd848df8baf87a6d60c00190fae11eacbdd01991ca0e22388a1205780",
|
||||
"logSha256": "a7bc412cee1de9d731c66a69dac53e69fa195807951091fefd1298c9b120ef86",
|
||||
"total": 31,
|
||||
"passed": 31,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "workspaces",
|
||||
"owner": "ui/src/components/Sidebar.tsx",
|
||||
"sourceSha256": "2baa44740b00babc7fc5a02c2d28d8b5ed2bc478ad8840200f2689f073a1c9bd",
|
||||
"title": "keeps the simplified navigation while experimental settings are loading",
|
||||
"mutation": {
|
||||
"before": "const showWorkspacesLink = !chatRail && experimentalSettings?.enableIsolatedWorkspaces === true;",
|
||||
"after": "const showWorkspacesLink = experimentalSettings === undefined || (!chatRail && experimentalSettings?.enableIsolatedWorkspaces === true);"
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).not.toContain(\"Workspaces\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "0925c954fb40aeadf79dea76a015a9dcb637e2db9995b62e05094c86e0343f51",
|
||||
"logSha256": "04c617da040c5b2f26885b6268951335b4c048f4deeed0fc2ec5ee511992b23a",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "adb925244eaeb8a6b3206f425c5e10775ec7f3cda791a965e739c55264cb4626",
|
||||
"logSha256": "6cf3b4b626a80e4443325c75f7fe1887f12425322f142d93b69c476fcda9e8f3",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Company menuNew TaskSearchDashboardIn\u2026' not to contain 'Workspaces'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/Sidebar.test.tsx:270:39\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "goals",
|
||||
"owner": "ui/src/components/Sidebar.tsx",
|
||||
"sourceSha256": "2baa44740b00babc7fc5a02c2d28d8b5ed2bc478ad8840200f2689f073a1c9bd",
|
||||
"title": "keeps the simplified navigation while experimental settings are loading",
|
||||
"mutation": {
|
||||
"before": "const showGoalsLink = experimentalSettings?.enableGoalsSidebarLink === true;",
|
||||
"after": "const showGoalsLink = experimentalSettings === undefined || experimentalSettings?.enableGoalsSidebarLink === true;"
|
||||
},
|
||||
"intendedGuard": "expect([...container.querySelectorAll(\"nav a\")].map((a) => a.textContent?.trim())).not.toContain(\"Goals\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "c65540e311c4131bb3b367088e8b3a2d175eb55577bcd434dd0128b4169374e9",
|
||||
"logSha256": "87f3efaff20b56dfe4943004415205e0f1e1d06733230ccf8cd9dc27162cbebb",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "512f85c2e3ec85a0c09364a4872484e237f3771a7628911b569602b8987a98eb",
|
||||
"logSha256": "d58f1e168ce951a214c10f9b2a690b061fc98113b3645ce1504129606a247620",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected [ 'Search', 'Dashboard', \u2026(10) ] to not include 'Goals'\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2077:15)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/Sidebar.test.tsx:271:92\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "conference",
|
||||
"owner": "ui/src/components/Sidebar.tsx",
|
||||
"sourceSha256": "2baa44740b00babc7fc5a02c2d28d8b5ed2bc478ad8840200f2689f073a1c9bd",
|
||||
"title": "keeps the simplified navigation while experimental settings are loading",
|
||||
"mutation": {
|
||||
"before": "const conferenceRoomChatEnabled = experimentalSettings?.enableConferenceRoomChat === true;",
|
||||
"after": "const conferenceRoomChatEnabled = experimentalSettings === undefined || experimentalSettings?.enableConferenceRoomChat === true;"
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).not.toContain(\"Conference Room\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "0637b1e10ca0d6d18e82baf74113b71d5217c879f7c26ce16fc2aab888c942ec",
|
||||
"logSha256": "88ba4df5eab5e9e6b90f43104c534d9ec4cdfeb32770a1d66a301c47dd9381d5",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "0867024b3b2a6995608c8eefaafe9bc4d59000dee742c987d6d3948e89352ddd",
|
||||
"logSha256": "c03a4143999fc21ff93cd6e945113cb76c140c34483515a26f4321eb84b25d24",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Company menuNew TaskSearchDashboardIn\u2026' not to contain 'Conference Room'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/Sidebar.test.tsx:273:39\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "pipelines",
|
||||
"owner": "ui/src/components/Sidebar.tsx",
|
||||
"sourceSha256": "2baa44740b00babc7fc5a02c2d28d8b5ed2bc478ad8840200f2689f073a1c9bd",
|
||||
"title": "keeps the simplified navigation while experimental settings are loading",
|
||||
"mutation": {
|
||||
"before": "const showPipelines = experimentalSettings?.enablePipelines === true;",
|
||||
"after": "const showPipelines = experimentalSettings === undefined || experimentalSettings?.enablePipelines === true;"
|
||||
},
|
||||
"intendedGuard": "expect(container.textContent).not.toContain(\"Pipelines\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "290be9d7e8547a1842c594f0ba83c443e812ebaf7cebbb5ecf1a17f155d07cbc",
|
||||
"logSha256": "5d56516d7a59c85d3c4926780e6ec3c1b60467fcd9818160cc0014da29adb6c5",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "5922637d763e6dbe8091839eef6f6d1336c22c32caaf922671058471fb8834ee",
|
||||
"logSha256": "bf37d6a6eddb8da880fd0fff2680369b11cd9784d81d0f1d36fc7129ea3be981",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected 'Company menuNew TaskSearchDashboardIn\u2026' not to contain 'Pipelines'\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/Sidebar.test.tsx:274:39\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "goals-placeholder",
|
||||
"owner": "ui/src/components/Sidebar.tsx",
|
||||
"sourceSha256": "2baa44740b00babc7fc5a02c2d28d8b5ed2bc478ad8840200f2689f073a1c9bd",
|
||||
"title": "keeps the simplified navigation while experimental settings are loading",
|
||||
"mutation": {
|
||||
"before": "const goalsLinkPending = experimentalSettings === undefined;",
|
||||
"after": "const goalsLinkPending = false;"
|
||||
},
|
||||
"intendedGuard": "expect(container.querySelector('[data-testid=\"sidebar-goals-placeholder\"]')).not.toBeNull()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "db75c234ed61cab71950d8e371a17496f3faa6e378f8fb4c8f5ba2eda2ff657a",
|
||||
"logSha256": "79549245a36c31565101c45b74edbc22a9ebeffbe8b6206e3ba8530c3f03d454",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "af191c72c1f19e45e061cd3b4af054e4abe41087e48cf7b882b78f5698805621",
|
||||
"logSha256": "528addefacd70920cd7fd16fbc15cb6be237fdc21379d6e61722ee847648aa3f",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected null not to be null\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/components/Sidebar.test.tsx:272:86\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# Test audit, Round 7 UI — Sidebar loading state follow-up
|
||||
|
||||
This isolated follow-up removes **4 registered executions, 4 root renders and 40 net test lines** from `ui/src/components/Sidebar.test.tsx`. It changes no production lines and adds no helper. The pinned audit base is `c39262305b6b48a5decd564af8369083cd972b33`. Prior UI proof remains unchanged. Registration/render reductions are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact migrations
|
||||
|
||||
| Retired registration | Keeper | Original observations retained |
|
||||
|---|---|---|
|
||||
| `does not flash the Workspaces link while experimental settings are loading` | `keeps the simplified navigation while experimental settings are loading` | expect(container.textContent).not.toContain("Workspaces"); |
|
||||
| `reserves the Goals nav slot while experimental settings are loading` | `keeps the simplified navigation while experimental settings are loading` | expect([...container.querySelectorAll("nav a")].map((a) => a.textContent?.trim())).not.toContain("Goals");; expect(container.querySelector('[data-testid="sidebar-goals-placeholder"]')).not.toBeNull(); |
|
||||
| `does not flash the Conference Room item while experimental settings are loading (PAP-137)` | `keeps the simplified navigation while experimental settings are loading` | expect(container.textContent).not.toContain("Conference Room"); |
|
||||
| `does not flash the Pipelines nav item while experimental settings are loading` | `keeps the simplified navigation while experimental settings are loading` | expect(container.textContent).not.toContain("Pipelines"); |
|
||||
|
||||
Sidebar.tsx loads instance settings through useQuery. Workspaces, Goals, Conference Room and Pipelines derive from the same unresolved experimentalSettings value; Goals preserves its pending placeholder. The keeper uses the exact original never-resolving promise and renderSidebar helper. Existing nav checks remain; all five original moved assertions run before the unchanged unmount. Enabled, disabled, mobile, legacy, chat and permission/navigation variants remain distinct.
|
||||
|
||||
The verifier reconstructs the exact candidate text from only the declared removals and insertions, requires byte equality, and compares the entire program AST after the same migrations. Every unrelated source byte, hook, mock, test body and binding remains unchanged, outside the audit receipt’s explicitly declared duplicate-title or blank-line cleanup where applicable. Setup ASTs are equal except the explicitly normalized attachment callback binding described above where applicable. No tests are skipped, softened, suppressed or added. Each file remains included in `@paperclipai/ui` under CI `general-workspaces-a`; Vitest file sharding selects it once across the two shards, with no workflow/config change.
|
||||
|
||||
## Complete-file evidence
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 35 | 35 | 0 | 0 |
|
||||
| Candidate | 31 | 31 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/components/Sidebar.test.tsx`.
|
||||
|
||||
All 5 separately selected positive keeper controls passed. Every deliberate behavior mutation failed at the corresponding intended keeper assertion, not setup, timeout or another guard:
|
||||
|
||||
| Mutation | Intended assertion |
|
||||
|---|---|
|
||||
| workspaces | `expect(container.textContent).not.toContain("Workspaces")` |
|
||||
| goals | `expect([...container.querySelectorAll("nav a")].map((a) => a.textContent?.trim())).not.toContain("Goals")` |
|
||||
| conference | `expect(container.textContent).not.toContain("Conference Room")` |
|
||||
| pipelines | `expect(container.textContent).not.toContain("Pipelines")` |
|
||||
| goals-placeholder | `expect(container.querySelector('[data-testid="sidebar-goals-placeholder"]')).not.toBeNull()` |
|
||||
|
||||
Every mutated production owner was restored byte for byte after each mutation and checked against pinned Git bytes. Name-filtered controls select exactly one keeper; their unselected tests are not full-suite evidence. [Compact controls](2026-10-12-test-audit-r7-ui-sidebar-controls.json) preserve source/owner hashes, exact migration map, body verifier digests, complete run/log digests, intended failure receipts and restoration checks. Temporary AST/receipt scripts initially needed corrections for chained assertions or statement position; those verifier attempts produced no accepted test evidence.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion; it is not claimed as a pass. These focused runs do not substitute for pending delivery-head CI. No push, rebase, pull request or merge is included in this packet.
|
||||
@@ -0,0 +1,271 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/pages/Timeline.test.tsx",
|
||||
"base": "c39262305b6b48a5decd564af8369083cd972b33",
|
||||
"originalSha256": "19543e8f10e41d438633bd3a825f45a9fa3c56f2807c01bc245b4e8e185a1108",
|
||||
"candidateSha256": "8d5909b8aaf4d5d882d2fd1bc9072a2e8bb7fce9c98466ca15a6382e279ec9a7",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"additionalDeclaredCleanup": "Remove extra blank line before describe closes",
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 2,
|
||||
"rootRendersRemoved": 2,
|
||||
"netTestLinesRemoved": 26,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "requests the collapsed app sidebar by default",
|
||||
"keeper": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"setupAstEqual": true,
|
||||
"setupBindingNormalization": null,
|
||||
"guards": [
|
||||
"expect(container.querySelector('[data-testid=\"request-collapsed-sidebar\"]')).not.toBeNull();"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "requests the company timeline without a user lens parameter",
|
||||
"keeper": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"setupAstEqual": true,
|
||||
"setupBindingNormalization": null,
|
||||
"guards": [
|
||||
"expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(\n \"company-1\",\n expect.objectContaining({\n from: expect.any(String),\n to: expect.any(String),\n }),\n { signal: expect.any(AbortSignal) },\n );",
|
||||
"expect(mockWorkTimelineApi.get.mock.calls[0]?.[1]).not.toHaveProperty(\"userId\");"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "0f1b173c8a399bb8c3e831edac6805634304a749faffcfff7b8b1900da3d34a3",
|
||||
"mutationVerifierSha256": "a05f5ba9071c0ce7ce8eac1bc0f6c7dd83401c4e720816389d779f8919312064",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "ae7cd09b89cd59f8f56d2ce5da5de1c87118bfd6b2893f212d61feff14398d1e",
|
||||
"logSha256": "85573e535172170fc924ed4ec78737698a3a21cebb3cc9d1aa5fdc72564c44c8",
|
||||
"total": 9,
|
||||
"passed": 9,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "8ae0a854a756f627c20177d38418368d2ba82e141cc85a2010136af545449938",
|
||||
"logSha256": "122fdad4346b64464466e96f0f34d5169578dcdc83de673df9ea82306a51224f",
|
||||
"total": 7,
|
||||
"passed": 7,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "collapsed",
|
||||
"owner": "ui/src/pages/Timeline.tsx",
|
||||
"sourceSha256": "32dd72ff0b295bf23863f438a1be2b0262f039ea4e86b20c0e8df12abc5fb703",
|
||||
"title": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"mutation": {
|
||||
"before": "{!embedded && <RequestCollapsedSidebar />}",
|
||||
"after": "{false && !embedded && <RequestCollapsedSidebar />}",
|
||||
"replacementCount": 2,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(container.querySelector('[data-testid=\"request-collapsed-sidebar\"]')).not.toBeNull()",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "3c601ebb6478cc5815ab76983793956f042357ebc63a753df89d786ff5487e7d",
|
||||
"logSha256": "bc0475e992fcaddd61f32e2bc5038fc469b86a8895c33159909cb089f5f1fc1b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "1669312785afa8b36c3e8495ff74cadddc7669a208289aa2a18687b4035cb89e",
|
||||
"logSha256": "caf49d3737ee24ea97c9f3adbf9165b8943ee107f66454c0a594ae5015afbc81",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected null not to be null\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/Timeline.test.tsx:187:86\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "userId",
|
||||
"owner": "ui/src/pages/Timeline.tsx",
|
||||
"sourceSha256": "32dd72ff0b295bf23863f438a1be2b0262f039ea4e86b20c0e8df12abc5fb703",
|
||||
"title": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"mutation": {
|
||||
"before": " ...params,",
|
||||
"after": " ...params,\n userId: \"unexpected-user\",",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(mockWorkTimelineApi.get.mock.calls[0]?.[1]).not.toHaveProperty(\"userId\")",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "f7f10efaff0dd60e4cdcf0cc25f48382b14abc2223a8999e570444988940af60",
|
||||
"logSha256": "eb14e95f3ba6aa4ef7c296071894a3ef933c82f5d89d9624e2fba7ce6faadd5b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "d809b1ab3cc65b66e7de8d8f09dc33f26ab2ce30c45fd7d5b1960389628cb0e8",
|
||||
"logSha256": "73d702114b4bb05f2629e7b07be87a62b0cbdfb92958c2db55a3808bb7156b6f",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected { \u2026(5) } to not have property \"userId\"\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/Timeline.test.tsx:197:60\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "company",
|
||||
"owner": "ui/src/pages/Timeline.tsx",
|
||||
"sourceSha256": "32dd72ff0b295bf23863f438a1be2b0262f039ea4e86b20c0e8df12abc5fb703",
|
||||
"title": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"mutation": {
|
||||
"before": "workTimelineApi.get(companyId, {",
|
||||
"after": "workTimelineApi.get(\"wrong-company\", {",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "cd568cc81271b304b2fc0c977a746436f7e6b49ebbc5e2f662fc9edfd20d517e",
|
||||
"logSha256": "b0e17b15d6b5feeeabfa130ef7450242c7903631ee3ad90c15c348bea3924e8b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "5336632844af286db208fdf555dd21685ab4e6cc22ac09f26ad3550379d24bc0",
|
||||
"logSha256": "340421b7b504bd8d0676f36fc2761f33a1a492e3e93dc3fb2bd8b5e183d13546",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'company-1', \u2026(2) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n- \"company-1\",\n- ObjectContaining {\n- \"from\": Any<String>,\n- \"to\": Any<String>,\n+ \"wrong-company\",\n+ {\n+ \"from\": \"2026-10-06T05:00:00.000Z\",\n+ \"limit\": 500,\n+ \"offset\": 0,\n+ \"to\": \"2026-10-13T04:59:59.999Z\",\n },\n {\n- \"signal\": Any<AbortSignal>,\n+ \"signal\": AbortSignal {\n+ Symbol(kEvents): Map {},\n+ Symbol(events.maxEventTargetListeners): 0,\n+ Symbol(events.maxEventTargetListenersWarned): false,\n+ Symbol(kHandlers): Map {},\n+ Symbol(kAborted): false,\n+ Symbol(kReason): undefined,\n+ Symbol(kComposite): false,\n+ },\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/Timeline.test.tsx:189:37\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "from",
|
||||
"owner": "ui/src/pages/Timeline.tsx",
|
||||
"sourceSha256": "32dd72ff0b295bf23863f438a1be2b0262f039ea4e86b20c0e8df12abc5fb703",
|
||||
"title": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"mutation": {
|
||||
"before": " ...params,",
|
||||
"after": " ...params,\n from: 123,",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "0aa7e25d872ce8864151a25adf4ed3d744b425e99273a24a55edf55f6ab1b6ba",
|
||||
"logSha256": "aaf0f2657f7bb1ca04517ce053e0728fb83c94223664d4ef1fb0cae0f9d61cd5",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "9feb2d30523ad4e7567d61d28e24f69eaa1a1869eaf1ff6e01cdb829d5c50b15",
|
||||
"logSha256": "dd4740f663a636023f5ced054eaed3e51b0d1ee3db5df51f8f0127c8329564a6",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'company-1', \u2026(2) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n \"company-1\",\n- ObjectContaining {\n- \"from\": Any<String>,\n- \"to\": Any<String>,\n+ {\n+ \"from\": 123,\n+ \"limit\": 500,\n+ \"offset\": 0,\n+ \"to\": \"2026-10-13T04:59:59.999Z\",\n },\n {\n- \"signal\": Any<AbortSignal>,\n+ \"signal\": AbortSignal {\n+ Symbol(kEvents): Map {},\n+ Symbol(events.maxEventTargetListeners): 0,\n+ Symbol(events.maxEventTargetListenersWarned): false,\n+ Symbol(kHandlers): Map {},\n+ Symbol(kAborted): false,\n+ Symbol(kReason): undefined,\n+ Symbol(kComposite): false,\n+ },\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/Timeline.test.tsx:189:37\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "to",
|
||||
"owner": "ui/src/pages/Timeline.tsx",
|
||||
"sourceSha256": "32dd72ff0b295bf23863f438a1be2b0262f039ea4e86b20c0e8df12abc5fb703",
|
||||
"title": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"mutation": {
|
||||
"before": " ...params,",
|
||||
"after": " ...params,\n to: 123,",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "21260048ec34af7eda86634ddd81d75077b8e8cbaa7b8fd54406a59de8ac2d7c",
|
||||
"logSha256": "3100577ba46ffe7b80de31995086231d60745954b26e5e95cab0462b7925f4bf",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "8c49b367f7089b044ea8084ec2c03f7896402544b0d1f917601d941b000c4355",
|
||||
"logSha256": "e00c810574b08bb023ddb25751e81a0320b013ea8bd6b9a25f4b56406a246ccb",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'company-1', \u2026(2) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n \"company-1\",\n- ObjectContaining {\n- \"from\": Any<String>,\n- \"to\": Any<String>,\n+ {\n+ \"from\": \"2026-10-06T05:00:00.000Z\",\n+ \"limit\": 500,\n+ \"offset\": 0,\n+ \"to\": 123,\n },\n {\n- \"signal\": Any<AbortSignal>,\n+ \"signal\": AbortSignal {\n+ Symbol(kEvents): Map {},\n+ Symbol(events.maxEventTargetListeners): 0,\n+ Symbol(events.maxEventTargetListenersWarned): false,\n+ Symbol(kHandlers): Map {},\n+ Symbol(kAborted): false,\n+ Symbol(kReason): undefined,\n+ Symbol(kComposite): false,\n+ },\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/Timeline.test.tsx:189:37\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "signal",
|
||||
"owner": "ui/src/pages/Timeline.tsx",
|
||||
"sourceSha256": "32dd72ff0b295bf23863f438a1be2b0262f039ea4e86b20c0e8df12abc5fb703",
|
||||
"title": "renders range controls plus icon zoom controls without the user lens selector or visible-duration readout",
|
||||
"mutation": {
|
||||
"before": " }, { signal });",
|
||||
"after": " }, { signal: undefined });",
|
||||
"replacementCount": 1,
|
||||
"additionalPreClosingTagReplacement": false
|
||||
},
|
||||
"intendedGuard": "expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(",
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"jsonSha256": "f7137b440fa63b4b7dfcaa7d733a6836389ff6a3385d768021dcd9cd4a600961",
|
||||
"logSha256": "74011dd8441e744c01a605d359e1f714fef60505e180f010bd7a364962ff038b",
|
||||
"caseStatus": "passed"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"jsonSha256": "e52b536a50fd6fd1dfa8aeb0a71411e67277be3003911be7e592a3d70657c7c7",
|
||||
"logSha256": "e8be8243f0804bb96128cc9b62c55658309267c156cf0bc238081d317f1f3eb5",
|
||||
"caseStatus": "failed",
|
||||
"intendedFailureMessages": [
|
||||
"AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'company-1', \u2026(2) ]\n\nReceived:\n\n 1st vi.fn() call:\n\n [\n \"company-1\",\n- ObjectContaining {\n- \"from\": Any<String>,\n- \"to\": Any<String>,\n+ {\n+ \"from\": \"2026-10-06T05:00:00.000Z\",\n+ \"limit\": 500,\n+ \"offset\": 0,\n+ \"to\": \"2026-10-13T04:59:59.999Z\",\n },\n {\n- \"signal\": Any<AbortSignal>,\n+ \"signal\": undefined,\n },\n ]\n\n\nNumber of calls: 1\n\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:2233:10)\n at Proxy.<anonymous> (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/index.C4tKZ0yW.js:1944:14)\n at Proxy.methodWrapper (file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/chai@6.3.0/node_modules/chai/index.js:1737:25)\n at /Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/ui/src/pages/Timeline.test.tsx:189:37\n at file:///Users/dotta/.codex/worktrees/test-audit-r7-ui/paperclip/node_modules/.pnpm/vitest@5.0.3_@opentelemetry+api@1.9.1_@types+node@24.13.3_jsdom@30.0.1_@noble+hashes@2.4.0__v_wr5mxfuu6kgge6st623q4v5egm/node_modules/vitest/dist/chunks/run.BTlFXlnv.js:2783:20"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"priorEvidenceUnchanged": [
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json",
|
||||
"sha256": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md",
|
||||
"sha256": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-controls.json",
|
||||
"sha256": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript-controls.json",
|
||||
"sha256": "9e4dbdc33806d6fcc912dc3aacc8691a77939e2ce2403032b2a8127c0e407e94"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui-transcript.md",
|
||||
"sha256": "9e4ac8c92a3a9eca727c29a77ae428702d19ecdee0cab0e3dfcda0a36bf004db"
|
||||
},
|
||||
{
|
||||
"file": "doc/plans/2026-10-12-test-audit-r7-ui.md",
|
||||
"sha256": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8"
|
||||
}
|
||||
],
|
||||
"ownerRestoredToBaseGitBytes": true,
|
||||
"productionLinesChanged": 0,
|
||||
"postControlPolish": {
|
||||
"controlCandidateSha256": "e5f6171e4fa9998e4bf14edad445701c5ae003f401875e7fcf278546e3f9cdf7",
|
||||
"polishedCandidateSha256": "8d5909b8aaf4d5d882d2fd1bc9072a2e8bb7fce9c98466ca15a6382e279ec9a7",
|
||||
"difference": "Remove extra blank line before describe closes",
|
||||
"allIntendedGuardStatementsAndLocationsUnchanged": true,
|
||||
"completePolishedFileReplayPassed": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Test audit, Round 7 UI — Timeline default render follow-up
|
||||
|
||||
This isolated follow-up removes **2 registered executions, 2 root renders and 26 net test lines** from `ui/src/pages/Timeline.test.tsx`. It changes no production lines and adds no helper. The pinned audit base is `c39262305b6b48a5decd564af8369083cd972b33`. Prior UI proof remains unchanged. Registration/render reductions are per complete-suite invocation; no measured runtime or CI-speed claim is made.
|
||||
|
||||
## Exact migrations
|
||||
|
||||
| Retired registration | Keeper | Original observations retained |
|
||||
|---|---|---|
|
||||
| `requests the collapsed app sidebar by default` | `renders range controls plus icon zoom controls without the user lens selector or visible-duration readout` | expect(container.querySelector('[data-testid="request-collapsed-sidebar"]')).not.toBeNull(); |
|
||||
| `requests the company timeline without a user lens parameter` | `renders range controls plus icon zoom controls without the user lens selector or visible-duration readout` | expect(mockWorkTimelineApi.get).toHaveBeenCalledWith( "company-1", expect.objectContaining({ from: expect.any(String), to: expect.any(String), }), { signal: expect.any(AbortSignal) }, );; expect(mockWorkTimelineApi.get.mock.calls[0]?.[1]).not.toHaveProperty("userId"); |
|
||||
|
||||
Timeline.tsx calls loadTimelineWindow with the selected company, computed date window and AbortSignal. The default unembedded render requests RequestCollapsedSidebar. All three paired cases use the same emptyTimeline beforeEach result, company context, blank URL search, fresh query client, render and flush. Sidebar-request and complete API argument/no-userId guards move after existing controls assertions. Populated stats, pagination, clipping, overlap and project scope remain distinct.
|
||||
|
||||
The verifier reconstructs the exact candidate text from only the declared removals and insertions, requires byte equality, and compares the entire program AST after the same migrations. Every unrelated source byte, hook, mock, test body and binding remains unchanged, outside the audit receipt’s explicitly declared duplicate-title or blank-line cleanup where applicable. Setup ASTs are equal except the explicitly normalized attachment callback binding described above where applicable. No tests are skipped, softened, suppressed or added. Each file remains included in `@paperclipai/ui` under CI `general-workspaces-a`; Vitest file sharding selects it once across the two shards, with no workflow/config change.
|
||||
|
||||
## Complete-file evidence
|
||||
|
||||
| Run | Registered | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 9 | 9 | 0 | 0 |
|
||||
| Candidate | 7 | 7 | 0 | 0 |
|
||||
|
||||
From `ui/`: `pnpm exec vitest run src/pages/Timeline.test.tsx`.
|
||||
|
||||
All 6 separately selected positive keeper controls passed. Every deliberate behavior mutation failed at the corresponding intended keeper assertion, not setup, timeout or another guard:
|
||||
|
||||
| Mutation | Intended assertion |
|
||||
|---|---|
|
||||
| collapsed | `expect(container.querySelector('[data-testid="request-collapsed-sidebar"]')).not.toBeNull()` |
|
||||
| userId | `expect(mockWorkTimelineApi.get.mock.calls[0]?.[1]).not.toHaveProperty("userId")` |
|
||||
| company | `expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(` |
|
||||
| from | `expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(` |
|
||||
| to | `expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(` |
|
||||
| signal | `expect(mockWorkTimelineApi.get).toHaveBeenCalledWith(` |
|
||||
|
||||
Every mutated production owner was restored byte for byte after each mutation and checked against pinned Git bytes. Name-filtered controls select exactly one keeper; their unselected tests are not full-suite evidence. [Compact controls](2026-10-12-test-audit-r7-ui-timeline-controls.json) preserve source/owner hashes, exact migration map, body verifier digests, complete run/log digests, intended failure receipts and restoration checks. Temporary AST/receipt scripts initially needed corrections for chained assertions or statement position; those verifier attempts produced no accepted test evidence.
|
||||
|
||||
The earlier broad local `pnpm test:run` was intentionally stopped before completion; it is not claimed as a pass. These focused runs do not substitute for pending delivery-head CI. No push, rebase, pull request or merge is included in this packet.
|
||||
|
||||
The mutation controls preceded the final blank-line cleanup. Exact byte reconstruction confirms that every intended migrated guard and its source location stayed unchanged; the final polished complete file passed again. The controls JSON records both candidate hashes.
|
||||
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"audit": {
|
||||
"file": "ui/src/components/task-chat/transcript-adapter.test.ts",
|
||||
"base": "51dc1eb69f5c52d6ef0e29cd2e4061da6c88871f",
|
||||
"originalSha256": "a4b107fd4a12ee5acf55ee2a6aabcf1f8d7f26202b8adf2c533d1929a39be1ef",
|
||||
"candidateSha256": "f79f72eb7d5f43e26fb0ac9f614887786dabea6f82af12e64446aba5fa54e03c",
|
||||
"allOtherProgramAstUnchanged": true,
|
||||
"allOtherSourceBytesUnchanged": true,
|
||||
"formattingOnlyCleanup": "Remove one extra blank line before the enclosing describe closes",
|
||||
"newHelpers": 0,
|
||||
"registeredInvocationsRemoved": 2,
|
||||
"parserCallsRemoved": 2,
|
||||
"netTestLinesRemoved": 15,
|
||||
"migrations": [
|
||||
{
|
||||
"retired": "marks only the message still open at the tail as streaming",
|
||||
"keeper": "tags agent messages streamed inside a live run turn as interstitial",
|
||||
"parserInputsAndFilterEqual": true,
|
||||
"originalKindGuardRetained": true,
|
||||
"exactMigratedStatements": [
|
||||
"if (messages[0].kind !== \"message\" || messages[1].kind !== \"message\")\n return;",
|
||||
"expect(messages[0].streaming).toBe(false);",
|
||||
"expect(messages[1].streaming).toBe(true);"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"parserCallsRemoved": 1
|
||||
},
|
||||
{
|
||||
"retired": "stamps atMs from the first streamed chunk",
|
||||
"keeper": "tags settled-history messages interstitial too, so live and history agree",
|
||||
"parserInputsAndFilterEqual": true,
|
||||
"originalKindGuardRetained": true,
|
||||
"exactMigratedStatements": [
|
||||
"if (messages[0].kind !== \"message\" || messages[1].kind !== \"message\")\n return;",
|
||||
"expect(messages[0].atMs).toBe(Date.parse(TS));",
|
||||
"expect(messages[1].atMs).toBe(Date.parse(\"2026-07-31T12:00:05.000Z\"));"
|
||||
],
|
||||
"registeredInvocationsRemoved": 1,
|
||||
"parserCallsRemoved": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"verifierSha256": "421f9dbbf4e63e1ed307279704a1be2918526bc35109fa15adc88d06668d0c58",
|
||||
"runs": [
|
||||
{
|
||||
"kind": "baseline",
|
||||
"jsonSha256": "2bc04c1382a274652a867e74fd7916721419e325e76c4c364757538a85d35300",
|
||||
"total": 105,
|
||||
"passed": 105,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
{
|
||||
"kind": "candidate",
|
||||
"jsonSha256": "1a7f58af351f003c24491660580c7565fcb6f46203ec641837c06cf39ced9f14",
|
||||
"total": 103,
|
||||
"passed": 103,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
],
|
||||
"mutations": [
|
||||
{
|
||||
"slug": "transcript-non-tail-reset",
|
||||
"owner": "ui/src/components/task-chat/transcript-adapter.ts",
|
||||
"title": "tags agent messages streamed inside a live run turn as interstitial",
|
||||
"sourceSha": "b1895fa6038b65e4d7fab3773dc1ea83ec1a35892d1b001142b14b275e7b1ae1",
|
||||
"replacements": 1,
|
||||
"mutation": {
|
||||
"before": "if (it.kind === \"message\" && idx !== messageIndex) it.streaming = false;",
|
||||
"after": "if (it.kind === \"message\" && idx !== messageIndex) it.streaming = true;"
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"caseStatus": "passed",
|
||||
"jsonSha256": "298db52c7b3cd9636c1dfcc50b403e278bdd35a3e8f72156973bb77b9e1c294f",
|
||||
"logSha256": "8ab8ab05ce7559f4de16ffd01ed68c7ce0aa847401f129bcea8bf449cc2ac206"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"caseStatus": "failed",
|
||||
"intendedAssertionFailure": "AssertionError: expected true to be false // Object.is equality",
|
||||
"jsonSha256": "bc2ffc949c5982f6975a48356631cf044f272950a6567815895e6b312a48dca0",
|
||||
"logSha256": "1eff4e160b2a4237619c4849d5c6896fc8d627ce24c72b18393328aff9d22b4f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"slug": "transcript-message-timestamp",
|
||||
"owner": "ui/src/components/task-chat/transcript-adapter.ts",
|
||||
"title": "tags settled-history messages interstitial too, so live and history agree",
|
||||
"sourceSha": "b1895fa6038b65e4d7fab3773dc1ea83ec1a35892d1b001142b14b275e7b1ae1",
|
||||
"replacements": 1,
|
||||
"mutation": {
|
||||
"before": "atMs: Number.isFinite(atMs) ? atMs : undefined,",
|
||||
"after": "atMs: Number.isFinite(atMs) ? atMs + 1 : undefined,"
|
||||
},
|
||||
"restoredByteForByte": true,
|
||||
"positive": {
|
||||
"exit": 0,
|
||||
"caseStatus": "passed",
|
||||
"jsonSha256": "03eadad261cf00b8294cffe69d300e521c370d5e939c8cfd585c38c55b90ea25",
|
||||
"logSha256": "ff2883bd26a884dacfafb56fa698259aa6023d2526ee3d982ddffc2c6a50d1de"
|
||||
},
|
||||
"negative": {
|
||||
"exit": 1,
|
||||
"caseStatus": "failed",
|
||||
"intendedAssertionFailure": "AssertionError: expected 1785499200001 to be 1785499200000 // Object.is equality",
|
||||
"jsonSha256": "cff42697f265879a73202f701d39fac703788a5bf26ebe236e47e943227a101b",
|
||||
"logSha256": "4736d9727f5398ac9009afd0498ad19529009d7d4eee639743f40b875f9e3103"
|
||||
}
|
||||
}
|
||||
],
|
||||
"allProductionOwnerBytesRestored": true,
|
||||
"productionLinesChanged": 0,
|
||||
"priorUiProofUnchanged": {
|
||||
"commit": "51dc1eb69f5c52d6ef0e29cd2e4061da6c88871f",
|
||||
"files": {
|
||||
"doc/plans/2026-10-12-test-audit-r7-ui.md": "896b9928af85a4c7d588dfca93484980aadb238bd79d8142e671c62f0b191fa8",
|
||||
"doc/plans/2026-10-12-test-audit-r7-ui-controls.json": "0d445f6f066032f2d9cd724525a8d7d2290246cbfeee1f6ab4b44818467af2a7",
|
||||
"doc/plans/2026-10-12-test-audit-r7-ui-appdetail.md": "715f23cd27502c92d0f5d1b83b91bcac03f254c73ab63b44181ae76939805229",
|
||||
"doc/plans/2026-10-12-test-audit-r7-ui-appdetail-controls.json": "7906fde0d62e14a7511b5cf45f864c9386a4c377922333eefc7f7e703c832f6a"
|
||||
}
|
||||
},
|
||||
"discardedAttempt": "Initial candidate clipped the suite-closing statement and failed to parse; regenerated from baseline before accepted full-file run. No result from that attempt counts as proof.",
|
||||
"ciInclusion": "@paperclipai/ui project in general-workspaces-a; complete file remains included; no routing changes",
|
||||
"combinedNetTestLinesRemoved": 386,
|
||||
"combinedRegisteredInvocationsRemoved": 4,
|
||||
"combinedCompleteFileBaselineCases": 333,
|
||||
"combinedCompleteFileCandidateCases": 329,
|
||||
"finalPolishedSourceRun": {
|
||||
"candidateSha256": "f79f72eb7d5f43e26fb0ac9f614887786dabea6f82af12e64446aba5fa54e03c",
|
||||
"jsonSha256": "1cdd87edeed3e2801bcb6aeaab417ae87452bced5daf8f3436ac52bc1ded9aef",
|
||||
"total": 103,
|
||||
"passed": 103,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Test audit, Round 7 UI — transcript follow-up
|
||||
|
||||
This follow-up removes **two duplicate registered invocations, two parser calls, and 15 net test lines (14 from the migrations and one blank-line cleanup)** from `ui/src/components/task-chat/transcript-adapter.test.ts`. It adds no helper and changes zero production lines. Combined with the [first UI batch](2026-10-12-test-audit-r7-ui.md) and [AppDetail migration](2026-10-12-test-audit-r7-ui-appdetail.md), the branch removes **386 net test lines and four registered invocations**. Helper cleanup accounts for 320 lines; the three retirement/migration batches account for 65, with one additional blank-line cleanup. No wall-time or CI-speed claim is made.
|
||||
|
||||
The transcript audit base is `51dc1eb69f5c52d6ef0e29cd2e4061da6c88871f`. This file still has its original Round 6 parent bytes at that base. Prior UI tests and reports remain unchanged.
|
||||
|
||||
## Exact migrations
|
||||
|
||||
| Retired registration | Retained keeper | Exact observations carried |
|
||||
|---|---|---|
|
||||
| `marks only the message still open at the tail as streaming` | `tags agent messages streamed inside a live run turn as interstitial` | Original kind-narrowing guard, first message streaming=false, tail message streaming=true. |
|
||||
| `stamps atMs from the first streamed chunk` | `tags settled-history messages interstitial too, so live and history agree` | Original kind-narrowing guard, first timestamp Date.parse(TS), second timestamp Date.parse("2026-07-31T12:00:05.000Z"). |
|
||||
|
||||
Within each pair, the stream array, runId, running state, parser call and message-filter predicate match exactly. The live retired case uses an intermediate items variable; its parser expression and subsequent filter match the keeper's direct chain. Both scopes resolve stream to the same enclosing constant. The settled pair already uses the same direct chain. No hooks, mocks or producer state differ between paired cases. Existing keeper length and interstitial/settled-streaming assertions remain.
|
||||
|
||||
Live running=true and settled running=false remain distinct. **`marks no message streaming when the tail is a tool call` remains byte-identical.** Every other case remains byte-identical, including saved plans, final replies, provider activity, native response-wake authority, thinking state, tool identity, usage, grouping and timeline boundaries.
|
||||
|
||||
TaskChatThread calls the actual transcript parser and settledRunChildren for its run segments. The parser assigns each message timestamp from its first transcript entry and resets non-tail streaming state after the full input. The owner is `ui/src/components/task-chat/transcript-adapter.ts`. The complete suite remains included in `@paperclipai/ui` / `general-workspaces-a`; no CI routing or skip policy changes.
|
||||
|
||||
## Evidence
|
||||
|
||||
| Complete transcript file | Registered cases | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Baseline | 105 | 105 | 0 | 0 |
|
||||
| Candidate | 103 | 103 | 0 | 0 |
|
||||
|
||||
Run from `ui/`: `pnpm exec vitest run src/components/task-chat/transcript-adapter.test.ts`.
|
||||
|
||||
The audit checks parser inputs and filter ASTs, inserts the original three statements from each retired body, and compares the entire candidate/original program outside those declared migrations. It also reconstructs candidate text from exactly the two removals and two insertions, requiring byte equality. All other source bytes stay unchanged outside the declared blank-line cleanup before the enclosing describe closes. The initial edit clipped the enclosing suite boundary and failed to parse; the temporary codemod was corrected and the candidate regenerated from pinned source before the accepted run. That attempt supplies no passing evidence.
|
||||
|
||||
Both positive keeper controls passed. Both deliberate production regressions failed at the intended migrated assertion:
|
||||
|
||||
| Regression | Intended failure |
|
||||
|---|---|
|
||||
| Leave completed non-tail messages streaming | Live keeper expected false and received true for messages[0].streaming. |
|
||||
| Shift parsed message timestamps by one millisecond | Settled keeper expected 1785499200000 and received 1785499200001 for messages[0].atMs. |
|
||||
|
||||
Neither control failed on setup, timeout or a different assertion. The production owner was restored byte for byte after each mutation and matched pinned Git bytes. Name-filtered controls select one keeper; unselected cases are not full-suite evidence.
|
||||
|
||||
[Transcript controls](2026-10-12-test-audit-r7-ui-transcript-controls.json) retain the exact source hashes, body-audit digest, migration map, complete-file counts, mutation descriptions, intended failures, run/log digests, prior proof hashes and byte-restoration receipts. Temporary raw verifiers/logs are identified by digests but not preserved in full. Across the three separate baseline/candidate packets, all thirteen complete files total 333 baseline cases and 329 candidate cases. After removing one extra blank line, a final complete-file replay passed all 103 cases on the polished source bytes. A delivery-head combined replay remains separate PR evidence.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Test audit, Round 7 UI — preservation report
|
||||
|
||||
This UI batch removes **364 net test lines and one registered invocation** from three files. It changes **zero production lines**. Of the 364 lines, 320 come from helper cleanup and 44 from retiring the duplicate after its fixture/assertion migration. The helper cleanup reduces maintenance code; it makes no wall-time or CI-speed claim. The one retired invocation avoids one duplicate rendered roster scenario.
|
||||
|
||||
The branch starts at pinned parent `e868e6645ce0d598d756d3458f0b3d71afd6db4a`, the head of [Round 6, PR #16047](https://github.com/paperclipai/paperclip/pull/16047). It follows OpenClaw's [test-audit skill](https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/SKILL.md) and [campaign process](https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/CAMPAIGN.md). All ten Round 6 test files were excluded. Prior cleanup remains in the parent history.
|
||||
|
||||
## Scope and cost
|
||||
|
||||
| File | Net test lines removed | Source helper calls expanded | Registered invocations removed |
|
||||
|---|---:|---:|---:|
|
||||
| `ui/src/pages/InviteLanding.test.tsx` | 132 | 18 | 0 |
|
||||
| `ui/src/pages/Agents.test.tsx` | 136 | 16 | 1 |
|
||||
| `ui/src/components/Layout.test.tsx` | 96 | 26 | 0 |
|
||||
| Total | 364 | 60 | 1 |
|
||||
|
||||
Each helper contains one synchronous `root.render` statement. It creates the same complete JSX subtree on each call. The invitation helper also creates a fresh initial-route array. Helpers accept the existing root and query client. They create no root or query client, add no await, and contain no assertion. The roster helper retains QueryClientProvider → ToastProvider → Agents. The distinct table render of production and streamlined AgentList remains inline. Every original outer `act`, flush, timer, input, assertion, cleanup and provider order remains, outside the single declared migration below.
|
||||
|
||||
The audit read the actual invitation, roster and shell owners, their App routes, production-shell/roster counterparts, current tests and source history. It inspected analogous invitation-access, shell-route, company-selection, sidebar, plugin-overlay and roster surfaces. Permission gates, membership/account cache checks, keyboard/hover controls, accessibility, pending state and routing cases retain their own registrations.
|
||||
|
||||
## Exact retirement and keeper
|
||||
|
||||
Retired: Agents `uses the built-in agents route segment as the built-in filter`.
|
||||
|
||||
Keeper: Agents `shows and filters built-in agents when the experimental flag is enabled`.
|
||||
|
||||
Both cases came from commit `8b6a06ee254` and use `/agents/builtin`, the enabled built-in flag, and the same built-in and regular agent/org rows. The keeper now copies the retired case's exact setup, including the populated BuiltInAgentState.agent pointing to the same built-in object as the agent list. It also carries the retired `1 agent` assertion. Its original checks remain: company-scoped built-in API request, Built-in tab text, included built-in name, excluded regular name, absent old badge, and no redirect to the all-agents route.
|
||||
|
||||
The former keeper's null nested agent fixture does not supply a separate ready/list behavior: the actual roster uses entry.agentId and entry.status on that path. The setup modal is not mounted for a ready entry. Copying the retired populated fixture preserves the stronger keeper's observations while retaining every retired observation. Other built-in enable/disable and production-versus-streamlined registrations remain.
|
||||
|
||||
No other case retires. In particular, invitation error presentation remains distinct from its accessibility guard and membership/cache timing cases. Layout route variants retain all original parameter rows and loop iterations. Roster environment-loading, single-environment, disabled-feature and fallback-provider cases remain separate.
|
||||
|
||||
## Complete-file evidence
|
||||
|
||||
| Run | Complete files | Passed cases | Failures | Skips |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Original changed owners | 3 | 71 | 0 | 0 |
|
||||
| Original owner siblings | 8 | 79 | 0 | 0 |
|
||||
| Candidate changed owners | 3 | 70 | 0 | 0 |
|
||||
| Final candidate owners and siblings | 11 | 149 | 0 | 0 |
|
||||
|
||||
The original total is 150, compared with candidate 149. These are actual Vitest registrations, including table rows. The sixty helper counts are source call expansions, not retired executions. Complete-file runs have no name filter. Separate name-filtered mutation controls intentionally select one keeper and do not count their unselected cases as complete-suite proof.
|
||||
|
||||
Commands run from `ui/` use `pnpm exec vitest run` followed by the complete file paths. Changed-owner paths are `src/pages/InviteLanding.test.tsx`, `src/pages/Agents.test.tsx` and `src/components/Layout.test.tsx`. Siblings are SidebarAgents, ActiveAgentsPanel, access/InvitesSection, useCompanyInviteAccess, shell-navigation, company-selection, PluginAppShellOverlays and SidebarShell.
|
||||
|
||||
Dependencies were installed offline with the frozen lockfile in this checkout. Initial dependency-link exploration and two startup-error attempts were discarded; none counts as baseline proof. Accepted runs use this checkout's workspace packages.
|
||||
|
||||
## Expansion and intended regressions
|
||||
|
||||
The verifier expands the actual helper bodies at all sixty call sites, then compares complete original and candidate ASTs. They match outside removal of the named duplicate, its exact setup migration, and insertion of its count assertion into the keeper. Location/comment/raw-format metadata and whitespace-only JSX text are ignored; executable values and statement order are retained. Root/client argument names are checked at each call. Independent review also checks lexical binding, provider nesting and fresh JSX/route values; AST substitution alone does not establish binding correctness.
|
||||
|
||||
All three positive keeper controls passed. All deliberate production regressions failed at the intended retained assertion:
|
||||
|
||||
| Mutation | Intended failing keeper observation |
|
||||
|---|---|
|
||||
| Return all agents from the built-in filter | Migrated `1 agent` count guard. |
|
||||
| Omit actual invite auth aria-invalid state | Email input's fixed `aria-invalid === "true"` guard. |
|
||||
| Bypass the settings sidebar branch | Visible Company settings sidebar guard. |
|
||||
|
||||
These failures are direct assertions, not timeout/setup/worker errors. Each owner was restored byte for byte and checked against the pinned Git source. The final diff has no production changes. Mutation controls use fixed literal expected values from the original tests; helpers supply no oracle.
|
||||
|
||||
## Provenance and limits
|
||||
|
||||
[Controls JSON](2026-10-12-test-audit-r7-ui-controls.json) records exact original/candidate source hashes, all sixty expansion counts, the retirement map, decisions for all 71 original owner registrations, complete-file results, intended failures, raw-run digests, and source-restoration receipts. Raw expansion scripts and logs were temporary audit packets. Digests identify them but do not preserve their full payloads.
|
||||
|
||||
The required token gate and diff whitespace check passed. Workspace typecheck/build, broad default tests and latest-head CI/Greptile status belong in the PR so this preservation report does not confuse focused proof with whole-workspace success. No tests, workflow, dependency, production, retry, timeout or skip policy was weakened to make checks pass.
|
||||
|
||||
## Integration with current master
|
||||
|
||||
During replay after #16047 landed, the retired Priority-filter case had gained
|
||||
three assertions for the agent-created-task checkbox. Those assertions moved
|
||||
to the retained same-input grid/render case, which already enables that filter.
|
||||
The label and initial unchecked state remain covered; no new contract is removed.
|
||||
Fresh PR CI validates the integrated source. Historical controls above remain
|
||||
evidence for their recorded revisions.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Test audit R9: owner-level duplicate consolidation
|
||||
|
||||
Base: `77408fa6c3d7c916d7160760eae2144aaa0f8fe7` (#16072). This batch removes nine registered tests and 79 net test lines. It changes no production code. No CI timing claim was measured.
|
||||
|
||||
| Retired proof | Keeper and preserved contract | Owner mutation caught by keeper |
|
||||
| --- | --- | --- |
|
||||
| CLI ZIP: separate binary blob and invalid UTF-8 fallback cases | Archive round-trip keeps blob bytes and invalid UTF-8 note bytes exact; valid UTF-8 blob input is covered there too. | Bypass blob storage or decode invalid UTF-8 lossily; exact round-trip fails. |
|
||||
| Daytona: first execute creates a session before the second reuses it | Persistent-shell case checks one created session, a `paperclip-` id, and the same session id for both commands. | Break the id prefix or session-store hit; the keeper fails. |
|
||||
| Shared account handle: plain `acct-42` acceptance | Whitespace-rejection case retains the exact accepted input and checks it is not trimmed. | Return null for the accepted identifier; the keeper fails. |
|
||||
| Loopback bind: bare `pnpm dev` rewrite | Loopback-preset case retains the same bare command and checks the rewritten command plus preset behavior. | Stop rewriting the bare command; the keeper fails. |
|
||||
| Native runner: generic remote-runner acceptance | Remote Dot capability case retains generic-remote acceptance alongside the Dot-specific rejection and acceptance controls. | Require Dot capability for generic remote runners; the keeper fails. |
|
||||
| Auth route: null Sentry DSN in an unset configuration | Full persisted-user session response already checks `sentryDsn: null` and `sentryEnvironment: null`. | Return an empty string for the DSN; full response equality fails. |
|
||||
| Discord registration: successful registration before runtime enablement | The 503 refresh case first proves successful registration, capabilities, callback, persisted receipt, token exclusion, and idempotent reconciliation. | Persist the bot token in the receipt; the migrated token-exclusion assertion fails. |
|
||||
| Slack OAuth: organization grant over-grant provenance | The no-scope refresh case first checks the original database grant before refresh, then retains the post-refresh warning. | Omit OAuth provenance when creating the initial organization grant; the pre-refresh database assertion fails. |
|
||||
|
||||
Each mutation was tested against the intended retained assertion. Production owners were restored byte-for-byte after controls. Complete owner candidates passed before restoration. The modified files were then replayed on this exact base in two runs: six owners passed 1,001 tests; after building the local plugin SDK dependency, the two server owners passed 1,691 tests. The final combined total is 2,692 passing tests across all eight changed owners. `git diff --check` passes.
|
||||
|
||||
The reductions consolidate proof at an existing owner and keep the distinct boundary controls. They do not claim a specific CI-time reduction; the focused suites still exercise their retained inputs and assertions.
|
||||
@@ -0,0 +1,57 @@
|
||||
# IssueChatThread test audit: round 18
|
||||
|
||||
Exact parent: #16091, `dc6cfe7512d5dcb25aa9df5b918d46f35cd2eb22`.
|
||||
|
||||
The complete IssueChatThread owner has 89 declarations and 99 registered rows before this change. The candidate has 87 declarations and 97 rows. All rows and parameter tables outside the two retirements remain intact. The paired TaskChatThread owner has 107 declarations and 225 rows. It has no qualifying cut. Native and legacy modes have separate owners and contracts.
|
||||
|
||||
## Retirement map
|
||||
|
||||
| Retired invocation | Retained invocation | Preserved proof and phase |
|
||||
| --- | --- | --- |
|
||||
| omits the bottom spacer when the composer is hidden | drops the count heading and does not use an internal scrollbox | Same fresh root and renderIssueThread(root, { showComposer: false }); migrate the spacer query and null assertion after initial render, before unmount. |
|
||||
| keeps the composer floating with a capped editor height | does not restore the composer viewport for passive live updates by default | Same fresh root and renderIssueThread(root); migrate dock, composer and editor queries and every observation after initial render, before the keeper's liveRuns rerender. |
|
||||
|
||||
The AST comparison permits only these two declaration removals and exact observer moves. Helpers, hooks, provider inputs, fixtures, cleanup, actions, and parameter rows match the parent. The final runtime case names match the baseline minus the two named retirements. The active composer viewport case stays separate because it changes preservation state and snapshot position. No helper was added.
|
||||
|
||||
## Replay
|
||||
|
||||
All full-file runs have zero failed or skipped cases.
|
||||
|
||||
| Phase | Passed cases |
|
||||
| --- | ---: |
|
||||
| Parent baseline | 99 |
|
||||
| Candidate | 97 |
|
||||
| Original test revert | 99 |
|
||||
| Restored candidate after controls | 97 |
|
||||
| Unchanged TaskChatThread sibling | 225 |
|
||||
|
||||
Command: `pnpm exec vitest run ui/src/components/IssueChatThread.test.tsx --reporter=json --outputFile=<phase>.json`. The sibling uses the same command with TaskChatThread.test.tsx.
|
||||
|
||||
## Production controls
|
||||
|
||||
Each control changes the real IssueChatThread.tsx owner. Each selected keeper fails once at the migrated assertion below. Other cases are filtered with Vitest -t; no test-source skip was added.
|
||||
|
||||
| Control | Retained guard | Candidate line |
|
||||
| --- | --- | ---: |
|
||||
| hidden-spacer-leak | `expect(spacer).toBeNull()` | 425 |
|
||||
| missing-dock | `expect(dock).not.toBeNull()` | 3938 |
|
||||
| dock-sticky | `expect(dock?.className).toContain("sticky")` | 3939 |
|
||||
| dock-bottom-offset | `expect(dock?.className).toContain("bottom-(--sz-calc-8)")` | 3940 |
|
||||
| dock-layer | `expect(dock?.className).toContain("z-20")` | 3941 |
|
||||
| missing-composer | `expect(composer).not.toBeNull()` | 3946 |
|
||||
| composer-radius | `expect(composer?.className).toContain("rounded-md")` | 3947 |
|
||||
| composer-padding | `expect(composer?.className).toContain("p-(--sz-15px)")` | 3949 |
|
||||
| editor-height-cap | `expect(editor?.dataset.contentClassName).toContain("max-h-(--sz-28dvh)")` | 3954 |
|
||||
| editor-overflow | `expect(editor?.dataset.contentClassName).toContain("overflow-y-auto")` | 3955 |
|
||||
| editor-min-height | `expect(editor?.dataset.contentClassName).not.toContain("min-h-(--sz-72px)")` | 3956 |
|
||||
| editor-drop-target | `expect(editor?.dataset.fileDropTarget).toBe("parent")` | 3957 |
|
||||
|
||||
The radius positive and negative guards cover the same behavior. The wrong-radius control fails the stronger positive guard; no separate negative-radius mutation is claimed. Production source was restored after every control. SHA256 before and after: `23624e4e25b60c9ce8cc91fa548bfd46e18666723e5aa8d9f882846ded3bdf82`. Candidate test SHA256: `d2361cefd481433b7b5dabd0066b09fa6856f38e3610f910e02e8aab2b3023e3`.
|
||||
|
||||
## Scope and checks
|
||||
|
||||
The test diff adds 29 lines and removes 55 lines: 26 net test lines removed. It removes two registered executions and two fresh initial root/component renders. No measured runtime savings are claimed. Production LOC changes: zero. Keyboard, hash/manual scroll, permissions, retry, persistence, live update, file input, and parameterized cases remain distinct.
|
||||
|
||||
Focused UI typecheck, token gate, and diff check pass. The first UI typecheck could not resolve generated plugin SDK declarations in the fresh checkout. Building the SDK and shared dependency restored the declarations; the rerun passed. Full repository gates are reported in the PR; this focused proof does not claim they passed.
|
||||
|
||||
Local replay JSON, logs, complete declaration/runtime ledgers, exact AST proof and mutation failures are preserved under /private/tmp/issue-chat-*. The concise evidence above is included here so the retirement map and observed controls remain reviewable without local artifacts.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Execution-target sandbox test audit: round 19
|
||||
|
||||
Exact parent: #16092, `8e52b1935d8982fae24158fc94dacf218c023ee5`.
|
||||
|
||||
Read the complete 6,409-line test owner, all helpers, hooks, parameter rows and assertions. The declaration ledger has 129 declarations and 148 registered cases. The candidate has 128 declarations and 147 cases.
|
||||
|
||||
## Retirement map
|
||||
|
||||
Retire `bounds the pre-READY buffer growth-copy work by the bytes received`. Keep `bounds the pre-READY newline-scan work by the bytes received`.
|
||||
|
||||
Move `resetBufferGrowthCopyUnits()` before the keeper's bridge bring-up. Move the exact `readBufferGrowthCopyUnits()` and `copyUnits <= 4 * totalBytes` observation into its try block after the scan bound. Keep both bridge-presence/open-count guards and the exact bridge.stop/api.close finally block.
|
||||
|
||||
Both cases have the same recording API server, buffer cap, 64-byte chunks, chunk count, byte count, runner callback, recorder, provider target, readiness timeout and bridge options. The callback emits the same chunks in the same order. The setup, callback, bridge call and finalizer AST match after normalizing only two namespace labels. Fresh temp prefixes and runIds differ. They select fresh isolated paths and run namespaces; neither case dispatches a request or observes those names. They have the same path shape and valid runId character class. These differences are expressly assessed as isolation, not described as literal byte-equivalent inputs.
|
||||
|
||||
The two reset/read pairs use independent numeric accounting variables. Production never reads the counters to select behavior. Neither reset changes the buffer, gate, callbacks or lifecycle. Both bounds can observe one fresh bridge execution.
|
||||
|
||||
The whole-program AST permits only one declaration removal and the exact reset/read/bound migration. Helpers, hooks, fixtures, parameter rows, all other declarations and the separate many-small-chunk cap case match the parent. The restored runtime case list equals the baseline minus the named retirement.
|
||||
|
||||
## Full owner replay
|
||||
|
||||
All complete-file runs have zero failures and skips.
|
||||
|
||||
| Phase | Passed cases |
|
||||
| --- | ---: |
|
||||
| Exact parent baseline | 148 |
|
||||
| Candidate | 147 |
|
||||
| Original test revert | 148 |
|
||||
| Restored candidate after controls | 147 |
|
||||
|
||||
Command: `pnpm exec vitest run packages/adapter-utils/src/execution-target-sandbox.test.ts --reporter=json --outputFile=<phase>.json`.
|
||||
|
||||
## Actual production-owner controls
|
||||
|
||||
Each control selects the keeper with Vitest `-t`. It fails once at the intended assertion. Other rows are selector-filtered; no source skips are added. Neither control changes the counters or their test-only readers.
|
||||
|
||||
| Control | Actual owner change | Intended failure |
|
||||
| --- | --- | --- |
|
||||
| Newline rescan | Search from offset zero for retained buffers up to 32 KiB, then use the original cursor. This makes actual indexOf work repeat on retained bytes. | Retained scan bound, candidate line 4591: 8,638,528 scan units exceeded 1,065,216. |
|
||||
| Per-fragment growth | In appendReadinessBytes, allocate exactly the needed length for the first 32 KiB, then use the original doubling. This makes actual storage.copy work repeat. | Migrated copy bound, candidate line 4593: 8,863,744 copied bytes exceeded 1,065,216. |
|
||||
|
||||
The bounded controls avoid excessive stress while introducing a real buffer/cursor regression. The owner is restored after each control. SHA256 before and after: `9f61140f7c27698e7837b97dd78f9b3056045a41527da06bb0cee9f11c9f62a6`.
|
||||
|
||||
Candidate test SHA256: `270701dcf459289a6d09ac88c74cf9ccfb41ab8323d75eecbc9ef683072471ee`.
|
||||
|
||||
An initial harness refused the growth declaration because the text occurred in two functions. It applied no growth mutation. The final control binds the exact appendReadinessBytes function. Both final controls caught the intended regressions and restored the owner.
|
||||
|
||||
## Scope and checks
|
||||
|
||||
The test diff adds one line and removes 38 lines: 37 net test lines removed. It removes one registered execution and one real bridge bring-up. No measured time savings are claimed. Production LOC changes: zero.
|
||||
|
||||
Retain all transport, callback, command, host/sandbox path, credential, platform, readiness, timeout, security and lifecycle distinctions. Pre-preface overflow and post-preface overflow remain separate. Safe GET and committed POST remain separate. The independent many-small-chunk buffer-cap case remains intact.
|
||||
|
||||
Focused adapter-utils typecheck and build, token gate, and diff check pass. No repeated broad run was started: the exact parent's full local attempt failed in its server-only phase with unchanged host/server failures before workspace projects ran. Its focused UI proof and all 53 remote checks passed. This candidate does not claim a full local suite pass.
|
||||
|
||||
Full replay JSON/logs, exact declaration/runtime ledger, AST/input proof, mutation failures, original source and restored hash receipts are retained under /private/tmp/execution-target-*. This report includes the retirement map, exact counts and observed controls for review without those local files.
|
||||
@@ -0,0 +1,304 @@
|
||||
{
|
||||
"auditBaseline": "a090035cafb29c06f6fca2dbe43561ad866b7c1b",
|
||||
"stackBase": "58c5c4aa241c85f4665a3159c192888bbb2a5cfa",
|
||||
"auditCodeCommit": "d8de9422f0703d4508a9dc880b67a07f13e0f110",
|
||||
"savings": {
|
||||
"netTestLinesRemoved": 2831,
|
||||
"redundantExecutionsRemoved": 12,
|
||||
"productionLinesRemoved": 0
|
||||
},
|
||||
"declarations": {
|
||||
"total": 995,
|
||||
"R": 619,
|
||||
"F": 2,
|
||||
"C": 370,
|
||||
"D": 4
|
||||
},
|
||||
"actualExpandedHelperCalls": 625,
|
||||
"packets": {
|
||||
"adapter": {
|
||||
"declarationDecisions": {
|
||||
"R": 184,
|
||||
"F": 1,
|
||||
"C": 92,
|
||||
"D": 2
|
||||
},
|
||||
"actualExpandedHelperCalls": 134,
|
||||
"manifestSha256": "cb6f36fa4b836b7c16e23ce6f62326a15e27eed407742b0e09d4779f40f7c7b1",
|
||||
"declarationLedgerSha256": "89bc46ce2eb796524adfdeaaa50c382b39ee7c555224fe2d4f0cb1be8ba036b1",
|
||||
"expansionProofSha256": "8b3883f723147d2bcd53e0288d76fbf47b817d187a8fd79e4f85b258a840ad16"
|
||||
},
|
||||
"heartbeat": {
|
||||
"declarationDecisions": {
|
||||
"R": 141,
|
||||
"F": 0,
|
||||
"C": 130,
|
||||
"D": 0
|
||||
},
|
||||
"actualExpandedHelperCalls": 282,
|
||||
"manifestSha256": "85934e19feb6aed9247c09ff2057ab4acd1b9d5951e534047c597cbcc936417b",
|
||||
"declarationLedgerSha256": "a6a5cd83ecc969c53e662a7e10b715741a11efc97bc28e52ab5e30aec8313d60",
|
||||
"expansionProofSha256": "56e5b59358099059bc48204f74509a2bff8256ae91deeeea0f960575b0d221c1"
|
||||
},
|
||||
"ui": {
|
||||
"declarationDecisions": {
|
||||
"R": 276,
|
||||
"F": 1,
|
||||
"C": 147,
|
||||
"D": 1
|
||||
},
|
||||
"actualExpandedHelperCalls": 209,
|
||||
"manifestSha256": "0dd858bc5b8b2c3c23a6772ae0fea5087191af9a549a36d9db8f23be8e539127",
|
||||
"declarationLedgerSha256": "ae284c6efbf141895df4dc54de49013f65eac40c9389ec29914089e9c29c739d",
|
||||
"expansionProofSha256": "8bd16e860d15f25defa34dd49b6d7e48ac7bf1e0dccaa4c6bae5979562d60ad4"
|
||||
},
|
||||
"capability": {
|
||||
"declarationDecisions": {
|
||||
"R": 18,
|
||||
"F": 0,
|
||||
"C": 1,
|
||||
"D": 1
|
||||
},
|
||||
"actualExpandedHelperCalls": 0,
|
||||
"manifestSha256": "72fdcb9282dd7875bbc6df559fa8896c856151e60843657cdfe70054cd8b8924",
|
||||
"declarationLedgerSha256": "ed2a5abcbba3fd17deca8b8158a4e90f3eeb8abcc8af0061a65dda63c3edf884"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"path": "packages/adapter-utils/src/execution-target-sandbox.test.ts",
|
||||
"originalSha256": "cf7db1f718e260cc62da670a48b5d35715bbb25cd7312e49433d73e2bbe1800c",
|
||||
"executedCandidateSha256": "a3c8b8749df78efef34e9df476114ad09a7cbc8dd14e8ddbe50a40011ccde350",
|
||||
"finalSha256": "7c1e921b0dcc3fce8bb14df98c739a35d938f75795986fcaf8a9f7dac70134be",
|
||||
"testLinesRemoved": 191
|
||||
},
|
||||
{
|
||||
"path": "packages/adapter-utils/src/sandbox-callback-bridge.test.ts",
|
||||
"originalSha256": "2f687f0a26f0e1e32a92a464be46d5bfc6a3f821dda4b85d3cdaf7cef30f1216",
|
||||
"executedCandidateSha256": "bb0f00b084ab342472ef72a1a4a20a03170a26ff7a66b8e7b571f92f9921402f",
|
||||
"finalSha256": "79173674831037bde5938e1b2c37122f70bf4f63bd6a0df1a9ae2b2d4f94a25c",
|
||||
"testLinesRemoved": 214
|
||||
},
|
||||
{
|
||||
"path": "packages/adapter-utils/src/sandbox-managed-runtime.test.ts",
|
||||
"originalSha256": "e5f81914f167dd36879823e25807e1f0098694f1748b34371f37d0d5c00597d6",
|
||||
"executedCandidateSha256": "0751d74fd6b4396f5b84206ce33c45a680913c256ed3a9a97a305014d6046216",
|
||||
"finalSha256": "1c575bfd8e391886a5a23facfa19ed149732139c908f19fb6fad650083270e25",
|
||||
"testLinesRemoved": 470
|
||||
},
|
||||
{
|
||||
"path": "server/src/__tests__/environment-execution-target-capabilities.test.ts",
|
||||
"originalSha256": "7b89d2107451a0efc6e78664f9e8305cba940008a058d076d83963cf6cda4cbb",
|
||||
"executedCandidateSha256": "b5da63ebba187ea7e9ac49524c9e5f6acbb149ab2c635ded2964a26b22e085ed",
|
||||
"finalSha256": "b5da63ebba187ea7e9ac49524c9e5f6acbb149ab2c635ded2964a26b22e085ed",
|
||||
"testLinesRemoved": 22
|
||||
},
|
||||
{
|
||||
"path": "server/src/__tests__/heartbeat-comment-wake-batching.test.ts",
|
||||
"originalSha256": "21d7fc3642316f55c2eff83c482aab5649673cbe8cf82fe4ed111e1cae4ba5be",
|
||||
"executedCandidateSha256": "288cf2d50a3a31133889b9c2c7b65d3a5eae4ef198b27162583016558a4afebd",
|
||||
"finalSha256": "288cf2d50a3a31133889b9c2c7b65d3a5eae4ef198b27162583016558a4afebd",
|
||||
"testLinesRemoved": 688
|
||||
},
|
||||
{
|
||||
"path": "server/src/__tests__/heartbeat-process-recovery.test.ts",
|
||||
"originalSha256": "33b4140b1dff1ad5ed916463e997742c64dfa4b5e66e65c5658c22aa86ac9dc2",
|
||||
"executedCandidateSha256": "bf7c888eaba44a56d12823bb948026d5db44f2249a75a6cf083343edbd7c0673",
|
||||
"finalSha256": "bf7c888eaba44a56d12823bb948026d5db44f2249a75a6cf083343edbd7c0673",
|
||||
"testLinesRemoved": 684
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/IssueProperties.test.tsx",
|
||||
"originalSha256": "8d604724e4c9c423b560d13dae6e939113f248b050905542970ee0660dfeab98",
|
||||
"executedCandidateSha256": "2dd0c39f6be93e054ed7691984b68c25c32b9313dcfdacac8576d6e02d97128b",
|
||||
"finalSha256": "2dd0c39f6be93e054ed7691984b68c25c32b9313dcfdacac8576d6e02d97128b",
|
||||
"testLinesRemoved": 26
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/TaskChatThread.test.tsx",
|
||||
"originalSha256": "fc4707ae6a4b16ebf887b277d44f7432c7be3644b8eeb9388caa19a83405ffe4",
|
||||
"executedCandidateSha256": "aa92b96c1f6fc2a87c4f99bdb4fc76eb8f839bcceb664114c11b3ee87dd392fa",
|
||||
"finalSha256": "aa92b96c1f6fc2a87c4f99bdb4fc76eb8f839bcceb664114c11b3ee87dd392fa",
|
||||
"testLinesRemoved": 250
|
||||
},
|
||||
{
|
||||
"path": "ui/src/components/task-chat/TaskChatComposer.test.tsx",
|
||||
"originalSha256": "3443b48fa7d7d4d11942ffe283ebd6d441e5bc2aa4cb5f30c87f6a98050b0a48",
|
||||
"executedCandidateSha256": "93a07bec0a1d6d449754bbf856d557ea283c20e71ae8e5c1ff29ff2b53abd238",
|
||||
"finalSha256": "93a07bec0a1d6d449754bbf856d557ea283c20e71ae8e5c1ff29ff2b53abd238",
|
||||
"testLinesRemoved": 92
|
||||
},
|
||||
{
|
||||
"path": "ui/src/pages/apps/AppsConnect.test.tsx",
|
||||
"originalSha256": "7a3164ecef2d66c8a84d12cde1a0d7a0888ada5ca9c2f851e2a65420749091c7",
|
||||
"executedCandidateSha256": "fcd9b439de18c5435b3ea6c6fc0f3de73f5880533f82652508b41cfaecc471b6",
|
||||
"finalSha256": "fcd9b439de18c5435b3ea6c6fc0f3de73f5880533f82652508b41cfaecc471b6",
|
||||
"testLinesRemoved": 194
|
||||
}
|
||||
],
|
||||
"completeFileRuns": [
|
||||
{
|
||||
"run": "baseline",
|
||||
"completeFiles": 30,
|
||||
"passed": 1841,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"jsonSha256": "98cf86a556acc308c9d9dfd1b5fa9d32b7156be6b92f77cd820608120c4875a3",
|
||||
"wallSeconds": 692.28
|
||||
},
|
||||
{
|
||||
"run": "extra-baseline",
|
||||
"completeFiles": 9,
|
||||
"passed": 219,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"jsonSha256": "bdba4be5d0390309f2e90cfe6f83f350c6e0cb69f76d1be0d6b9d84d02a0454d",
|
||||
"wallSeconds": 29.05
|
||||
},
|
||||
{
|
||||
"run": "candidate",
|
||||
"completeFiles": 30,
|
||||
"passed": 1831,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"jsonSha256": "1ca64e93260e51d2621dcb5e6ac996554b87db871be44643014ecc3b05d126be",
|
||||
"wallSeconds": 429.46
|
||||
},
|
||||
{
|
||||
"run": "extra-candidate",
|
||||
"completeFiles": 9,
|
||||
"passed": 217,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"jsonSha256": "817daf21df1ffa8a3daad411ad14210a15b445e14f0a93392595f5ac3a84a420",
|
||||
"wallSeconds": 15.48
|
||||
}
|
||||
],
|
||||
"mutationControls": {
|
||||
"acceptedIntendedFailures": [
|
||||
{
|
||||
"id": "F-actual-bridge-token-log-exclusion-retained",
|
||||
"testTitle": "keeps sentinel route, query, body, and tokens off the http2 telemetry and logs",
|
||||
"guard": "actual issued bridge-token exclusion from logDump",
|
||||
"jsonSha256": "feaa61e1d9cebc1dbb2190dbed10c77901e4bd2c24e59871a11198d8dbf0d623",
|
||||
"logSha256": "2b5ef18cb9ae39c203318fea74b448ab4752b26b50815afbbaf553d20d604def",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "C-missing-preface-cleanup-oracle-retained",
|
||||
"testTitle": "test_fallback_to_queue_v1_happens_at_most_once_per_run",
|
||||
"guard": "provider cleanup count before finally",
|
||||
"jsonSha256": "d593dbe4bdfcc23958c1ebc5dabc7d62d5c9b042d1133ff5648ed98285c2173a",
|
||||
"logSha256": "0eeea70f7b19da9b992caab26e32b393f8caad7b6a2673050a3153576c3f72cb",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "C-host-READY-newline-compatibility-retained",
|
||||
"testTitle": "test_http2_gateway_writes_no_frame_between_ready_and_the_preface",
|
||||
"guard": "independent host READY newline compatibility",
|
||||
"jsonSha256": "4207fb4cbc0fd5dc6f51213e045f913ba7ec711e50132e4238a5ff1ae367ba82",
|
||||
"logSha256": "894e44ae03faf6c35fd239e94a1cb469efbc4ac26373231ec5d1fdc3bf5db971",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "C-real-scratch-wipe-retained",
|
||||
"testTitle": "the workspace wipe keeps an in-flight scratch tarball at the root but removes a stale sibling",
|
||||
"guard": "seeded scratch-file .resolves bytes preservation",
|
||||
"jsonSha256": "f2c2eb5d881f8bf294a6cc7344836315c0af53b3222facf0a523420a551f5671",
|
||||
"logSha256": "2227abb5cef178d176dc3161e4ead11e9a0be84b85982f5b3a62b3c7181d3fb9",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "self-authored-comment-liveness-bypass-retained",
|
||||
"testTitle": "cancels a deferred comment wake when its only queued comment was authored by the finishing run",
|
||||
"guard": "exact cancelled wake self-authorship error",
|
||||
"jsonSha256": "37db35d7e74e24546d93113301da10b34faba82556af855f26c1a202b9c9ebfe",
|
||||
"logSha256": "9d9efb43f8d0ac1999cf749a87573df607fc13fad89945e97a42f25f48180790",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "F-reachable-PostHog-auth-methods-retained",
|
||||
"testTitle": "renders the same PostHog method choices in page and task-dialog hosts",
|
||||
"guard": "reachable OAuth aria-checked fixed assertion after Change",
|
||||
"jsonSha256": "e05ddaa20035263df996b93af9e8af10af9ca60d91d9e12fff4659d2312a7a36",
|
||||
"logSha256": "0d20720ae732945ccea500b0ca127d69bef8f93b8d64fefb17a64c377a83084f",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "post-drain-success-terminal-receipt-failed-retained",
|
||||
"testTitle": "cancels a deferred comment wake when its only queued comment was authored by the finishing run",
|
||||
"guard": "persisted exact firstRun id/succeeded matcher",
|
||||
"jsonSha256": "e467fb18bc4b9857073a4e9fa2114f72690a87598664df7062e137bd88e03d0b",
|
||||
"logSha256": "a823e1cc7c64c14ce7063f7c46d21ce91dd76529c149fc840d5cac47fd1ce98e",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
},
|
||||
{
|
||||
"id": "C-rejected-capability-snapshot-retained",
|
||||
"testTitle": "never forces the persistent session when the resolution rejects",
|
||||
"guard": "rejected-resolution snapshot toBeUndefined before execute",
|
||||
"jsonSha256": "0af6106ffb09a5161e94bab6a41a89e27207bcc3bda656bdc6ce10e2b4ddfa5b",
|
||||
"logSha256": "1474419f5cfeaf68845d2b7d7d26433b54e549cb296507fe0b63574c2389f694",
|
||||
"failedAtIntendedAssertion": true,
|
||||
"notSetupOrTimeout": true
|
||||
}
|
||||
],
|
||||
"originalWeakControlsPassed": 2,
|
||||
"positiveUniqueKeeperCasesPassed": 7,
|
||||
"allProductionOwnersRestored": true,
|
||||
"restoredProductionOwners": [
|
||||
{
|
||||
"path": "packages/adapter-utils/src/duplex-frame-codec.ts",
|
||||
"currentSha256": "97efc3275ab763b79f2afcbcdf4d6ad2a187e713c91c35c0186b6aa8828816b9",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
},
|
||||
{
|
||||
"path": "packages/adapter-utils/src/execution-target.ts",
|
||||
"currentSha256": "9f61140f7c27698e7837b97dd78f9b3056045a41527da06bb0cee9f11c9f62a6",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
},
|
||||
{
|
||||
"path": "packages/adapter-utils/src/sandbox-managed-runtime.ts",
|
||||
"currentSha256": "2b6093b6481d9ef1f30a9c5798d2d9b4d3c8355867dad4b2bb685a554413b49d",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
},
|
||||
{
|
||||
"path": "server/src/modules/wake-queue/adapters/postgres.ts",
|
||||
"currentSha256": "86529cb3acf96b165148654d76980dd1aaef69571b16662cf041e2f7530c8851",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
},
|
||||
{
|
||||
"path": "server/src/services/environment-execution-target.ts",
|
||||
"currentSha256": "7f78af7a3726f25068648938f686a2adb5a28445b61a9eb6626960e22a18798a",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
},
|
||||
{
|
||||
"path": "server/src/services/heartbeat/run-completion.ts",
|
||||
"currentSha256": "0136bfae6e3ea5dd0af3beaff535f414158d2eedc9a86ad385ef49e68014d9d7",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
},
|
||||
{
|
||||
"path": "ui/src/features/connections/ConnectionSetupFlow.tsx",
|
||||
"currentSha256": "cf61ae14cf58c496712fb003aae61453f3e907fc22a49b3bd170b3b2a3a9eb23",
|
||||
"equalsPinnedBaselineBytes": true
|
||||
}
|
||||
],
|
||||
"coreRestorationReceiptSha256": "97a9bcdaaf480029ef05eeb9226acbf652ab516a2eee9554e111589f4baf892f",
|
||||
"uiRestorationReceiptSha256": "2eef6a3afa1f876eef9c2fef3b82c2a0d0bb5711b9a9c55cc739b9ddab8f7c3e",
|
||||
"rejectedEarlierReceiptMutation": "Changing the successful gateway receipt before queue drain timed out before the intended assertion. Excluded from accepted proof. The accepted replacement changes the persisted receipt atomically after real drain in the existing release transaction.",
|
||||
"independentReviewSha256": "fadc62d0af5cae5c1347824cf8b7f86f4ae48a8aa5828f041a4b7292c0480982"
|
||||
},
|
||||
"formattingIdentity": {
|
||||
"proofSha256": "4cd2af912973bf1a68a9ae34b1d50cc69f6e38d9b209a1fc8f6822a22075c339",
|
||||
"completeRuntimeAstIncludingLiteralRawTextEqual": true,
|
||||
"allStringTemplateRegexSourceBytesEqual": true,
|
||||
"note": "The three adapter test files were tidied after the positive and negative runtime runs. Final source hashes differ only in leading indentation and one duplicate blank import line. Full literal-aware AST equality links the executed bytes to the final bytes; all 134 actual helper expansions were checked again."
|
||||
},
|
||||
"scope": "Owner and sibling preservation evidence. Whole-workspace verification and latest delivery-head CI/Greptile outcomes are recorded in the pull request."
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
# Test audit, Round 6 — preservation report
|
||||
|
||||
Round 6 removes **2,831 net test lines and 12 redundant executions**, repairs two false proofs, and changes **zero production lines**. Complete owner/sibling baselines passed **2,060/2,060**; candidates passed **2,048/2,048** across the same **39 complete files**, with no failures or skips. These counts establish preserved selected-suite coverage, not a wall-time speed improvement.
|
||||
|
||||
Audit baseline: `a090035cafb29c06f6fca2dbe43561ad866b7c1b` includes the prior cleanup and current master. Audit code commit: `d8de9422f0703d4508a9dc880b67a07f13e0f110`. The initial delivery replayed only this round's identical test changes on stacked base `58c5c4aa241c85f4665a3159c192888bbb2a5cfa`. After the parent squash merge, the same round's changes were replayed onto merged master `dabb8c5daf430a1987af611e2e7ca4c7b23ef7d4`. This report records preservation evidence; workspace checks and current delivery/CI status belong in the PR.
|
||||
|
||||
The audit followed OpenClaw's [test-audit skill](https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/SKILL.md) and [campaign process](https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/CAMPAIGN.md): baseline complete files, read owners/callers/siblings, classify each declaration, nominate exact keepers, independently review, then run candidates and deliberate production regressions.
|
||||
|
||||
## Exact scope
|
||||
|
||||
Only these ten existing test files change; no application, API, workflow, lockfile, telemetry, style or token contract changes.
|
||||
|
||||
| Test file | Net lines removed |
|
||||
|---|---:|
|
||||
| `packages/adapter-utils/src/execution-target-sandbox.test.ts` | 191 |
|
||||
| `packages/adapter-utils/src/sandbox-managed-runtime.test.ts` | 470 |
|
||||
| `packages/adapter-utils/src/sandbox-callback-bridge.test.ts` | 214 |
|
||||
| `server/src/__tests__/heartbeat-process-recovery.test.ts` | 684 |
|
||||
| `server/src/__tests__/heartbeat-comment-wake-batching.test.ts` | 688 |
|
||||
| `server/src/__tests__/environment-execution-target-capabilities.test.ts` | 22 |
|
||||
| `ui/src/components/TaskChatThread.test.tsx` | 250 |
|
||||
| `ui/src/components/task-chat/TaskChatComposer.test.tsx` | 92 |
|
||||
| `ui/src/components/IssueProperties.test.tsx` | 26 |
|
||||
| `ui/src/pages/apps/AppsConnect.test.tsx` | 194 |
|
||||
|
||||
Totals: sandbox **875**, heartbeat **1,372**, UI **562**, capabilities **22**. The final sandbox total includes removal of one duplicate blank import line during indentation cleanup; the other savings include helper costs and repaired expectations.
|
||||
|
||||
All **995 original declarations** have decisions: **R619** retained, **F2** repaired, **C370** consolidated, **D4** deleted as duplicate or test-owned proof. C includes fixture/query/render consolidation and case migrations; it does not mean 370 executions were removed. Every remaining table, timeout, retry and distinct security/ownership/transport/platform/lifecycle contract remains.
|
||||
|
||||
## All twelve retired executions and their keepers
|
||||
|
||||
Owner labels below refer to the exact files in the scope table. Each row is one execution, including the two declarations retired from the handwritten latch group.
|
||||
|
||||
| Owner | Retired case | Retained keeper and preserved observation |
|
||||
|---|---|---|
|
||||
| execution-target-sandbox | `marks the orderly completion and reports a success for a healthy channel` | `test_close_after_orderly_completion_keeps_the_run_result`: actual bridge settle/read around provider exit, success and no loss event/counter. Retired case exercised only a handwritten fake. |
|
||||
| execution-target-sandbox | `reports the failure and does not mark for a latched loss` | `fails a clean CLI completion closed when the bridge channel was lost mid-turn`, alongside retained actual bridge loss cases: typed failure translation remains; retired latch assertion exercised only the fake. |
|
||||
| execution-target-sandbox | `cannot clear the loss latch with a later completion` | Same clean-CLI failure keeper: identical failed injected disposition, exitCode and typed error, plus provider_exit stderr. Extra mark affected only the handwritten fake. |
|
||||
| execution-target-sandbox | `test_a_missing_preface_aborts_the_open_and_falls_back_to_queue_v1` | `test_fallback_to_queue_v1_happens_at_most_once_per_run`: same READY-without-preface path; migrated provider close/stop receipt **before finally**, one open and one queue fallback. |
|
||||
| execution-target-sandbox | `encodes the READY frame to the exact byte-for-byte wire format` | callback-bridge `test_http2_gateway_writes_no_frame_between_ready_and_the_preface`: real generated gateway, literal version/key order/newline/preface, plus migrated host-encoder compatibility. |
|
||||
| sandbox-managed-runtime | `the workspace wipe command preserves in-flight sync scratch tarballs (.paperclip-upload-*)` | `the workspace wipe keeps an in-flight scratch tarball at the root but removes a stale sibling`: executes the real shell wipe and checks independently seeded bytes and stale-file absence. |
|
||||
| sandbox-callback-bridge | `maps an indeterminate host outcome to a non-retryable 409 in the in-sandbox bridge server` | execution-target-sandbox `forwards the host indeterminate-outcome header so the sandbox server maps the 504 to a non-retryable 409`: actual request/status/header/body. Separate actual HTTP/2 mapping remains. |
|
||||
| heartbeat-comment-wake-batching | `cancels a deferred wake containing only a comment authored by the closing run` | `cancels a deferred comment wake when its only queued comment was authored by the finishing run`: exact persisted wake cancellation/error, migrated total-run count, and restored exact first-run succeeded receipt. |
|
||||
| AppsConnect | `shows an in-flight state while Paperclip prepares Notion sign-in` | `keeps the same steps when Notion sign-in takes over the wizard`: identical pending setup/actions; all four original assertions retained, with three migrated, plus no-stepper checks. |
|
||||
| AppsConnect | `uses the gallery app name automatically` | `leaving the default name connects with the app name`: identical GitHub/key/submit inputs and both original assertions, plus navigation. |
|
||||
| environment-execution-target-capabilities | `omits the snapshot when the resolution rejects` | `never forces the persistent session when the resolution rejects`: identical rejected target; migrated undefined-snapshot assertion before actual runner delegation, then forceSession=false. |
|
||||
| environment-execution-target-capabilities | `carries no snapshot for session-output streaming when the resolution rejects` | Same rejection keeper and identical setup; separate positive streaming, native-sync rejection and concurrent-sync controls remain. |
|
||||
|
||||
One additional assertion-only cut removes a literal test-owned `push("provisioned")`/equality in the generic asset round-trip. Real provisioned/helper bytes and the restore callback's actual remote-file receipt remain. A restore-diagnostic title was corrected; its assertions and ordering behavior remain unchanged.
|
||||
|
||||
## Two false proofs repaired
|
||||
|
||||
| Keeper | Original weakness | Repaired public observation |
|
||||
|---|---|---|
|
||||
| `keeps sentinel route, query, body, and tokens off the http2 telemetry and logs` | Negative sentinel inventory omitted the actual generated bridge token. | After real HTTP/2 readiness/request success, require the issued token to be nonempty and exclude those actual bytes from both recorder JSON and onLog chunks. Host token/route/query/body/forwarded Authorization checks remain. |
|
||||
| `renders the same PostHog method choices in page and task-dialog hosts` | Compared subject-derived optional button states; real controls are collapsed radios with aria-checked, so missing controls could equal missing controls. | Open Change in both actual hosts; fixed selected/unselected radio expectations, required-key Connect gate, real typed key, and original dialog primary-action receipt. |
|
||||
|
||||
For each repair, the pinned original weak case **passed the same production regression** that made the repaired keeper fail at its intended assertion. No fabricated token, receipt, copied expected inventory or subject-derived expectation supplies the repaired oracle.
|
||||
|
||||
## Independent review and expansion
|
||||
|
||||
Independent reviewers expanded **625 calls from actual candidate helper bodies**: sandbox **134**, heartbeat **282**, UI **209**. Complete ordered runtime programs equal the originals outside the specifically recorded retirements, migrations and F bodies. Review separately checked lexical callee/free-reference bindings, immutable arguments, callback captures, field/JSX spread order, fresh nested allocations, genuine DB/process receipts, all outer awaits, timer/wait budgets, transaction/action ordering and cleanup. No expected assertions are generated by fixture helpers. No added helper comments or sub-1,000-to-over-1,000 file crossings occur.
|
||||
|
||||
**Review caught and restored a real heartbeat preservation gap:** the first proposal migrated cancellation and total-run counts but omitted the retired case's exact first-run `succeeded` receipt. The final keeper copies the original DB read, waits for real queue release and a settled row, then directly asserts `{ id: firstRun!.id, status: "succeeded" }` before cleanup. Waiting only for success would hide a failed receipt behind a timeout; the final settled-state wait permits the direct guard to fail. The correction was independently approved before integration.
|
||||
|
||||
The sandbox files were tidied **after** runtime controls. The candidate runs used the earlier bytes; final hashes differ only by leading indentation and one blank import line. `tidy-proof.json` proves complete AST identity including literal raw text and exact string/template/regex source bytes, and all 134 helper expansions were rechecked. The durable controls record connects each executed hash to its final hash; it does not claim the final formatting bytes were separately runtime-tested.
|
||||
|
||||
## Accepted regression evidence
|
||||
|
||||
All eight deliberate production regressions failed at their intended retained guard, with no setup/worker/timeout failure:
|
||||
|
||||
| Regression | Observed guard |
|
||||
|---|---|
|
||||
| Actual issued bridge token added to ready log | Actual-token exclusion from logDump. |
|
||||
| Missing-preface close bypassed | Provider close/stop count before finally received zero. |
|
||||
| Host READY encoder adds a newline | Host encoder versus independently literal-pinned generated gateway bytes. |
|
||||
| Scratch exclusion removed from real wipe | Independently seeded scratch read failed with ENOENT at its resolves-byte assertion. |
|
||||
| Self-authored deferred input kept live | Exact wake cancellation error differed. |
|
||||
| PostHog authentication choices removed | Fixed OAuth aria-checked assertion received undefined after Change. |
|
||||
| First-run success receipt corrupted after real drain | Exact persisted first-run matcher received failed instead of succeeded. |
|
||||
| Rejected capability resolution fabricates a snapshot | Undefined-snapshot assertion received an object before runner execution. |
|
||||
|
||||
Seven unmutated positive keeper controls passed, and both original weak controls passed under their respective regressions. All production owner bytes were restored. **One earlier receipt mutation was rejected and excluded:** changing success before queue drain timed out before the intended assertion. The accepted replacement alters the persisted receipt atomically after real drain in the existing release transaction and reaches the direct status guard.
|
||||
|
||||
## Durable provenance
|
||||
|
||||
[Controls and source/packet digests](2026-10-12-test-audit-round6-controls.json) retain the complete-file run JSON digests, final/executed source hashes, accepted mutation JSON/log digests, restoration receipt and formatting identity digest. Packet manifest / expansion SHA256 values are:
|
||||
|
||||
| Packet | Manifest SHA256 | Expansion SHA256 |
|
||||
|---|---|---|
|
||||
| Sandbox | `cb6f36fa4b836b7c16e23ce6f62326a15e27eed407742b0e09d4779f40f7c7b1` | `8b3883f723147d2bcd53e0288d76fbf47b817d187a8fd79e4f85b258a840ad16` |
|
||||
| Heartbeat | `85934e19feb6aed9247c09ff2057ab4acd1b9d5951e534047c597cbcc936417b` | `56e5b59358099059bc48204f74509a2bff8256ae91deeeea0f960575b0d221c1` |
|
||||
| UI | `0dd858bc5b8b2c3c23a6772ae0fea5087191af9a549a36d9db8f23be8e539127` | `8bd16e860d15f25defa34dd49b6d7e48ac7bf1e0dccaa4c6bae5979562d60ad4` |
|
||||
| Capabilities | `72fdcb9282dd7875bbc6df559fa8896c856151e60843657cdfe70054cd8b8924` | No helper expansion; exact semantic preservation review. |
|
||||
|
||||
Full source ledgers, verifiers and raw logs were temporary audit packets. This report and its adjacent controls JSON retain compact accepted decisions and provenance; digests identify those packets but do not retain their full payloads. Broad workspace and latest delivery-head checks remain separate PR evidence.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Command-managed-runtime upload duplicate retirement — 2026-10-13
|
||||
|
||||
## Scope and baseline
|
||||
|
||||
- Base: `418eb4623ae767b5c9ce28c1c3e9fcb98f63702e`, the head of [PR #16173](https://github.com/paperclipai/paperclip/pull/16173).
|
||||
- Branch: `codex/test-audit-r19-adapter-utils-upload-case`, stacked on `codex/test-audit-r18-worktree-config`.
|
||||
- Production owner: `createCommandManagedRuntimeClient().writeFile` in `packages/adapter-utils/src/command-managed-runtime.ts`. `prepareCommandManagedRuntime` constructs this client for command-backed adapter staging and fallback sync operations.
|
||||
- Owner file: `packages/adapter-utils/src/command-managed-runtime.test.ts`.
|
||||
- Method: [OpenClaw test-audit](https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/SKILL.md), its audit workflow, owning-boundary guide, and proof policy.
|
||||
- Required repository guidance, contributor rules, roadmap, and PR template were consulted. GitHub searches for the exact case and upload-test consolidation found no duplicate retirement. Related adapter audit: [PR #16115](https://github.com/paperclipai/paperclip/pull/16115).
|
||||
|
||||
## Retirement and retained proof
|
||||
|
||||
Retire only `test_single_stream_writeFile_collapses_roundtrips_under_96MiB`, originally at line 921. It was added in `030dd9d15c` ([PR #10340](https://github.com/paperclipai/paperclip/pull/10340)) as Research A1 proof for the single-stream round-trip reduction. Its two real-shell uploads duplicate the existing transport owner cases.
|
||||
|
||||
Before removal, strengthen the existing single-stream case at line 462 from 3 MiB to 9 MiB. This preserves the retired case's exact size witness. Its payload still contains every byte value, and it still checks exact file bytes, two total shell calls, cleanup, stdin transport, monotonic progress, and final progress. Change the existing base64 guard to allow exactly a 9 MiB source buffer. The guard checks decoded buffer length, not the resulting string length.
|
||||
|
||||
The production threshold is **96 MiB of encoded base64**, not decoded payload bytes. A 9 MiB payload encodes to 12 MiB. Reverting to chunked uploads for this witness fails the surviving exact two-call assertion. Neither the retired case nor this keeper proves the exact 96 MiB boundary or the above-threshold branch.
|
||||
|
||||
Strengthen the existing 12 MiB chunked-progress case from `calls.length > 2` to `3 + Math.ceil(payload.byteLength / (3 * 1024 * 1024))`: seven total shell calls. The three fixed calls initialize the staged file, rename it, and clean it up. The remaining four calls upload decoded 3 MiB chunks. Its binary-byte, bounded-stdin, monotonic-progress, and final-progress assertions stay intact. This is the retired chunk-count formula applied at the surviving larger payload.
|
||||
|
||||
The 10 MiB partial-final-chunk case, omitted-capability case, cleanup-failure case, and all 22 other cases stay byte-for-byte unchanged. The retired cross-case greater-than assertion is implied by the retained exact counts of two and seven.
|
||||
|
||||
## Cost and lines
|
||||
|
||||
- Remove one case: owner file goes from 28 to 27 cases.
|
||||
- Remove the 23-line declaration and its following blank line. Four existing lines change. Git reports 4 added / 28 removed physical test-file lines: net **24 fewer physical lines**.
|
||||
- Production LOC and support LOC change: zero. This receipt is the only additional tracked file.
|
||||
- Retired fixture performed eight real shell invocations: two single-stream and six chunked. Growing the surviving single-stream payload adds no invocations. Net reduction: **eight shell invocations**.
|
||||
- Retired fixture uploaded 18 MiB. Growing the keeper adds 6 MiB. Net reduction: **12 MiB of decoded upload payload** per owner run.
|
||||
- Prior three-file adapter-utils audit baseline: 87/87 passed. The retired case cost 228.125125 ms. Its original 3 MiB keeper cost 51.266500 ms.
|
||||
- Candidate 9 MiB keeper cost 163.982042 ms. Prior owner duration: 1,495.937988 ms; candidate owner duration: 1,979.989990 ms. These are separate host runs, not a controlled performance comparison. They do **not** establish a suite speedup. The deterministic process and byte reductions are the supported savings.
|
||||
|
||||
## Validation
|
||||
|
||||
- `pnpm exec vitest run packages/adapter-utils/src/command-managed-runtime.test.ts --maxWorkers=1 --reporter=json --outputFile=/private/tmp/test-audit-r19-command-managed-runtime-candidate.json`: **27/27 passed**. Recorded command wall time: 7.421 s, including collection and startup.
|
||||
- `pnpm --filter @paperclipai/adapter-utils typecheck`: passed.
|
||||
- `git diff --check`: passed.
|
||||
- Exact diff inspection confirmed the encoded threshold semantics and fixed overhead in the chunk count. A declaration comparison confirmed exactly one removed case, exactly two changed keepers, and all other 25 cases byte-for-byte unchanged.
|
||||
- No production mutation experiments ran. No production code, dependencies, contracts, UI, or CI routing changed.
|
||||
- Repo-wide typecheck, test suite, and build were not run locally for this narrow retirement. This is focused owner proof; broad CI gates and review remain pending before merge.
|
||||
|
||||
## Risk and disposition
|
||||
|
||||
Low risk with directly retained owner proof. The larger single-stream fixture costs more than the original 3 MiB keeper, and timing savings are modest and unproven at suite level. Independent transport, capability-default, partial-chunk, progress, atomic rename, and cleanup contracts remain. Open a stacked PR for review; do not merge.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Agent adapter validation route fixture audit — 2026-10-13
|
||||
|
||||
Status: remove one duplicate positive test. Based on `github/master` `d67e3507b23041212fa26302ce6de078c5ebcc05`. Owner: `server/src/__tests__/agent-adapter-validation-routes.test.ts`. Production code is unchanged.
|
||||
|
||||
## Fixture consolidation and removed declaration
|
||||
|
||||
The dynamic external-adapter positive now sets an unrelated adapter as disabled. This same retained declaration proves the registered `external_test` adapter remains selectable and asserts both HTTP 201 and the returned adapter type. Remove the weaker duplicate positive that separately asserted only HTTP 201. Keep the disabled-selection rejection cases and the case that permits editing an existing disabled adapter.
|
||||
|
||||
The audit also considered deleting the local `requestApp` helper and replacing its 38 uses with direct `request(app)` calls. Do not include that cleanup here: the direct-request version repeatedly approached or exceeded the 15-second first-test timeout locally, while the focused baseline passed. Keep the helper until a broader timing comparison shows no regression.
|
||||
|
||||
The remaining route declarations continue to cover provider-specific model refresh, credentials, rollback, Runner rollout, Dot authorization, provider admission, validation failures, and partial edits. The production entry point remains `routes/agents.ts`; UI configuration and create/hire flows call these routes. CI discovers this filename in the serialized route/authz lane.
|
||||
|
||||
## Local validation and savings
|
||||
|
||||
The historical serialized weight is 24,906 ms and includes isolated process startup, imports, tests, and teardown. It is not a measured runtime reduction. The change removes one test declaration and about 13 test lines, including its separator. It removes no production lines, and no CI time reduction is claimed.
|
||||
|
||||
Focused exact-head validation passed: `pnpm exec vitest run --project @paperclipai/server server/src/__tests__/agent-adapter-validation-routes.test.ts` reported **44/44 passing** in 29.57 seconds with local bind access. `git diff --check` is also required before commit. Relevant adapter registry/environment siblings remain to be validated if later changes touch their behavior.
|
||||
|
||||
## Integration with current master
|
||||
|
||||
The disabled-adapter fixture moves into the existing dynamic-registration keeper. Newer saved-legacy, harness-selection and native-Codex rollout regression cases from master are retained unchanged.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Agent instruction route graph reuse recheck (2026-10-13)
|
||||
|
||||
Owner: `server/src/__tests__/agent-instructions-routes.test.ts`. Rechecked in `/private/tmp/paperclip-test-audit-pr-stack` at `9b4d0cd872ca1fc897f320cb181fc060f0bd5bf1` without changing its branch. The owner blob is identical to both `codex/test-audit-r15-onboarding-duplicates` at `568ba003a5` and the parent of prior candidate `0e5043c65513ac470208152dd90e0e74b6dcd022`: `4f7a65449b3d5ed70539bec18b1865e18c7648b7`.
|
||||
|
||||
The earlier 34-line candidate removes duplicated module setup, not duplicated endpoint executions. Applied its exact owner patch after the parent explicitly authorized this setup reduction. Test source changes by +16/-50 lines, 853 to 819 lines, net -34. Registered and expanded cases remain 24; removed executions and assertions: zero. Production files changed: zero.
|
||||
|
||||
`registerModuleMocks` duplicated the existing hoisted mock registrations. The existing `hoistModuleGraph` helper loads route and middleware exports once; every test still constructs a fresh app/router and HTTP server. `vi.resetAllMocks` clears implementations and once queues as well as call histories; each test's defaults are rearmed, including the secret normalization implementation formerly preserved by `clearAllMocks`. The route's hire lock map is not used by these fixtures because their actor has no run ID. The existing helper inherits the server's 30-second hook timeout, replacing the previous explicit 60-second per-case preload timeout.
|
||||
|
||||
The complete section from the first test declaration through the final lifecycle mock is byte-identical before and after, SHA256 `6b0b031908125729e8a135878a5d5245fa7a4137520df79ddf295c25ffcd2353`. JSON reports contain identical ordered names for all 24 expanded cases. Retained regressions cover external-root and mislabeled-managed-root instance-admin restrictions; generic patch, hire, direct creation and compatibility endpoints; own/peer read authorization; canonical revisions and forged identity/base-revision rejection; binary and empty downloads; candidate resolution scope; history/diff/restore; adapter merge/replacement and skill-selection preservation.
|
||||
|
||||
Full owner command, with authorized local loopback access:
|
||||
|
||||
```sh
|
||||
node node_modules/vitest/vitest.mjs run --project @paperclipai/server server/src/__tests__/agent-instructions-routes.test.ts --reporter=json --outputFile=/private/tmp/agent-instructions-current-ARM.json
|
||||
```
|
||||
|
||||
| Arm | Passed | Report start-to-suite-end duration |
|
||||
| --- | --- | --- |
|
||||
| Baseline | 24/24 | 20.816 s |
|
||||
| Candidate | 24/24 | 7.268 s |
|
||||
| Candidate repeat | 24/24 | 7.445 s |
|
||||
|
||||
These are local observations on a concurrently used host, with one baseline and no reverse-order control. They support stable candidate behavior, not a quantified performance or CI speedup claim. Two sandboxed attempts stalled at HTTP fixtures and were cancelled (exit 130); they are excluded. Existing provider-trace cleanup warnings also appeared in passing baseline and candidate runs.
|
||||
|
||||
Exact PR-branch rerun also passed 24/24 in 8.06 seconds with `node_modules/.bin/vitest run --project @paperclipai/server server/src/__tests__/agent-instructions-routes.test.ts --silent=true`. `git diff --check` passed. Reports: `/private/tmp/agent-instructions-current-baseline.json`, `/private/tmp/agent-instructions-current-candidate.json`, and `/private/tmp/agent-instructions-current-candidate-repeat.json`. No staging, commit, push, branch switch, or edits to other owners were performed. Repo-wide checks were not run for this narrow test-only worker handoff.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Agent skills route duplicate canonicalization audit — 2026-10-13
|
||||
|
||||
Status: duplicate-proof deletion, based on `github/master` `d67e3507b23041212fa26302ce6de078c5ebcc05`. Owner: `server/src/__tests__/agent-skills-routes.test.ts`. Production code and unrelated declarations are unchanged.
|
||||
|
||||
## Removed declarations and retained keepers
|
||||
|
||||
Remove two successful canonicalization replays:
|
||||
|
||||
- The sync case that sends the `paperclip` alias with `mode: replace` and checks the canonical persisted singleton is covered by the retained complete-replacement case. That keeper starts with an existing assignment, performs the same request and checks the same canonical result. It additionally proves replacement removes the prior assignment.
|
||||
- The hire case that checks canonical desired skills in the approval payload and requested configuration snapshot is covered by the retained detailed hire case. That keeper checks both canonical fields and also preserves agent creation, icon, actor attribution, and source-issue linkage.
|
||||
|
||||
All other declarations remain. The broader suite retains adapter-specific configuration, secret handling, merge/remove/replace semantics, beta gates, built-in role defaults, onboarding trust, approval policy, actor attribution, and rejection boundaries. Production routes, services, and their seams are unchanged. Caller review covered Agent Skills and Company Skills UI, New Agent/onboarding hire flows, and CLI `skills agent sync`.
|
||||
|
||||
## Local validation and savings
|
||||
|
||||
The historical serialized route/authz weight is 24,106 ms and includes suite startup, imports, database setup, and shutdown. It is not a measurement of removable runtime. The edit removes two test declarations and about 48 test lines, including separators. It removes two HTTP requests and their repeated module-reset/import setup. No production lines are removed, and no CI time reduction is claimed.
|
||||
|
||||
The focused exact-head owner suite passed **58/58** in 38.38 seconds. No baseline or mutation run is claimed. A faulty canonicalizer or approval serializer should fail the retained replacement and detailed-hire keepers. The existing skills-service and authorization-service owner suites continue to cover storage and the direct permission decision.
|
||||
@@ -0,0 +1,70 @@
|
||||
# CLI portability codec test audit
|
||||
|
||||
Date: 2026-10-13. Proof base: `06c1b2035df44fa03c065ddd69e8bf8df8513b86` (the preceding audit stack). This candidate changes tests only.
|
||||
|
||||
## Decision and preserved contracts
|
||||
|
||||
Retire `cli/src/__tests__/zip-codec.test.ts` after moving its unique observations into `packages/shared/src/portability-zip.test.ts`:
|
||||
|
||||
| Retired declaration | Shared keeper |
|
||||
| --- | --- |
|
||||
| `matches blobs/ entries at the archive root and under a package root` | Same named classifier keeper, now also rejects `blobs/nested/file`. |
|
||||
| `decodes valid UTF-8 entries to text` | `decodes valid UTF-8 entries to text and falls back to base64 for invalid bytes`, with identical Unicode text/path/result. |
|
||||
| `round-trips blob and invalid UTF-8 entries byte-exactly` | Existing STORE/DEFLATE archive keeper, now using the retired valid-UTF8 blob bytes `[0x68, 0x69]` and adding `notes/raw` with `[0x68, 0x69, 0xff, 0xfe, 0xc0]`. |
|
||||
|
||||
The valid-UTF8 blob is necessary: arbitrary invalid binary bytes can still become base64 through the decoder fallback if opaque-blob classification breaks. The keeper now demands base64 `aGk=` for the ASCII blob, and `aGn//sA=` for invalid non-blob UTF8. Existing STORE, genuine DEFLATE, shared-root, direct UTF8, truncation, descriptor, central-directory, duplicate-path and decompression-limit assertions remain.
|
||||
|
||||
The CLI module is a production re-export of the shared codec. Real consumers remain in `resolveInlineSourceFromPath`, `resolveChunkedImportZip`, folder conversion/filtering (`cli/src/commands/client/company.ts`), and server uploaded-archive parsing (`server/src/routes/companies.ts`). No production exports or code are deleted. CLI file/transport/filtering tests remain in `company-import-zip.test.ts` and `company-import-transfer.test.ts`.
|
||||
|
||||
History: #10507 (`916c13501f`) introduced CLI codec tests. #10531 (`5ec7ce76e5`) moved the codec into shared, retained CLI re-exports, and introduced the primary shared-owner tests. This candidate removes the replay left behind by that ownership move.
|
||||
|
||||
## Reproducible verification
|
||||
|
||||
From a dependency-ready checkout at the proof base, the complete baseline/revert owner command is:
|
||||
|
||||
```sh
|
||||
node node_modules/vitest/vitest.mjs run --no-file-parallelism --maxWorkers=1 \
|
||||
packages/shared/src/portability-zip.test.ts \
|
||||
cli/src/__tests__/zip-codec.test.ts
|
||||
```
|
||||
|
||||
For the candidate/restored owner, omit the deleted CLI file. Run transport proof separately:
|
||||
|
||||
```sh
|
||||
node node_modules/vitest/vitest.mjs run --no-file-parallelism --maxWorkers=1 \
|
||||
cli/src/__tests__/company-import-zip.test.ts \
|
||||
cli/src/__tests__/company-import-transfer.test.ts
|
||||
```
|
||||
|
||||
| Phase | Complete owner | Separate transport |
|
||||
| --- | --- | --- |
|
||||
| Untouched baseline | 18/18 | 18/18 |
|
||||
| Candidate | 15/15 | 18/18 |
|
||||
| Full test-file revert | 18/18 | Not repeated |
|
||||
| Restored candidate | 15/15 | Not repeated |
|
||||
| After production restoration | 15/15 | 18/18 |
|
||||
| After verbose mutation receipts | 15/15 | Not repeated |
|
||||
|
||||
All complete-file positive runs have zero failed, skipped/pending and todo tests. `git diff --check` passes. No tests were disabled or weakened because of failures.
|
||||
|
||||
## Actual production falsification
|
||||
|
||||
Each mutation changes only `packages/shared/src/portability-zip.ts`, is run separately, and is restored byte-for-byte before the next mutation. Focused negative runs select one keeper and therefore skip 14 unrelated cases; each selected case fails at the intended assertion.
|
||||
|
||||
1. Broaden the classifier regex from `/(^|\/)blobs\/[^/]+$/` to `/(^|\/)blobs\/.+$/`. Run shared owner with `-t 'matches blobs/'`. The migrated `blobs/nested/file` assertion fails: expected false, received true.
|
||||
2. Replace `if (isBlobStorePath(pathValue)) {` with `if (false) {` in the conversion function. Run shared owner with `-t 'round-trips STORE'`. The migrated archive assertion fails because `blobs/4f2d1c9a` becomes plain `"hi"` instead of `{ encoding: "base64", data: "aGk=", contentType: "application/octet-stream" }`.
|
||||
3. Replace archive entry conversion `body: bytesToPortableFileEntry(archivePath, entryBytes),` with `body: isBlobStorePath(archivePath) ? bytesToPortableFileEntry(archivePath, entryBytes) : textDecoder.decode(entryBytes),`. Run shared owner with `-t 'round-trips STORE'`. The migrated `notes/raw` assertion fails because lossy text replaces the expected base64 bytes. This preserves the blob branch so the failure specifically witnesses the non-blob invalid-UTF8 archive contract.
|
||||
|
||||
Production SHA256 before and after all mutations: `b5860afd9ab301fb9cd44f3b3799c28d6fe6297fc891ebbe330b4b62bc53bd4e`.
|
||||
Original shared test SHA256: `7d48436c195120e9717d9ee1b99f10c808c976088b6c2306cbfad3990053893a`.
|
||||
Candidate shared test SHA256: `1664aca9760a18f33ce2657a4ac878a6015c8a6a43b4290dd88cd2b78663b6db`.
|
||||
Deleted CLI test SHA256: `e3634f25fa66544937cf12d30432ff8d109fbfbd578b5a6548df5aef1625f8ef`.
|
||||
Candidate patch SHA256: `172b2e5fa01f369cb07edf19a133680c82bb239cdbd44ccb878bf1ef0ea98071`.
|
||||
|
||||
## Cost and limits
|
||||
|
||||
Diff: 9 added and 52 removed test lines, **43 net test lines removed**. Three registrations and one CLI Vitest file are retired; the shared owner stays at 15 cases. Production reduction is zero.
|
||||
|
||||
`scripts/run-vitest-stable.mjs` routes CLI to `general-workspaces-a` and shared to `general-workspaces-b`; `.github/workflows/pr-trusted.yml` executes those groups. This removes duplicate CLI collection/import/case work. No wall-time or CI percentage savings are claimed.
|
||||
|
||||
This is focused local evidence, not a repo-wide PR-ready result. Repo-wide typecheck, full suite, build, CI and reviewer checks were not run by this proof agent and must be attributed separately by the delivery supervisor. No commit or PR was created by this agent.
|
||||
@@ -0,0 +1,221 @@
|
||||
# Proven Codex staging permission-test consolidation
|
||||
|
||||
Historical proof base: #16106 exact head 397459400c6e7b18b462dcac3d8886b390ca8e8c. Original test and production source match exact-head blobs before controls. Isolated snapshot only; serial runs; no shared checkout edits, commits, pushes or PRs.
|
||||
|
||||
Historical result: PROVEN. Owner packages/adapters/codex-local/src/server/codex-home.test.ts shrinks 1,412 -> 1,371 lines (41 net removed); registered cases 53 -> 50; three fixture allocations/stageCodexHomeForSync calls and their teardown sequences retired. No subprocess delta or CI-time improvement claimed.
|
||||
|
||||
Keeper `stageCodexHomeForSync stages exactly the allowlist, derefs auth.json to bytes, and excludes decoys` gains direct actual 0700 staged-root and 0600 checks for auth.json, config.toml, config.json and instructions.md. Retire the standalone auth mode, every-file mode and directory-mode cases. Same buildFakeHome input bytes/action apply; original runId values differ only in temporary-directory prefix. Keeper retains its run-1 prefix, real allowlist/content/symlink/decoy receipts and cleanup. Dedicated secret-bearing managed-MCP config.toml permission case remains independent.
|
||||
|
||||
| Complete-owner phase | Cases | Pass | Fail | Exit |
|
||||
|---|---:|---:|---:|---:|
|
||||
| baseline | 53 | 53 | 0 | 0 |
|
||||
| candidate | 50 | 50 | 0 | 0 |
|
||||
| file-mode-0644-original | 53 | 50 | 3 | 1 |
|
||||
| file-mode-0644-keeper | 50 | 48 | 2 | 1 |
|
||||
| directory-mode-0755-original | 53 | 52 | 1 | 1 |
|
||||
| directory-mode-0755-keeper | 50 | 49 | 1 | 1 |
|
||||
| restored-candidate | 50 | 50 | 0 | 0 |
|
||||
| integrated-stack | 50 | 50 | 0 | 0 |
|
||||
|
||||
Every accepted run has zero pending/todo/skipped cases. Both real regressions produce direct intended filesystem-mode assertion failures, not setup, timeouts or worker failures.
|
||||
|
||||
## Actual production mutations
|
||||
|
||||
- File chmod: in stageCodexHomeEntry's real write path replace explicit fs.chmod(target, 0o600) with 0o644. Actual staged files become group/other readable (420 actual vs 384 expected). Original full owner fails three cases: auth.json mode, managed-MCP config.toml mode, every-file mode. Candidate fails keeper's actual auth.json stat plus the retained managed-MCP case. The duplicate security assertions now live on real staged bytes in keeper.
|
||||
- Directory chmod: add fs.chmod(stagedHome, 0o755) immediately after mkdtemp and before allowlist copying. Actual staged-root mode becomes 493 vs expected 448. Original dedicated 0700 case and keeper's actual root-stat guard both fail. File bytes/permissions and fixture contents remain otherwise unchanged.
|
||||
|
||||
Regressions run one at a time, with full original/candidate owners under identical mutated production bytes. Test/production restores occur in finally and are byte/hash-verified after every pair. Final restored candidate passes 50/50. Source then restores to exact #16106 test and production originals. Fake fixture credentials only; no real Codex home/credentials are accessed, and no production files outside the isolated snapshot are changed.
|
||||
|
||||
## Dependencies and isolation
|
||||
|
||||
The global pnpm installer differed from the repository's pinned pnpm@9.15.4, stalled before installing the graph, and was explicitly stopped/reaped (exit 143). No tests ran under that installer. Historical setup linked immutable third-party packages from a pnpm store. That private dependency receipt is not bundled. The reproduction below uses a fresh install from the pinned lockfile. Workspace shared and adapter-utils links resolve only to source inside the exact-head isolated snapshot; no application/workspace source is borrowed from the shared checkout. Use existing Codex package Vitest config and full explicit owner path; no mocked resolver/helper or custom alias replaces production code. All controls use the same graph.
|
||||
|
||||
## Exact hashes and integrated stack
|
||||
|
||||
- Original test SHA256: 8f7a0497cb4d8a0f8de3d341585e74c8c17203ab93fe69d0d78fc59410beb220
|
||||
- Proven candidate test SHA256: 8615ceaed7c04647bbc2c674448a012d15a6f597eb36c9b2bfbf854baf947cac
|
||||
- Original/restored production SHA256: fd08bc782af5bdc4631d43cc41f156d97daad629d04fa8654d177f549f8b1b9e
|
||||
|
||||
The proven shard, provisioning and Codex test patches all pass git apply --check and apply together without conflict to exact #16106 snapshot. Integrated Codex candidate retains its executed SHA and passes 50/50 with all three patches present. The historical integrated receipt recorded shard test hash `58ecff8ddceecb8e0c44240fa3dbf7ae6a9756dbac35b5ad52a14e655cfc8327`, provisioning test hash `ffacee5de45871dfe9bb2613aa05ffadb452e4c1f7188b8f0519d36d70d935b2`, the Codex candidate hash above, exit 0, 50/50 passing, zero pending/todo and report digest `822a0d3dbf84d01cb6fb62081a7028d77345c4d1526558c920700672fb313dd3`. Afterwards all three tests restore to exact #16106 original bytes. This proves the combined patches apply and Codex owner passes; it does not claim other owners were rerun in the combined state.
|
||||
|
||||
## Inspectable reproduction
|
||||
|
||||
The results above are historical, recorded on 2026-10-13. This documentation repair
|
||||
checks the Git source and mutation hashes; it does not claim fresh test results.
|
||||
Historical raw reports remain local and are not bundled. The relevant failure
|
||||
excerpts and exact executed production edits are included here; no private proof
|
||||
file is needed to reconstruct the checks.
|
||||
|
||||
Run the block from a repository checkout with the public `base` and `head` Git
|
||||
revisions below. Fetch those public PR/revisions first if they are absent.
|
||||
Python 3.12+, Git and Node are required. The UI and Codex recipes also require
|
||||
pnpm 9.15.4 and network access to install dependencies.
|
||||
|
||||
The script uses an isolated snapshot and obtains the candidate test directly
|
||||
from its Git revision. It runs the full baseline and candidate owners, applies
|
||||
each mutation independently, checks the intended failing case, restores exact
|
||||
source bytes in `finally`, and reruns the full candidate. Counts are
|
||||
`[total, passed, failed, skipped-or-filtered]`. Original-owner mutation counts
|
||||
above are historical; this recipe repeats candidate sensitivity checks.
|
||||
Prefix the command with `AUDIT_RECONSTRUCT_ONLY=1` to verify source and mutation
|
||||
hashes without extracting a snapshot, installing dependencies or running tests.
|
||||
|
||||
```sh
|
||||
python3 - <<'PY'
|
||||
import hashlib, io, json, os, re, subprocess, tarfile, tempfile
|
||||
from pathlib import Path
|
||||
|
||||
spec = {'base': '397459400c6e7b18b462dcac3d8886b390ca8e8c',
|
||||
'head': '449f503f75463956aa0c16cf18623c6217b1f01f',
|
||||
'owner': 'packages/adapters/codex-local/src/server/codex-home.test.ts',
|
||||
'kind': 'codex',
|
||||
'hashes': {'packages/adapters/codex-local/src/server/codex-home.test.ts': '8f7a0497cb4d8a0f8de3d341585e74c8c17203ab93fe69d0d78fc59410beb220',
|
||||
'packages/adapters/codex-local/src/server/codex-home.ts': 'fd08bc782af5bdc4631d43cc41f156d97daad629d04fa8654d177f549f8b1b9e'},
|
||||
'candidate_hash': '8615ceaed7c04647bbc2c674448a012d15a6f597eb36c9b2bfbf854baf947cac',
|
||||
'baseline': [53, 53, 0, 0],
|
||||
'candidate': [50, 50, 0, 0],
|
||||
'mutations': [{'name': 'directory-mode-0755',
|
||||
'path': 'packages/adapters/codex-local/src/server/codex-home.ts',
|
||||
'changes': [[' try {\n for (const entry of CODEX_SYNC_ALLOWLIST) {',
|
||||
' await fs.chmod(stagedHome, 0o755);\n'
|
||||
' try {\n'
|
||||
' for (const entry of CODEX_SYNC_ALLOWLIST) {']],
|
||||
'hash': '1b7d02534f024e2f9c4fd6a3bda4604cd3609163e14e3de7dc504847cc1adf5e',
|
||||
'counts': [50, 49, 1, 0],
|
||||
'guard': 'stageCodexHomeForSync stages exactly the allowlist, derefs auth.json to '
|
||||
'bytes, and excludes decoys',
|
||||
'message': 'expected 493 to be 448'},
|
||||
{'name': 'file-mode-0644',
|
||||
'path': 'packages/adapters/codex-local/src/server/codex-home.ts',
|
||||
'changes': [[' await fs.chmod(target, 0o600);\n',
|
||||
' await fs.chmod(target, 0o644);\n']],
|
||||
'hash': '151071d796a14e1bd53a8227d1679f9c30ac73459206fe77481723d151f5118e',
|
||||
'counts': [50, 48, 2, 0],
|
||||
'guard': 'stageCodexHomeForSync stages exactly the allowlist, derefs auth.json to '
|
||||
'bytes, and excludes decoys',
|
||||
'message': 'expected 420 to be 384'}]}
|
||||
|
||||
def digest(data):
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
def blob(rev, path):
|
||||
return subprocess.check_output(["git", "show", rev + ":" + path])
|
||||
|
||||
owner = spec["owner"]
|
||||
original = {p: blob(spec["base"], p) for p in spec["hashes"]}
|
||||
candidate = blob(spec["head"], owner)
|
||||
assert all(digest(b) == spec["hashes"][p] for p, b in original.items())
|
||||
assert digest(candidate) == spec["candidate_hash"]
|
||||
mutants = []
|
||||
for m in spec["mutations"]:
|
||||
text = original[m["path"]].decode()
|
||||
for before, after in m["changes"]:
|
||||
assert text.count(before) == 1, (m["name"], "ambiguous mutation")
|
||||
text = text.replace(before, after)
|
||||
data = text.encode()
|
||||
assert digest(data) == m["hash"], m["name"]
|
||||
mutants.append((m, data))
|
||||
if os.environ.get("AUDIT_RECONSTRUCT_ONLY") == "1":
|
||||
print("Original, candidate and all mutation hashes match historical proof.")
|
||||
raise SystemExit(0)
|
||||
|
||||
archive = subprocess.check_output(["git", "archive", spec["base"]])
|
||||
with tempfile.TemporaryDirectory(prefix="paperclip-audit-") as directory:
|
||||
root = Path(directory)
|
||||
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
|
||||
tar.extractall(root, filter="data")
|
||||
if spec["kind"] != "tap":
|
||||
assert subprocess.check_output(["pnpm", "--version"], cwd=root, text=True).strip() == "9.15.4"
|
||||
subprocess.run(["pnpm", "install", "--frozen-lockfile"],
|
||||
cwd=root, check=True)
|
||||
|
||||
def run(label, expected, mutation=None):
|
||||
report = root / "audit-report.json"
|
||||
if report.exists():
|
||||
report.unlink()
|
||||
if spec["kind"] == "tap":
|
||||
command = ["node", "--test", "--test-reporter=tap", owner]
|
||||
elif spec["kind"] == "ui":
|
||||
command = ["pnpm", "--filter", "@paperclipai/ui",
|
||||
"exec", "vitest", "run", "src/pages/CompanyExport.test.tsx",
|
||||
"--maxWorkers=1", "--reporter=default", "--reporter=json",
|
||||
"--outputFile.json=" + str(report)]
|
||||
else:
|
||||
command = ["node", "node_modules/vitest/vitest.mjs", "run", "--config",
|
||||
"packages/adapters/codex-local/vitest.config.ts", owner,
|
||||
"--reporter=json", "--outputFile=" + str(report)]
|
||||
if mutation and mutation.get("selector"):
|
||||
command += ["-t", mutation["selector"]]
|
||||
result = subprocess.run(command, cwd=root, capture_output=True, text=True,
|
||||
timeout=180)
|
||||
output = result.stdout + result.stderr
|
||||
print(label, "exit", result.returncode, output)
|
||||
if spec["kind"] == "tap":
|
||||
counts = {k: int(v) for k, v in re.findall(
|
||||
r"^# (tests|pass|fail|skipped) (\d+)$", output, re.M)}
|
||||
actual = [counts[k] for k in ["tests", "pass", "fail", "skipped"]]
|
||||
if mutation:
|
||||
failures = re.findall(
|
||||
r"^not ok \d+ - (.*?)\n(.*?)(?=^(?:ok|not ok) \d+ - |^1\.\.|\Z)",
|
||||
output, re.M | re.S)
|
||||
assert any(name == mutation["guard"] and mutation["message"] in body
|
||||
and "ERR_ASSERTION" in body for name, body in failures), label
|
||||
else:
|
||||
data = json.loads(report.read_text())
|
||||
actual = [data[k] for k in ["numTotalTests", "numPassedTests",
|
||||
"numFailedTests", "numPendingTests"]]
|
||||
if mutation:
|
||||
hits = [a for suite in data["testResults"] for a in suite["assertionResults"]
|
||||
if a["status"] == "failed" and mutation["guard"] in a["fullName"]]
|
||||
assert any(mutation["message"] in msg for a in hits
|
||||
for msg in a["failureMessages"]), label
|
||||
if mutation.get("line"):
|
||||
assert any(f'CompanyExport.test.tsx:{mutation["line"]}:' in msg
|
||||
for a in hits for msg in a["failureMessages"]), label
|
||||
assert actual == expected, (label, actual, expected)
|
||||
assert result.returncode == (1 if mutation else 0), label
|
||||
|
||||
try:
|
||||
run("baseline", spec["baseline"])
|
||||
(root / owner).write_bytes(candidate)
|
||||
run("candidate", spec["candidate"])
|
||||
for m, data in mutants:
|
||||
try:
|
||||
(root / owner).write_bytes(candidate)
|
||||
(root / m["path"]).write_bytes(data)
|
||||
run(m["name"] + "-keeper", m["counts"], m)
|
||||
finally:
|
||||
for path, data in original.items():
|
||||
(root / path).write_bytes(data)
|
||||
assert all((root / p).read_bytes() == b for p, b in original.items())
|
||||
(root / owner).write_bytes(candidate)
|
||||
run("restored-candidate", spec["candidate"])
|
||||
finally:
|
||||
for path, data in original.items():
|
||||
(root / path).write_bytes(data)
|
||||
assert all((root / p).read_bytes() == b for p, b in original.items())
|
||||
PY
|
||||
```
|
||||
|
||||
Recorded intended failure excerpts (fixture path prefixes omitted):
|
||||
|
||||
```text
|
||||
directory-mode-0755-original
|
||||
stageCodexHomeForSync creates the staged dir with mode 0700
|
||||
AssertionError: expected 493 to be 448 // Object.is equality
|
||||
directory-mode-0755-keeper
|
||||
stageCodexHomeForSync stages exactly the allowlist, derefs auth.json to bytes, and excludes decoys
|
||||
AssertionError: expected 493 to be 448 // Object.is equality
|
||||
file-mode-0644-original
|
||||
stageCodexHomeForSync writes the staged auth.json with mode 0600
|
||||
AssertionError: expected 420 to be 384 // Object.is equality
|
||||
stageCodexHomeForSync writes the staged config.toml (managed MCP bearer header) with mode 0600
|
||||
AssertionError: expected 420 to be 384 // Object.is equality
|
||||
stageCodexHomeForSync writes every staged regular file with mode 0600
|
||||
AssertionError: auth.json should be staged 0600: expected 420 to be 384 // Object.is equality
|
||||
file-mode-0644-keeper
|
||||
stageCodexHomeForSync stages exactly the allowlist, derefs auth.json to bytes, and excludes decoys
|
||||
AssertionError: auth.json should be staged 0600: expected 420 to be 384 // Object.is equality
|
||||
stageCodexHomeForSync writes the staged config.toml (managed MCP bearer header) with mode 0600
|
||||
AssertionError: expected 420 to be 384 // Object.is equality
|
||||
```
|
||||
@@ -0,0 +1,123 @@
|
||||
# Codex crash reconciliation test audit
|
||||
|
||||
Base: `c1ae082a7a49c239a22e5149e223a41325b0ba2d` (CLI ZIP content uploaded as PR #16118).
|
||||
|
||||
Remove only `requires reconciliation for a classified Codex harness crash`,
|
||||
`server/src/__tests__/heartbeat-retry-scheduling.test.ts:2034–2059`.
|
||||
Saving: **26 test lines and one registered execution**. No runtime reduction is
|
||||
claimed. Keeper: `requires reconciliation for an error-code-only Codex harness
|
||||
crash`, originally lines 2061–2086 (candidate 2035–2060).
|
||||
|
||||
## Static preservation evidence
|
||||
|
||||
The full fixture helper at lines 186–253 writes the classification only into
|
||||
`heartbeatRuns.resultJson.errorFamily`. Both cases then update that run with
|
||||
`resultJson: null` before `heartbeat.scheduleBoundedRetry`. Both persist the same
|
||||
Codex adapter, active agent, failed status, harness-crash error code, zero retry
|
||||
attempts, context shape and company defaults. They call with the same random
|
||||
callback and identical relative `now`/created/updated/finished timestamps. The
|
||||
remaining absolute 12:00 versus 13:00 difference enters no clock comparison with
|
||||
another fixture; no retry deadline, active holder or evidence receipt is seeded.
|
||||
|
||||
Both have one identical literal disposition/code assertion:
|
||||
`{ outcome: "not_scheduled", errorCode: "legacy_execution_requires_reconciliation" }`.
|
||||
No unique assertion needs transfer. The retained error-code-only keeper correctly
|
||||
names the effective input. The classified title no longer reflects classification
|
||||
coverage after its result JSON is erased.
|
||||
|
||||
Production: the exported heartbeat facade delegates `scheduleBoundedRetry` to
|
||||
`server/src/services/heartbeat/retries.ts:1835–1850`, which loads the persisted
|
||||
run with full result JSON and enters `scheduleBoundedRetryForRun`. The real
|
||||
reconciliation guard is at line 501. Its classifier in
|
||||
`server/src/services/legacy-execution-recovery.ts:20–64` requires reconciliation
|
||||
for failed legacy runs without safe bootstrap evidence. Its decision does not
|
||||
consult error family. The subsequent scheduler would create an actual queued
|
||||
wake and scheduled-retry row if that guard were bypassed. It does not launch the
|
||||
provider during this call.
|
||||
|
||||
Non-test callers include run completion (`heartbeat/run-completion.ts:859,
|
||||
1412`), recovery (`heartbeat/recovery.ts:405`), scheduling
|
||||
(`heartbeat/scheduling.ts:668`) and the heartbeat facade. The existing general
|
||||
server CI lane owns this test file; the recorded duration inventory lists it.
|
||||
No new export, injection seam, workflow, production, telemetry or timeout change
|
||||
is needed.
|
||||
|
||||
History: commit `665408c6d01` (PR #10210) added two genuinely different retry
|
||||
classification cases on 2026-07-24. Commit `35fdc0c66b8` (PR #13075) on 2026-09-09
|
||||
renamed both to reconciliation cases, reset both result JSON values and replaced
|
||||
both original assertions with the same reconciliation expectation. That change
|
||||
collapsed their original classification distinction.
|
||||
|
||||
Excluded files from #16114/#16115/#16116/#16075 and the completed UUID-assignee
|
||||
candidate are untouched. Recent audit receipts contain no prior retirement of
|
||||
this pair. All test bytes outside the one removed body stay identical.
|
||||
|
||||
## Complete proof on the exact child base
|
||||
|
||||
| Complete owner/sibling phase | Passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|
|
||||
| Baseline | 66 | 0 | 0 |
|
||||
| Candidate | 65 | 0 | 0 |
|
||||
| Full revert | 66 | 0 | 0 |
|
||||
| Restored candidate | 65 | 0 | 0 |
|
||||
| Final production restore | 65 | 0 | 0 |
|
||||
|
||||
The integration owner has 52 cases before and 51 after. The unchanged extracted
|
||||
retry sibling has 14 cases. All phases run serially in this isolated worktree on
|
||||
exact parent `c1ae082a7a49c239a22e5149e223a41325b0ba2d`. No test execution overlaps
|
||||
the ZIP proof; a process scan confirmed its test processes had exited first.
|
||||
|
||||
```sh
|
||||
node node_modules/vitest/vitest.mjs run --project @paperclipai/server server/src/__tests__/heartbeat-retry-scheduling.test.ts server/src/services/heartbeat/retries.test.ts --exclude '**/dist/**' --pool=forks --maxWorkers=1
|
||||
```
|
||||
|
||||
The unmutated selected keeper passes 1/1. The actual mutation replaces the one
|
||||
`if (await legacyExecutionNeedsReconciliationWithEvidence(db, run))` guard at
|
||||
`server/src/services/heartbeat/retries.ts:501` with
|
||||
`if (false && await legacyExecutionNeedsReconciliationWithEvidence(db, run))`.
|
||||
The same keeper then fails at its exact disposition/code matcher on candidate
|
||||
`heartbeat-retry-scheduling.test.ts:2057:23`. The real scheduler returns an actual
|
||||
scheduled successor instead of the required reconciliation result. That run
|
||||
exits 1 with exactly one selected failure; the other 50 cases are filtered out
|
||||
and are not claimed as complete coverage. The production file is restored
|
||||
byte-for-byte before the final complete 65/65 run.
|
||||
|
||||
Selected command: the integration file only with
|
||||
`-t 'requires reconciliation for an error-code-only Codex harness crash'`.
|
||||
The harness adds default and JSON reporters and a separate output file per phase.
|
||||
Whole-file byte-slice equality proves every test byte outside the single removed
|
||||
body is unchanged. No assertion transfers or production edits are committed.
|
||||
|
||||
## Setup recovery and practical limits
|
||||
|
||||
The first child baseline was rejected: 11 non-database cases passed and 55
|
||||
cases skipped because Postgres could not create its 56-byte SysV tracking
|
||||
segment. An isolated initdb diagnostic confirmed shared-memory-ID exhaustion;
|
||||
`kern.sysv.shmmni` was 32 and all 32 IDs were occupied. This was not a test failure
|
||||
or disk-space failure.
|
||||
|
||||
The authorized guarded recovery removed 18 stale segments. For each segment an
|
||||
immediate `ipcs` read confirmed NATTCH=0 and a process scan confirmed both creator
|
||||
and last-user PIDs absent. All 14 attached segments were preserved. No live
|
||||
Postgres process was stopped, and no kernel limits were changed. Before/after
|
||||
state and per-ID checks are saved in the proof packet's `ipc-cleanup.json`.
|
||||
The exact baseline retry succeeded, followed by all accepted controls above.
|
||||
The rejected setup run is preserved in `rejected-baseline-shm/`.
|
||||
|
||||
This worktree uses its own frozen offline dependencies, native Postgres package
|
||||
symlink hydration, and shared/SDK build prerequisites. The successful source
|
||||
removal is 26 lines and one execution; no CI wall-time improvement is claimed.
|
||||
`git diff --check` passes. Broad typecheck/build and unrelated suites were not run
|
||||
for this narrow test deletion. CI, GitHub duplicate search and reviewer gates
|
||||
remain open. Related prior audit: PR #16075; its files are excluded here.
|
||||
|
||||
Model: OpenAI `gpt-6.1-sol` (GPT-6.1-Sol); context window unavailable. Tool use and
|
||||
code execution supplied the source inspection and proof.
|
||||
|
||||
Original test SHA-256: `92425b672e93810e0de2d21efdcb65c902b1c90926fee2a941322445172a65fe`.
|
||||
Candidate test SHA-256: `1fcad08c3af36e539e7f2c62d706605f65a70c9394026b0beb4ffb8aecc7c867`.
|
||||
Unchanged sibling SHA-256: `ad98a02f6112a21a8975235db6a9dec227020221bf048c8dfcbdbb55c6ce2047`.
|
||||
Restored production-owner SHA-256: `9f29feb6d986f281cbfd849e0152f49cf56e18974c6df1bc801010dcce4c877a`.
|
||||
|
||||
Full commands, source hashes, phase JSON/logs and exact failure stack:
|
||||
`/private/tmp/codex-reconciliation-child-proof/receipt.json` and sibling files.
|
||||
@@ -0,0 +1,216 @@
|
||||
# CompanyExport proof receipt
|
||||
|
||||
Historical proof base `8e955181d8a3776acc86e4662de3c77933231a76`; OpenAI GPT-6.1-Sol.
|
||||
|
||||
Two same-render retirements preserve the complete original check union: empty fidelity-panel absence joins the automatic-preview keeper; initial disabled/unchecked attachment and dimmed task-folder checks join the rich-download keeper before clicks. Existing fidelity API and 3-of-6 file-count assertions remain. Whole-program AST proof confirms all other fixtures, hooks, provider inputs, actions and cases unchanged.
|
||||
|
||||
| Full owner phase | Passed | Failed / skipped | Exit |
|
||||
|---|---:|---:|---:|
|
||||
| Baseline | 13 | 0 / 0 | 0 |
|
||||
| Candidate | 11 | 0 / 0 | 0 |
|
||||
| Full revert | 13 | 0 / 0 | 0 |
|
||||
| Restored candidate | 11 | 0 / 0 | 0 |
|
||||
|
||||
Four independent actual production controls each selected one keeper and exited 1 at its intended guard: render an empty fidelity panel (line 253); enable disabled attachments (317); check disabled attachments (318); stop dimming the excluded tasks directory (319). Each had 0 passing / 1 failing selected case; 10 other cases were filtered out, with no source skips added. Production bytes restored after each; final SHA-256 `52775673ad41a2156e9e3f23016ca7e957fc552930139b2048685a55c10d6d2d`.
|
||||
|
||||
Saving: 2 registered root/provider renders, 14 net test lines (5 added / 19 removed), no measured runtime claim. Initial anchored selector selected zero cases and was rejected by the harness; log/JSON preserved, all controls rerun with corrected unique selectors. Existing React act warnings remain. Diff check passes. No broad suite/typecheck/build for this proof-only proposal. No production changes, branch, commit, push or PR.
|
||||
|
||||
## Inspectable reproduction
|
||||
|
||||
The results above are historical, recorded on 2026-10-13. This documentation repair
|
||||
checks the Git source and mutation hashes; it does not claim fresh test results.
|
||||
Historical raw reports remain local and are not bundled. The relevant failure
|
||||
excerpts and exact executed production edits are included here; no private proof
|
||||
file is needed to reconstruct the checks.
|
||||
|
||||
Run the block from a repository checkout with the public `base` and `head` Git
|
||||
revisions below. Fetch those public PR/revisions first if they are absent.
|
||||
Python 3.12+, Git and Node are required. The UI and Codex recipes also require
|
||||
pnpm 9.15.4 and network access to install dependencies.
|
||||
|
||||
The script uses an isolated snapshot and obtains the candidate test directly
|
||||
from its Git revision. It runs the full baseline and candidate owners, applies
|
||||
each mutation independently, checks the intended failing case, restores exact
|
||||
source bytes in `finally`, and reruns the full candidate. Counts are
|
||||
`[total, passed, failed, skipped-or-filtered]`. Original-owner mutation counts
|
||||
above are historical; this recipe repeats candidate sensitivity checks.
|
||||
Prefix the command with `AUDIT_RECONSTRUCT_ONLY=1` to verify source and mutation
|
||||
hashes without extracting a snapshot, installing dependencies or running tests.
|
||||
|
||||
```sh
|
||||
python3 - <<'PY'
|
||||
import hashlib, io, json, os, re, subprocess, tarfile, tempfile
|
||||
from pathlib import Path
|
||||
|
||||
spec = {'base': '8e955181d8a3776acc86e4662de3c77933231a76',
|
||||
'head': '34270ee5c767a561fe34fe57a6d807d1a9496f70',
|
||||
'owner': 'ui/src/pages/CompanyExport.test.tsx',
|
||||
'kind': 'ui',
|
||||
'hashes': {'ui/src/pages/CompanyExport.test.tsx': '096af584a2f94cd423a59296b6e7af160081e95f6234747573973197f766cb0c',
|
||||
'ui/src/pages/CompanyExport.tsx': '52775673ad41a2156e9e3f23016ca7e957fc552930139b2048685a55c10d6d2d'},
|
||||
'candidate_hash': 'a6cceb2266154c372d9081ec79ca3dcd8e3acb6994e2599bbc98dd11f8988012',
|
||||
'baseline': [13, 13, 0, 0],
|
||||
'candidate': [11, 11, 0, 0],
|
||||
'mutations': [{'name': 'empty-fidelity',
|
||||
'path': 'ui/src/pages/CompanyExport.tsx',
|
||||
'changes': [['{fidelityReport && fidelityReport.warnings.length > 0 && (',
|
||||
'{fidelityReport && (']],
|
||||
'hash': 'a9f8c23c46b994199221719de840292a9c8c2c9ff613e5f4e220441b5c4cd0ea',
|
||||
'counts': [11, 0, 1, 10],
|
||||
'guard': 'loads the no-task export automatically without an interstitial',
|
||||
'message': "AssertionError: expected 'Paperclip exportExporting 1 of 1 file…' not to "
|
||||
"contain 'Not included in this export'",
|
||||
'selector': 'loads the no-task export automatically without an interstitial',
|
||||
'line': 253},
|
||||
{'name': 'attachments-enabled',
|
||||
'path': 'ui/src/pages/CompanyExport.tsx',
|
||||
'changes': [['const disabled = isAttachments && '
|
||||
'!isAttachmentsCategoryEnabled(categories);',
|
||||
'const disabled = false;']],
|
||||
'hash': '0b978fefc8f332f4863d16d8af9a76ebdd2d6916fc3778c588fceee08173a3ae',
|
||||
'counts': [11, 0, 1, 10],
|
||||
'guard': 'keeps task history opt-in, then requests all selected files on download',
|
||||
'message': 'AssertionError: expected false to be true // Object.is equality',
|
||||
'selector': 'keeps task history opt-in, then requests all selected files on download',
|
||||
'line': 317},
|
||||
{'name': 'attachments-checked',
|
||||
'path': 'ui/src/pages/CompanyExport.tsx',
|
||||
'changes': [['const checked = categories[key] && !disabled;',
|
||||
'const checked = isAttachments && disabled ? true : categories[key] && '
|
||||
'!disabled;']],
|
||||
'hash': '966c964b9b8c124cb60c280146619eca379095a89ddb8930420365614a72498d',
|
||||
'counts': [11, 0, 1, 10],
|
||||
'guard': 'keeps task history opt-in, then requests all selected files on download',
|
||||
'message': 'AssertionError: expected true to be false // Object.is equality',
|
||||
'selector': 'keeps task history opt-in, then requests all selected files on download',
|
||||
'line': 318},
|
||||
{'name': 'undimmed-task-folder',
|
||||
'path': 'ui/src/pages/CompanyExport.tsx',
|
||||
'changes': [['if (!anyIncluded) tones[node.path] = "muted";',
|
||||
'if (!anyIncluded && node.path !== "tasks") tones[node.path] = "muted";']],
|
||||
'hash': 'f1f8f968e1ffe2bd26d4158ba86f6639531131bd96d728a0b62811d6f27f71ab',
|
||||
'counts': [11, 0, 1, 10],
|
||||
'guard': 'keeps task history opt-in, then requests all selected files on download',
|
||||
'message': "AssertionError: expected 'group grid w-full grid-cols-(--gtc-3)…' to "
|
||||
"contain 'opacity-50'",
|
||||
'selector': 'keeps task history opt-in, then requests all selected files on download',
|
||||
'line': 319}]}
|
||||
|
||||
def digest(data):
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
def blob(rev, path):
|
||||
return subprocess.check_output(["git", "show", rev + ":" + path])
|
||||
|
||||
owner = spec["owner"]
|
||||
original = {p: blob(spec["base"], p) for p in spec["hashes"]}
|
||||
candidate = blob(spec["head"], owner)
|
||||
assert all(digest(b) == spec["hashes"][p] for p, b in original.items())
|
||||
assert digest(candidate) == spec["candidate_hash"]
|
||||
mutants = []
|
||||
for m in spec["mutations"]:
|
||||
text = original[m["path"]].decode()
|
||||
for before, after in m["changes"]:
|
||||
assert text.count(before) == 1, (m["name"], "ambiguous mutation")
|
||||
text = text.replace(before, after)
|
||||
data = text.encode()
|
||||
assert digest(data) == m["hash"], m["name"]
|
||||
mutants.append((m, data))
|
||||
if os.environ.get("AUDIT_RECONSTRUCT_ONLY") == "1":
|
||||
print("Original, candidate and all mutation hashes match historical proof.")
|
||||
raise SystemExit(0)
|
||||
|
||||
archive = subprocess.check_output(["git", "archive", spec["base"]])
|
||||
with tempfile.TemporaryDirectory(prefix="paperclip-audit-") as directory:
|
||||
root = Path(directory)
|
||||
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
|
||||
tar.extractall(root, filter="data")
|
||||
if spec["kind"] != "tap":
|
||||
assert subprocess.check_output(["pnpm", "--version"], cwd=root, text=True).strip() == "9.15.4"
|
||||
subprocess.run(["pnpm", "install", "--frozen-lockfile"],
|
||||
cwd=root, check=True)
|
||||
|
||||
def run(label, expected, mutation=None):
|
||||
report = root / "audit-report.json"
|
||||
if report.exists():
|
||||
report.unlink()
|
||||
if spec["kind"] == "tap":
|
||||
command = ["node", "--test", "--test-reporter=tap", owner]
|
||||
elif spec["kind"] == "ui":
|
||||
command = ["pnpm", "--filter", "@paperclipai/ui",
|
||||
"exec", "vitest", "run", "src/pages/CompanyExport.test.tsx",
|
||||
"--maxWorkers=1", "--reporter=default", "--reporter=json",
|
||||
"--outputFile.json=" + str(report)]
|
||||
else:
|
||||
command = ["node", "node_modules/vitest/vitest.mjs", "run", "--config",
|
||||
"packages/adapters/codex-local/vitest.config.ts", owner,
|
||||
"--reporter=json", "--outputFile=" + str(report)]
|
||||
if mutation and mutation.get("selector"):
|
||||
command += ["-t", mutation["selector"]]
|
||||
result = subprocess.run(command, cwd=root, capture_output=True, text=True,
|
||||
timeout=180)
|
||||
output = result.stdout + result.stderr
|
||||
print(label, "exit", result.returncode, output)
|
||||
if spec["kind"] == "tap":
|
||||
counts = {k: int(v) for k, v in re.findall(
|
||||
r"^# (tests|pass|fail|skipped) (\d+)$", output, re.M)}
|
||||
actual = [counts[k] for k in ["tests", "pass", "fail", "skipped"]]
|
||||
if mutation:
|
||||
failures = re.findall(
|
||||
r"^not ok \d+ - (.*?)\n(.*?)(?=^(?:ok|not ok) \d+ - |^1\.\.|\Z)",
|
||||
output, re.M | re.S)
|
||||
assert any(name == mutation["guard"] and mutation["message"] in body
|
||||
and "ERR_ASSERTION" in body for name, body in failures), label
|
||||
else:
|
||||
data = json.loads(report.read_text())
|
||||
actual = [data[k] for k in ["numTotalTests", "numPassedTests",
|
||||
"numFailedTests", "numPendingTests"]]
|
||||
if mutation:
|
||||
hits = [a for suite in data["testResults"] for a in suite["assertionResults"]
|
||||
if a["status"] == "failed" and mutation["guard"] in a["fullName"]]
|
||||
assert any(mutation["message"] in msg for a in hits
|
||||
for msg in a["failureMessages"]), label
|
||||
if mutation.get("line"):
|
||||
assert any(f'CompanyExport.test.tsx:{mutation["line"]}:' in msg
|
||||
for a in hits for msg in a["failureMessages"]), label
|
||||
assert actual == expected, (label, actual, expected)
|
||||
assert result.returncode == (1 if mutation else 0), label
|
||||
|
||||
try:
|
||||
run("baseline", spec["baseline"])
|
||||
(root / owner).write_bytes(candidate)
|
||||
run("candidate", spec["candidate"])
|
||||
for m, data in mutants:
|
||||
try:
|
||||
(root / owner).write_bytes(candidate)
|
||||
(root / m["path"]).write_bytes(data)
|
||||
run(m["name"] + "-keeper", m["counts"], m)
|
||||
finally:
|
||||
for path, data in original.items():
|
||||
(root / path).write_bytes(data)
|
||||
assert all((root / p).read_bytes() == b for p, b in original.items())
|
||||
(root / owner).write_bytes(candidate)
|
||||
run("restored-candidate", spec["candidate"])
|
||||
finally:
|
||||
for path, data in original.items():
|
||||
(root / path).write_bytes(data)
|
||||
assert all((root / p).read_bytes() == b for p, b in original.items())
|
||||
PY
|
||||
```
|
||||
|
||||
Recorded intended failure excerpts (fixture path prefixes omitted):
|
||||
|
||||
```text
|
||||
empty-fidelity / loads the no-task export automatically without an interstitial
|
||||
AssertionError: expected 'Paperclip exportExporting 1 of 1 file…' not to contain 'Not included in this export'
|
||||
CompanyExport.test.tsx:253
|
||||
attachments-enabled / keeps task history opt-in, then requests all selected files on download
|
||||
AssertionError: expected false to be true // Object.is equality
|
||||
CompanyExport.test.tsx:317
|
||||
attachments-checked / keeps task history opt-in, then requests all selected files on download
|
||||
AssertionError: expected true to be false // Object.is equality
|
||||
CompanyExport.test.tsx:318
|
||||
undimmed-task-folder / keeps task history opt-in, then requests all selected files on download
|
||||
AssertionError: expected 'group grid w-full grid-cols-(--gtc-3)…' to contain 'opacity-50'
|
||||
CompanyExport.test.tsx:319
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
# Company UI test consolidation proof
|
||||
|
||||
Proof base: PR #16111 exact head `06c1b2035df44fa03c065ddd69e8bf8df8513b86`. Proof used GPT-6.1-Sol. The changes affect only test files.
|
||||
|
||||
| Owner | Original | Candidate | Full original-file restore | Candidate restored |
|
||||
|---|---:|---:|---:|---:|
|
||||
| `ui/src/pages/CompanyImport.test.tsx` | 29/29 | 28/28 | 29/29 | 28/28 |
|
||||
| `ui/src/pages/CompanyEnvironments.test.tsx` | 32/32 | 31/31 | 32/32 | 31/31 |
|
||||
|
||||
All complete-owner runs exited 0, with no failures or skips. Both production mutations below exited 1 with exactly one failure in the keeper. The mutated production files were restored byte for byte before the final candidate runs. The reduction removes 2 test registrations and 15 net test lines. The isolated runs were about 7 seconds and 4 seconds per owner; this is not a CI timing claim.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Use a clean checkout at the proof base with dependencies installed. Run commands from the repository root.
|
||||
|
||||
```sh
|
||||
pnpm --filter @paperclipai/ui exec vitest run src/pages/CompanyImport.test.tsx --maxWorkers=1
|
||||
pnpm --filter @paperclipai/ui exec vitest run src/pages/CompanyEnvironments.test.tsx --maxWorkers=1
|
||||
```
|
||||
|
||||
Expected original counts: 29 and 32 passing tests. Apply the PR's two test-file changes and rerun the same commands: 28 and 31 passing tests. Restore both complete original test files from the proof-base commit and rerun: 29 and 32 passing. Reapply the candidate changes and rerun: 28 and 31 passing.
|
||||
|
||||
## Production mutation controls
|
||||
|
||||
For CompanyImport, apply the candidate test change, then in `ui/src/pages/CompanyImport.tsx` replace the success-path invalidation below:
|
||||
|
||||
```ts
|
||||
await queryClient.invalidateQueries({ queryKey: queryKeys.companies.all });
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```ts
|
||||
await queryClient.invalidateQueries({ queryKey: ["audit-unrelated-company-query"] });
|
||||
```
|
||||
|
||||
Run only the keeper:
|
||||
|
||||
```sh
|
||||
pnpm --filter @paperclipai/ui exec vitest run src/pages/CompanyImport.test.tsx --maxWorkers=1 -t 'submits the import as an async job, then activates selected agents and routines'
|
||||
```
|
||||
|
||||
Expected result: exit 1 at the `invalidateQueries` query-key assertion. The control observed the unrelated key in the real QueryClient call. Restore `CompanyImport.tsx` from the proof-base commit and verify its SHA-256 is `872d67109ab968fada24f126ad92974c5d060f7552dc4e8299a6185d2b87fe6c`.
|
||||
|
||||
For CompanyEnvironments, apply the candidate test change, then in `ui/src/pages/CompanyEnvironments.tsx` replace the Local option:
|
||||
|
||||
```tsx
|
||||
<option value="">Local</option>
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```tsx
|
||||
null
|
||||
```
|
||||
|
||||
Run only the keeper:
|
||||
|
||||
```sh
|
||||
pnpm --filter @paperclipai/ui exec vitest run src/pages/CompanyEnvironments.test.tsx --maxWorkers=1 -t 'opens the add-environment form on a standalone page and closes it on cancel'
|
||||
```
|
||||
|
||||
Expected result: exit 1 at the migrated `Local` option assertion. Restore `CompanyEnvironments.tsx` from the proof-base commit and verify its SHA-256 is `871d93af7394090ccdb6ffaaa518c7462055ffb75766e6b9736afdd34e18884b`.
|
||||
|
||||
After both restores, rerun the complete candidate-owner commands. They must pass 28/28 and 31/31. Keep the candidate changes in the two test files; do not leave either production mutation applied.
|
||||
|
||||
## Retained behavior
|
||||
|
||||
CompanyImport retains the full async import and activation assertions. The keeper adds a QueryClient invalidation spy and confirms the companies list is invalidated before activation. Its `finally` path restores the spy.
|
||||
|
||||
CompanyEnvironments moves the implicit Local-option assertion into the existing add-environment scenario after the same initial render and React flush, before opening Add. The same scenario retains the form and cancel assertions.
|
||||
|
||||
The proof also checked that retained test names, fixtures, provider state, actions, hooks, and assertions were unchanged beyond these migrations. There are no skips or production changes in this pull request.
|
||||
@@ -0,0 +1,29 @@
|
||||
# DB client test migration-pass audit
|
||||
|
||||
This test-only change removes 15 redundant initial `applyPendingMigrations(connectionString)` calls from `packages/db/src/client.test.ts`. Every test declaration and assertion stays unchanged. No production code, migration, import, or fixture helper changes.
|
||||
|
||||
## Why the calls are redundant
|
||||
|
||||
`createTempDatabase()` awaits `startEmbeddedPostgresTestDatabase()` before returning its connection string. The shared fixture awaits `ensurePostgresDatabase()` and `applyPendingMigrations()` before returning on both the fresh and template paths. Each removed call immediately followed `await createTempDatabase()`, with no intervening schema, migration-journal, or fixture-data mutation. It therefore inspected the same fully migrated database again.
|
||||
|
||||
For this fixture's hash-based migration journal, the repeated pass enters `inspectMigrations()`, opens a one-connection DB utility pool, and loads the applied hashes. `mapHashesToMigrationFiles()` reads and SHA-256 hashes every migration SQL file. The directory contains 334 SQL files at this revision. Removing 15 such passes avoids 15 × 334 = **5,010 migration SQL file reads and hashes**, plus **15 DB utility pool passes**. These are operation counts derived from the source and migration directory, not measured seconds or a CI speedup estimate. The fixture's required migration pass remains.
|
||||
|
||||
## Exact removal boundary
|
||||
|
||||
The removed calls were at original lines 204, 274, 327, 371, 401, 473, 567, 633, 699, 773, 877, 1010, 1406, 1983, and 2011. The import stays used by the replay tests.
|
||||
|
||||
All migration calls after journal or schema mutation remain, including the original calls at lines 191, 226, 295, 358, 433, 527, 598, 664, 735, 811, 971, 1045, 1297, 1384, 1510, 1604, 1930, 1998, and 2065. These preserve older-clone upgrades; renumbered recovery replay; inserted earlier migrations; 0044, 0046, 0047, 0048, 0050, and 0059 replay; built-in managed-resource history; 0134 inbox archives; all three 0135 repair/no-op passes; run-responsible-user repair; native persistence/status-versioning replay; provider-trace idempotence; and retired-model-profile cleanup. No replay call is removed from the board-key uniqueness test, which has no later mutation requiring migration.
|
||||
|
||||
The fixture-helper tests remain unchanged, including `publishes a complete template and starts suites from a copy of it` and `runs the fresh initdb path when PAPERCLIP_TEST_POSTGRES_TEMPLATE=0`. Both assert that the returned database reports `upToDate`.
|
||||
|
||||
## Verification
|
||||
|
||||
```sh
|
||||
node_modules/.bin/vitest run --project @paperclipai/db packages/db/src/client.test.ts packages/db/src/test-embedded-postgres.test.ts
|
||||
```
|
||||
|
||||
Complete owner replay: **2 files passed, 35 tests passed, 0 failed, 0 skipped; 26.77 seconds**. Real embedded PostgreSQL executed, including the fresh and template fixture-owner tests. The first sandboxed invocation encountered `listen EPERM` on localhost and was interrupted; the successful identical command ran with local socket access. No baseline timing comparison was run.
|
||||
|
||||
`git diff --check` passes. A byte-level reconstruction against HEAD confirms that the client test file retains every original line except the 15 specified call lines. It retains 19 `applyPendingMigrations(connectionString)` references and the corresponding import.
|
||||
|
||||
Risk: these tests now rely directly on the fixture's existing fully-migrated return contract. The fresh/template owner tests cover that contract. Mutation and idempotence coverage remains in the same client suite.
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"auditBase": "e868e6645ce0d598d756d3458f0b3d71afd6db4a",
|
||||
"historicalWeightsMs": {
|
||||
"selection": 1173,
|
||||
"phase6": 19600,
|
||||
"finalizer": 19882
|
||||
},
|
||||
"owners": {
|
||||
"runtimeMode": 27,
|
||||
"evidenceClassifier": 4,
|
||||
"controlPlanePort": 34
|
||||
},
|
||||
"removedAliasExecutions": 99,
|
||||
"restoration": {
|
||||
"owner": "server/src/services/native-runtime/paperclip-control-plane-port.ts",
|
||||
"originalSha256": "621beee8a451e9cab4fc09f886bfb4d95512c1433f82b7f9cbd3ea83fd2f3d0c",
|
||||
"restoredSha256": "621beee8a451e9cab4fc09f886bfb4d95512c1433f82b7f9cbd3ea83fd2f3d0c",
|
||||
"restored": true
|
||||
},
|
||||
"runs": {
|
||||
"execution-audit-pure-baseline": {
|
||||
"path": "/private/tmp/execution-audit-pure-baseline.json",
|
||||
"sha256": "1b5a7f42848ccd40eabbe409e8eb6331f0f41f70c12c54c11715cc29402148fb",
|
||||
"passed": 31,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"success": true
|
||||
},
|
||||
"execution-audit-port-baseline": {
|
||||
"path": "/private/tmp/execution-audit-port-baseline.json",
|
||||
"sha256": "9976aed11c8310e5c7245565413b1b4582367b8ec072006b79e05084c6c5e3c5",
|
||||
"passed": 0,
|
||||
"failed": 0,
|
||||
"pending": 34,
|
||||
"success": false
|
||||
},
|
||||
"execution-audit-port-baseline-repro": {
|
||||
"path": "/private/tmp/execution-audit-port-baseline-repro.json",
|
||||
"sha256": "ea16340d52952745b68ab8050e3c7406a6d61e1aaf142e1705a64066259b9e93",
|
||||
"passed": 0,
|
||||
"failed": 0,
|
||||
"pending": 34,
|
||||
"success": false
|
||||
},
|
||||
"execution-audit-port-baseline-repaired": {
|
||||
"path": "/private/tmp/execution-audit-port-baseline-repaired.json",
|
||||
"sha256": "cb3cd844a49d88dd762630a0914817ac389fbde89370b2917b21c3d5e4a1ad73",
|
||||
"passed": 34,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"success": true
|
||||
},
|
||||
"execution-audit-selection-replay": {
|
||||
"path": "/private/tmp/execution-audit-selection-replay.json",
|
||||
"sha256": "96935cbb6530793b848728372233281d7945e0d6634ad19faa177ef518c3aefb",
|
||||
"passed": 54,
|
||||
"failed": 0,
|
||||
"pending": 0,
|
||||
"success": true
|
||||
},
|
||||
"execution-audit-port-mutation": {
|
||||
"path": "/private/tmp/execution-audit-port-mutation.json",
|
||||
"sha256": "e9fcf577361e22373a9f07be42e53e5690290c57a630029ed78e7e9978da6cd5",
|
||||
"passed": 0,
|
||||
"failed": 1,
|
||||
"pending": 33,
|
||||
"success": false
|
||||
},
|
||||
"execution-audit-port-control": {
|
||||
"path": "/private/tmp/execution-audit-port-control.json",
|
||||
"sha256": "59f030337cea965a4a135a62f0fe8315ed52f0bffd2ed5311040094d4517049e",
|
||||
"passed": 1,
|
||||
"failed": 0,
|
||||
"pending": 33,
|
||||
"success": true
|
||||
}
|
||||
},
|
||||
"deliveryBase": "770cc3be7e294d606d83599c6f6a3232d47d4c70",
|
||||
"branch": "codex/prune-native-acceptance-wrappers",
|
||||
"candidate": {
|
||||
"jsonSha256": "d7fe27f05c8ea33682a69f336e196020502cb42ae04f4717d9c8ffa7ef18c31c",
|
||||
"passed": 65,
|
||||
"failed": 0,
|
||||
"pending": 0
|
||||
},
|
||||
"retainedOwnerSha256": {
|
||||
"server/src/services/native-runtime/runtime-mode.test.ts": "c32e6b5276fc30a78f5f91ad7c9b46438d4b08917a3d933bbb1d991ef2b4d7e1",
|
||||
"server/src/services/native-runtime/evidence-classifier.test.ts": "4567ab0fa44b311d83110c1fe7a5d6051cf099131db019f1024858cc3c6e7d0c",
|
||||
"server/src/services/native-runtime/paperclip-control-plane-port.test.ts": "d236a5239fd4c7e15447740b04d160a51d268d555c7fb43c42e99cca4c417985"
|
||||
},
|
||||
"mutationFailure": {
|
||||
"case": "revalidates every persisted native binding before open and completion",
|
||||
"assertionLine": 1131,
|
||||
"message": "promise resolved \"undefined\" instead of rejecting",
|
||||
"selectedFailed": 1,
|
||||
"filteredPending": 33
|
||||
},
|
||||
"defaultShardSelection": {
|
||||
"shards": 12,
|
||||
"selectedFiles": 886,
|
||||
"uniqueFiles": 886,
|
||||
"eachOwnerCount": 1,
|
||||
"retiredAliasCount": 0,
|
||||
"retainedInputAliasCount": 1
|
||||
},
|
||||
"stockShardTests": {
|
||||
"passed": 29,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"logSha256": "bd0026448f3025cc8166d9ef02e8c90252e66da45af693717ef744fdbb7e0576"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# Execution wrapper test audit — 2026-10-13
|
||||
|
||||
Remove three import-only acceptance aliases while retaining their complete production-boundary owners. This retires **99 duplicate case registrations and nine test lines**, with **zero production behavior changes**. Complete retained-owner baselines and the final candidate each pass **65/65 cases**, with no failures or skips.
|
||||
|
||||
Delivery base: `770cc3be7e294d606d83599c6f6a3232d47d4c70`, the merged root after #16188. Qualification began at `e868e6645ce0d598d756d3458f0b3d71afd6db4a`; all three owners, the mutated production owner, and affected ownership references are byte-identical between those bases. Followed the OpenClaw [test-audit skill](https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/SKILL.md) and its candidate-evidence and retention rules.
|
||||
|
||||
## Retired aliases and retained contracts
|
||||
|
||||
| Retired server acceptance alias | Complete retained owner | Repeated cases | Removed test lines | Historical CI weight |
|
||||
|---|---|---:|---:|---:|
|
||||
| `heartbeat-native-runner-selection.test.ts` | `services/native-runtime/runtime-mode.test.ts` | 27 | 2 | 1,173 ms |
|
||||
| `native-runner-phase6.integration.test.ts` | `services/native-runtime/paperclip-control-plane-port.test.ts` | 34 | 3 | 19,600 ms |
|
||||
| `native-run-finalizer.test.ts` | `services/native-runtime/paperclip-control-plane-port.test.ts` plus `services/native-runtime/evidence-classifier.test.ts` | 38 | 4 | 19,882 ms |
|
||||
|
||||
Every retired executable line imports one of those test modules. There are no alias-owned assertions, fixtures, mocks, hooks, filters, configuration, or entry-point actions. All aliases originated unchanged in `25cf079ec5` (#12591, 2026-08-31), when server Vitest already included the colocated owners. The shared `src/**/*.test.ts` configuration, setup file, isolated forks, and default server walker execute the same owners. An actual current-Vitest run of runtime-mode plus its alias passes 54/54, demonstrating the duplicate registration.
|
||||
|
||||
Retain every runtime-selection, compatibility, persisted binding, authorization, redaction, event/replay, result, governance, review, transactional ordering, rollback, workspace-failure, and durable-evidence assertion in the three owners. Their bytes, table rows, timers, awaits, and budgets are unchanged. Independent parent review confirmed that the six-file cleanup removes only these import aliases and redirects the affected pointers.
|
||||
|
||||
Retain `native-runner-input-boundary.test.ts`: it imports a Runner package suite omitted from the root default project list. Deleting that superficially similar alias would remove root-suite input-contract coverage. Prior audit receipts and excluded adapter, native-session, recovery, workspace-runtime, UI, and E2E areas were not revisited.
|
||||
|
||||
The protocol-coverage generator and generated artifact now name the actual runtime-mode owner. The standalone-thin documentation invokes the three canonical owners directly and removes retired aliases from its file inventories. These labels carry no separate executable matrix signal; no acceptance requirement, source assertion, CI route, or production seam changes.
|
||||
|
||||
## Qualification and reversal
|
||||
|
||||
- Baseline: runtime-mode 27/27, evidence-classifier 4/4, and the complete real-Postgres control-plane owner 34/34. Final candidate: the same three files, 65/65, zero failures/skips.
|
||||
- Mutation: bypass only the persisted `run.nativeIssueId === this.#binding.issueId` comparison in `PaperclipControlPlanePort`. The retained `revalidates every persisted native binding before open and completion` fails at its rejection assertion (line 1131): the first corrupted task-binding open resolves `undefined` instead of rejecting. Setup completed; this was neither a timeout nor a harness failure.
|
||||
- Reversal: restore the production owner byte for byte, then the exact selected guard passes. Its 33 excluded cases are intentional name-filter skips, not a complete-file control claim. Restoration SHA-256: `621beee8a451e9cab4fc09f886bfb4d95512c1433f82b7f9cbd3ea83fd2f3d0c`.
|
||||
- Default routing: 886 unique general-server files across all 12 stock shards; each retained owner appears exactly once, retired aliases zero times, and the required package-input alias once. Stock shard-routing tests pass 29/29.
|
||||
- Runner `build:typescript`, generated protocol-coverage `--check`, generator syntax check, owner byte comparisons, and `git diff --check` pass.
|
||||
|
||||
Focused candidate command:
|
||||
|
||||
```sh
|
||||
pnpm --filter @paperclipai/server exec vitest run \
|
||||
src/services/native-runtime/runtime-mode.test.ts \
|
||||
src/services/native-runtime/evidence-classifier.test.ts \
|
||||
src/services/native-runtime/paperclip-control-plane-port.test.ts
|
||||
```
|
||||
|
||||
## Cost and limits
|
||||
|
||||
The **40.655 seconds** total above is historical raw suite weight from `scripts/general-server-shard-durations.json`, averaged from PR CI runs 38076844891 and 38076972444 (2026-10-10). It is not a fresh causal speed measurement or a prediction of critical-path job savings. No timing claim comes from removed line counts. The successful full port baseline took 16.472 seconds of test time (25.84-second invocation); the final three-owner candidate took 26.08 seconds total. Their scopes and host state differ, so they are not an A/B speed comparison.
|
||||
|
||||
Two initial database attempts failed before any assertions because the isolated `--ignore-scripts` install omitted Postgres dylib symlinks. The dependency's standard hydration script repaired that isolated setup; subsequent real-DB baseline, mutation, reversal, and candidate runs reached their intended assertions. Read-only shared-memory inventories showed 29 then 30 allocations against a 32-entry limit. No unrelated process was inspected, stopped, or changed. These failed setup attempts are excluded from the passing counts.
|
||||
|
||||
This is complete affected-owner proof. A combined execution of all retired aliases was not repeated; doing so would replay the expensive unchanged database owner. Vitest's combined `list` collection reported no-suite errors on import aliases because it reuses module registrations; collection is not used as runtime preservation proof. No complete repository typecheck, default suite, full build, Rust qualification, browser suite, or fresh remote CI pass was obtained for this local branch. Their absence is not represented as a full repository pass. Publication follows the parent's sequential stack disposition.
|
||||
|
||||
[Compact controls and provenance](2026-10-13-execution-wrapper-test-audit-controls.json) retain source/run hashes, counts, and the intended mutation failure. Raw logs and run JSON remain temporary local audit evidence; digests identify them but do not make them durable payloads.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Proven Grok stale-promotion cleanup consolidation
|
||||
|
||||
Proof base: #16106 exact head 397459400c6e7b18b462dcac3d8886b390ca8e8c. Supplied patch SHA256 verified as 06b3f1bdafc81bc0da4b47d18d9348101cb94a647de2da269bfe4903b73f6a3a. Original test, promotion production module, decision wrapper/CJS, home resolver and directory lock source match exact-head blobs. Isolated temporary snapshot only; serial full-owner runs; no shared checkout edits, commits, pushes or PR.
|
||||
|
||||
Result: PROVEN. Owner packages/adapters/grok-local/src/server/adapter-auth-promotion.test.ts shrinks 658 -> 633 lines (25 net); cases 30 -> 29; one fixture/two promotion calls retired. The production call chain also removes one real local Node predicate child: the empty-home first promotion seeds in-process, while the second same-identity promotion calls writeSameIdentityCredentialIfFresher → decideGrokAuthMerge → execFile("node", [grok-auth-merge-decision.cjs,...]). This is a source-proven call delta, not a measured child counter or provider run. Supplied static map's zero-subprocess statement overlooked that local predicate. No wall-time claim.
|
||||
|
||||
Keeper `keeps the home when the login is older than the existing same-identity credential` gains the retired case's actual company-home readdir equality. Retire `leaves no staged temporary file in the company home after a kept (not-fresher) outcome`.
|
||||
|
||||
Both original cases allocate the same root/env, promote UUID_A marker fresher expiry June2026 into companyA, then promote marker staler expiry January2026, user initiated=true, readiness=ready, soleOwner and noopLog. Keeper retains exact kept outcome and persisted fresher-token receipt, then checks actual entries===auth.json. Static Babel proof reports identical fixture/action prefixes after location/comment removal; runtime uses real scoped persistence, canonical directory lock and real decision predicate. Tied-expiry, success cleanup, redaction, user/sole-owner gates and cross-company cases remain untouched. No ownership or expected value changes.
|
||||
|
||||
| Complete-owner phase | Cases | Pass | Fail | Exit |
|
||||
|---|---:|---:|---:|---:|
|
||||
| baseline | 30 | 30 | 0 | 0 |
|
||||
| candidate | 29 | 29 | 0 | 0 |
|
||||
| reverted-owner | 30 | 30 | 0 | 0 |
|
||||
| kept-temp-leak-original | 30 | 29 | 1 | 1 |
|
||||
| kept-temp-leak-keeper | 29 | 28 | 1 | 1 |
|
||||
| restored-candidate | 29 | 29 | 0 | 0 |
|
||||
| integrated-stack | 29 | 29 | 0 | 0 |
|
||||
|
||||
Every run has zero pending/todo/skipped cases. Reverted-owner is a complete positive run of the exact original source before mutation, not only a source copy.
|
||||
|
||||
## Actual production mutation
|
||||
|
||||
Remove only `await rm(stagedTempPath, { force: true }).catch(() => undefined);` from writeSameIdentityCredentialIfFresher's finally. Leave the separate initial-publish writeAuthFileAtomically cleanup, handle close, decision predicate and rename intact. Actual stale same-identity keep outcome leaks .auth-PID-UUID.tmp alongside auth.json. Original retired case and keeper each fail their real readdir equality directly; each complete owner has exactly one failure. Keeper still observes kept and the correct persisted fresher credential before failing cleanup equality, so the mutation does not substitute a wrong identity/status outcome as proof.
|
||||
|
||||
Verbose original/keeper logs show the actual randomly generated temp filename as a received directory entry; no synthetic filename or expected list is produced by the mutation. Initial JSON-only original-control output abbreviated the received array, so its evidence harness declined to qualify the filename check. Source was restored and the sequence repeated with verbose+JSON reporters; accepted controls show actual leaked entries. The initial positive/control report copies remain in initial-json-reporter-attempt/; no production/assertion modification was used to expose a different outcome.
|
||||
|
||||
Production/test restore occurs in finally and is byte/hash-verified. Restored full candidate passes 29/29; all source finally returns to exact #16106 originals. Test afterEach removes its fake credential fixture roots, including leaked files; async predicate execFile awaits/reaps its child. No real provider, credentials, global instance home or other company was touched.
|
||||
|
||||
## Hashes and integrated stack
|
||||
|
||||
- Original test SHA256: 6925e2b73d52a02ada9457122a2d2262237b937028840443a4aaf38a8f2a2291
|
||||
- Proven candidate test SHA256: 653f85a7b4933e2db534155f3cfbdde1599e0c063b973d490125f58832b7b222
|
||||
- Original/restored promotion production SHA256: aa21740457ceef46e6cddf76dc640f1ed9056e16337ca8ed6def8a2b046e1543
|
||||
|
||||
All four proven shard/provisioning/Codex/Grok patches pass git apply --check and apply together without conflict on exact #16106. Integrated Grok owner passes 29/29 with all four patches present; integrated-receipt.json pins their test hashes and report digest. All four tests restore to exact #16106 bytes afterwards. Other three owners are not claimed rerun in that combined state.
|
||||
|
||||
Use checked-in root Vitest projects config (Grok has no separate config), exact full owner filter, same isolated workspace-source links and immutable third-party dependency graph as the Codex proof. Five additional root third-party links needed for root config loading are recorded in base-hashes.json; no shared workspace source links. No custom mocked resolver, provider, decision or credential comparison replaces production behavior.
|
||||
|
||||
## Checkout reproduction
|
||||
|
||||
Use a fresh checkout at the exact proof base above with repository dependencies installed through the pinned pnpm version. All commands below run from the repository root. After the baseline, fetch the reviewed branch and generate the candidate-only patch: `git fetch origin codex/test-audit-grok-auth-promotion && git diff 397459400c6e7b18b462dcac3d8886b390ca8e8c FETCH_HEAD -- packages/adapters/grok-local/src/server/adapter-auth-promotion.test.ts > /tmp/grok-candidate.patch`. Check and apply it with `git apply --check /tmp/grok-candidate.patch && git apply /tmp/grok-candidate.patch`.
|
||||
|
||||
```sh
|
||||
node node_modules/vitest/vitest.mjs run packages/adapters/grok-local/src/server/adapter-auth-promotion.test.ts --reporter=verbose
|
||||
```
|
||||
|
||||
Expected baseline: 30 passing cases. Apply `/tmp/grok-candidate.patch`, then run the same command: 29 passing cases. Restore the original test from `397459400c6e7b18b462dcac3d8886b390ca8e8c` and rerun the same command: 30 passing cases.
|
||||
|
||||
With the original test restored, execute this exact production mutation:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
owner = Path("packages/adapters/grok-local/src/server/adapter-auth-promotion.ts")
|
||||
source = owner.read_text()
|
||||
start = source.index("async function writeSameIdentityCredentialIfFresher(")
|
||||
end = source.index("/**\n * Promotes a Grok", start)
|
||||
section = source[start:end]
|
||||
cleanup = " await rm(stagedTempPath, { force: true }).catch(() => undefined);"
|
||||
assert section.count(cleanup) == 1
|
||||
owner.write_text(source[:start] + section.replace(cleanup, "") + source[end:])
|
||||
```
|
||||
|
||||
Run the same full-owner test command. Expected original control: 29 passing and one failure at `leaves no staged temporary file in the company home after a kept (not-fresher) outcome`; verbose diff shows actual `.auth-<pid>-<uuid>.tmp` and `auth.json` versus the fixed expected `["auth.json"]`.
|
||||
|
||||
Apply `/tmp/grok-candidate.patch` and run the same command under unchanged mutated production. Expected keeper control: 28 passing and one failure at `keeps the home when the login is older than the existing same-identity credential`, with the same actual-directory mismatch.
|
||||
|
||||
Always restore `packages/adapters/grok-local/src/server/adapter-auth-promotion.ts` from the exact proof-base commit in a finally/trap cleanup, even when either deliberately failing command exits 1. Leave the candidate test applied and rerun the same full-owner command: 29 passing cases. Confirm restored production SHA256 equals the value above. The mutation intentionally changes only the second writer's cleanup line; removing the first writer's cleanup or its handle-close line is a different control and must not be substituted.
|
||||
|
||||
For combined integration, apply the other three proven test patches plus this Grok patch to a fresh exact #16106 checkout, require each `git apply --check` to succeed, and run the same full Grok-owner command: 29 passing. No production mutation remains during this positive check.
|
||||
|
||||
The candidate patch is reproducible from the exact proof base and reviewed branch with the fetch/diff command above. Detailed raw phase logs and JSON remain in the original isolated proof workspace and are not claimed as checked-in deliverables. The mutation and restore steps below are fully specified. No broader typecheck/build/default-suite success claim.
|
||||