Skip to content

fix(claude): bound Desktop library reads and preflight writes - #7301

Closed
luvs01 wants to merge 3 commits into
lidge-jun:devfrom
luvs01:codex/desktop-library-byte-bounds-20261018
Closed

luvs01 wants to merge 3 commits into
lidge-jun:devfrom
luvs01:codex/desktop-library-byte-bounds-20261018

Conversation

@luvs01

@luvs01 luvs01 commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Bound Claude Desktop library profile and metadata reads to 4 MiB. Reject oversized files before allocation, consume at most the limit plus one sentinel byte if a file grows, and recheck path identity after reading. Connection-owned stores pass their existing tighter limits to the same reader.
  • Preflight the UTF-8 size of serialized profile and metadata output before backup/replacement writes, picker state changes, standard-profile pivots, or profile deletion. A size refusal preserves the existing library files instead of truncating them or deleting a profile before its metadata can be written.
  • Add boundary, growth, removal and unchanged-artifact regression cases, and document the limit. The regular-file and backup-read protections already present in the base, including fix(claude): verify Desktop backup and optional metadata reads (carry #7084) #7129, are retained. This diff adds byte bounds and write-size preflights only. Existing configurations above 4 MiB require manual reduction before these operations can proceed.

Provenance: the residual byte-bound/write-preflight change comes from luvs01/opencodex#730, authored by @luvs01 with the original Devin AI attribution retained below. The existing regular-file/backup-read prerequisites were integrated by @lidge-jun in #7129 from #7084 by @luvs01 and are already present in the base.

Verification

The initial local evidence below is bound to the original validated source snapshot:

  • Base: 8640c2f30edbd2fbb6947ed5c25e2793f5ae72a6 (dev).
  • Validated local HEAD: eb4df0fda185557d5b87feb1bab9ba213c730980 (sole parent is the base above).
  • Initial published signed HEAD: 3f05d9694285765c2b585fcddb440539e46206f1 (author luvs01, GitHub Verified / valid).
  • Initial tree: 9575596fbf9f8a8a94946fa39330c452d2b8707f.
  • Diff: 10 existing files, 383 insertions / 32 deletions, all mode 100644.
  • Patch SHA256: 64fbceea3af85e4927bfc81d276d600367b7bcc3ba88b78dffa2430b2ea5e32c.

The local HEAD identifies the initial tested snapshot. The initial GitHub-signed HEAD has the same tree and sole base parent; the different commit identity comes from publication metadata and signing. The test-only follow-up and its separate validation are recorded below.

Focused and static checks

Used verified Bun 1.4.3, revision 1.4.3+c6da4a4d3. The bun executable and OCX_TEST_RUNNER_BUN below both identify that same runtime. Dependencies were installed in an isolated checkout with frozen lockfiles and install scripts disabled; manifests and lockfiles were unchanged.

bun scripts/test-with-pinned-bun.ts --isolate --parallel=1 --timeout 20000 \
  ./tests/clients/desktop-3p.test.ts \
  ./tests/clients/desktop-3p-removal.test.ts \
  ./tests/clients/desktop-3p-write-files.test.ts \
  ./tests/clients/desktop-remote-store.test.ts \
  ./tests/claude-integration/claude-desktop-picker-profile.test.ts \
  ./tests/test-layout.test.ts ./tests/test-layout-tooling.test.ts
bun node_modules/typescript/bin/tsc --noEmit
bun scripts/structure-ssot.ts
bun scripts/privacy-scan.ts
bun scripts/file-size-ratchet.ts
git diff --check 8640c2f30edbd2fbb6947ed5c25e2793f5ae72a6 eb4df0fda185557d5b87feb1bab9ba213c730980
  • Focused runner: 123 passed, 10 skipped, 0 failed, 1,169 assertions across seven files. The Windows skips leave POSIX-only FIFO/symlink cases unexecuted on this host.
  • Typecheck, structure, privacy, file-size and diff checks: passed.
  • Docs: passed with Node 24.18.0 and the locked Astro dependencies, building 577 pages and checking 81,460 internal links. The build used the checked-in Astro configuration with temporary cache/output paths only; the internal-link integration remained enabled. From docs-site, the command was node node_modules/astro/bin/astro.mjs build --config .tmp/formal-astro.config.mjs. That temporary configuration imported ../astro.config.mjs and overrode only cacheDir, outDir and vite.cacheDir beneath .tmp/.

These are recorded results for the exact source tree above; they were not rerun merely to prepare this description.

Expanded comparison: incomplete, not green

Candidate command:

bun scripts/test.ts --changed=8640c2f30edbd2fbb6947ed5c25e2793f5ae72a6 --timeout 20000

All three runs used the unmodified official wrapper, Bun 1.4.3, isolation, four workers, the same 20-second default per-test limit and the unchanged 900-second suite limit. Tests with a shorter limit defined in their source retained it. Every log reported ten changed paths and 1,234 / 2,508 test files selected. None completed the selection or emitted a complete Bun summary.

Run Observed pass Observed fail Observed skip Observed file headings Termination
Original candidate 3,188 10 18 161 900-second limit; exit 124
Fixed-base comparison 3,336 16 18 165 900-second limit confirmed in log; wrapper exit receipt unavailable
Repeated candidate 3,466 1 18 169 900-second limit; exit 124

The numbers are partial observed counts, not complete-suite totals and not additive across runs. A full selected-membership list was not emitted. For baseline selection, an isolated checkout retained the exact base source tree while using a temporary local reverse-diff commit with the candidate as parent; the wrapper compared against the candidate to select the same ten changed paths. The baseline HEAD was restored afterwards. The candidate retained its original HEAD and command.

The nine other failures from the original candidate passed in the repeated candidate. Seven (four DSH lifecycle/wire cases and three forwarding-admission cases) passed on both expanded comparisons. The two account-pool cases failed on the baseline with a Windows task-registration probe timeout and follow-on spend-ledger/assertion errors, but passed on the repeated candidate. Thirteen additional disconnect/admission failures observed on the baseline also passed on the repeated candidate.

config and connect validation use isLinkPort remains a 20-second timeout on both the baseline and candidate, in both focused four-file comparisons and the expanded runs. Its underlying cause was not resolved or classified as environment-only. No candidate-only failure was reproduced in the observed comparison coverage; this does not establish the absence of regressions in unexecuted files.

Host load was not controlled. The original candidate overlapped static/docs checks. The baseline and repeated candidate were launched sequentially after their predecessors exited, but other test processes started after the repeated candidate began and were observed in 28 of its 29 samples. CPU sampling methods also differed, and the original run had no corresponding samples. These runs are not a strict equal-load performance comparison.

The full-suite default was impractical within the unchanged local resource budget. Under the documented AGENTS.md resource exception, the relevant focused regressions and static/docs checks above were completed. Remaining selected files, the rest of the full suite and platform-specific skips remain unverified and need an appropriate broader validation/CI scope before claiming complete coverage. Full-suite success is not attested. Current author review readiness under the documented resource exception is recorded below.

Initial publication CI

For signed HEAD 3f05d9694285765c2b585fcddb440539e46206f1, the CI run completed with a failure. Ubuntu test shards 1/4, 2/4 and 3/4, gates, structure, docs, keyring, storage policy, API usage, npm-global and Docker smoke passed. Windows/macOS lanes were skipped by the PR workflow policy.

Shard 4/4 stopped at batch 16/63 on normal writer never opens a backup path swapped to a FIFO (tests/clients/desktop-3p-write-files.test.ts:309). The existing POSIX fault-injection fixture watches readFileSync(fd), whereas the bounded reader now uses readSync; its output reports swapped: false. The missing injection is not evidence that an actual FIFO swap was accepted. This initial tree did not include the fixture-hook correction recorded below. No timeout was increased.

The latest hygiene and target checks passed after the prerequisite wording was clarified and the original source attribution preserved. The initial CI result remains failed; remaining shard-4 batches in that run are not claimed as tested.

Fixture follow-up

  • Fixture follow-up signed HEAD: fc800e121437275d43daf8b8d0fc5e8fe5246f8d (author luvs01, GitHub Verified / valid).
  • Sole parent: 3f05d9694285765c2b585fcddb440539e46206f1; fixture follow-up tree: 45115f6d7680ecd31a50013bd0527267ce6cc10d.
  • Only tests/clients/desktop-3p-write-files.test.ts changes in this follow-up, mode 100644, blob 3f845ac4ab7ab6f12c14eb02709c1f8bcc5c20f5. Runtime source blobs remain unchanged from the initial publication.
  • The child fixture intercepts readSync and performs the actual FIFO swap after the original descriptor read. Both backup/rollback scenarios, all 12 assertions, refusal reasons, file-content/type checks, and existing skip/timeout conditions are preserved.
  • Windows owning-file run with Bun 1.4.3: 17 passed, 4 skipped, 0 failed, 91 assertions. Typecheck passed. The four Windows skips include the two FIFO cases; no local POSIX execution is claimed.
  • Follow-up full-index patch SHA256: 628f1db2a1b73f7b80f89c3c0aa55e7c40e07e98181f2c401a68f0d995af1e91.

The exact-HEAD CI run completed successfully: Ubuntu test shards 1/4 through 4/4 and the enabled gates passed. The Ubuntu shard 4 log confirms that both normal writer never opens a backup path swapped to a FIFO and normal writer never opens a rollback path swapped to a FIFO actually executed and passed with Bun 1.4.3. Windows/macOS lanes were skipped by the existing PR workflow policy. This CI result does not turn the earlier incomplete local expanded runs into complete coverage; their recorded limits and failures above remain applicable.

Scoped test receipts at d908f21615d3645ff12b423e54ce0d6f12476b34

This normal GitHub-signed follow-up adds 25 test cases in three existing test files (+251 lines, all mode 100644). Author: luvs01; signature: Verified / valid. Sole parent: fc800e121437275d43daf8b8d0fc5e8fe5246f8d; tree: cb5b40ba3fa2e670419fce1e39f6cb8d4694a1db. Product source files are unchanged. It follows the scoped review; it is not an overall approval or completion of filesystem/config ownership review. The current author readiness declaration is recorded below; this scoped receipt is not maintainer approval.

The exact-HEAD CI run completed successfully, including all four enabled Ubuntu test shards. The shard 2 log and shard 4 log contain actual (pass) lines for all 25 additions, including both newly POSIX-only cases below. Test-name parameters in the table enumerate the literal names generated by each test loop; every listed expansion was checked in those logs.

Scenario File and exact test name / parameter expansions Result and assertion scope
Empty / growing read desktop-3p-removal.test.ts: bounded reader accepts an empty regular file unchanged; bounded reader caps growth from an empty file at limit plus one Ubuntu pass; Windows pass. Empty result/file stay empty; growth from zero consumes exactly 65 bytes at a 64-byte cap and refuses.
Same-byte inode replacement Same file: bounded reader refuses a same-byte regular inode replacement before open; bounded reader refuses a same-byte regular inode replacement after read Both actually passed on Ubuntu using real regular-file replacement with identical bytes and different inode. Windows passed before-open; after-read is explicitly skipped because rename-over-open raised EPERM before the intended recheck.
ctime observations and real transition Same file: bounded reader refuses a ctime-only observation change at descriptor; bounded reader refuses a ctime-only observation change at path recheck; bounded reader refuses a real ctime-only change after read on POSIX All Ubuntu pass. First two inject only observed ctimeNs and also passed on Windows; they are comparison coverage, not real OS timestamp receipts. The real POSIX case checks changed ctime with unchanged dev/ino/size/mode/mtime and file bytes; explicitly skipped on Windows.
Standard removal boundary / extensions Same file: standard removal preflights exact output limit+{extra}, selected gateway={selectedGateway}, extra=0,1, selectedGateway=true,false (4 cases) All Ubuntu and Windows pass. Exact limit succeeds and preserves arbitrary nested metadata, foreign entry extension and foreign profile bytes. Limit+1 refuses with zero unlink calls and all file bytes/list preserved, covering selected-gateway pivot and foreign-selected residue cleanup.
Picker apply/removal boundaries / extensions claude-desktop-picker-profile.test.ts: {operation} preflights exact metadata output limit+{extra} and preserves extensions, operation=apply, remove selected, remove fallback, remove unselected, extra=0,1 (8 cases) All Ubuntu and Windows pass. Exact limit succeeds with nested foreign metadata/entry extensions and foreign file bytes preserved. Limit+1 leaves profile/backup/metadata/state bytes and library list unchanged, before pivot/deletion. Apply also retains the existing picker entry extension.
Picker rollback itself fails Same file: picker reports failure when metadata publication and rollback restore fail; picker reports failure when metadata publication and rollback cleanup fail Both Ubuntu and Windows pass. The opaque result is write_failed, original metadata and foreign profile bytes remain, and the real new-profile residue is asserted; a failed rollback is not claimed to have restored all artifacts.
Writer output boundaries desktop-3p-write-files.test.ts: normal writer preflights exact {kind} output limit+{extra}, kind=profile,metadata, extra=0,1 (4 cases) All Ubuntu and Windows pass. Exact limit publishes the full-sized output and exact old-byte backup; limit+1 preserves profile/metadata bytes and creates no backup.

Existing regressions are retained: Desktop library reads accept the byte boundary and refuse a larger file tests exact 4 MiB / +1; Desktop library reads bound growth after descriptor validation and StoreIO limits a growing read to its tighter cap plus one sentinel byte bound post-stat growth. The pre-serialized metadata checks run before mutation in picker apply/removal and standard removal. The current shard 4 log also confirms actual passes for normal writer never opens a backup path swapped to a FIFO and normal writer never opens a rollback path swapped to a FIFO. Existing general-writer rollback-failure, foreign-field preservation and cleanup-interruption cases remain in their owning files.

Local Windows validation used the verified Bun 1.4.3 runtime and OCX_TEST_RUNNER_BUN, with the official pinned wrapper, isolation, one worker and an unchanged 20-second per-test default:

bun scripts/test-with-pinned-bun.ts --isolate --parallel=1 --timeout 20000 \
  ./tests/clients/desktop-3p-removal.test.ts \
  ./tests/claude-integration/claude-desktop-picker-profile.test.ts
bun scripts/test-with-pinned-bun.ts --isolate --parallel=1 --timeout 20000 \
  ./tests/clients/desktop-3p-write-files.test.ts

Results: 47 passed / 7 skipped / 0 failed, 305 assertions across the first two files; 21 passed / 4 skipped / 0 failed, 127 assertions in the writer file. Of the 25 additions, 23 executed and passed on Windows; two were explicitly skipped as described above. Typecheck, file-size, privacy and diff checks passed. The initial local trial was 47 passed / 6 skipped / 1 failed: Windows rename-over-open returned EPERM during the after-read inode injection. That trial remains part of the record; only the unsupported Windows injection was then explicitly skipped. No timeout or runtime assertion was weakened.

Platform/coverage limits remain explicit: the 11 reported Windows skips include nine existing POSIX-only cases. In addition, the existing picker FIFO test returns early on Windows while the runner reports pass, and its POSIX permission-mode assertions are also omitted there. Those are not Windows FIFO/mode receipts. Current PR CI skips Windows/macOS lanes under the existing policy. The original 123 passed / 10 skipped result belongs to the initial tree, and the successful FIFO run at fc800e1 belongs to that earlier HEAD. Neither is relabeled as a run of this follow-up. The earlier expanded local comparisons remain incomplete at the unchanged 900-second limit, including their recorded failures; they were not rerun or declared green. Filesystem/config-ownership and security review remain for the maintainers; the author readiness declaration below does not replace them.

Author review readiness

Author declaration for exact HEAD d908f21615d3645ff12b423e54ce0d6f12476b34 on 2026-10-19. This requests review; it does not attest maintainer approval or authorize merge/release.

  • Required local validation passed; commands, results, and any full-suite exception are documented.
  • I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
  • All currently reported correct Codex and CodeRabbit findings are resolved.
  • My PR is ready for review.

Evidence and scope: the local validation and exact-HEAD Ubuntu CI receipts above apply under the documented AGENTS.md full-suite resource exception. The final focused Windows runs are 47 pass / 7 skip and 21 pass / 4 skip, both with zero failures; final typecheck, privacy, size and diff checks passed. Exact-HEAD CI run 39140926325 passed all four enabled Ubuntu test shards. All 25 added cases, including the two POSIX-only additions, and both existing FIFO cases have actual Ubuntu pass receipts. The original 123 pass / 10 skip result stays bound to its initial tree.

Freshness: current dev is 1c5e963f1d5020ae5680b85feeb6cd03d1b8c042; this HEAD is 2 commits behind and 3 ahead, within the policy's at-most-10 allowance. It is not claimed to contain the exact current dev tip, and the two later dev commits are not represented as having been tested in these earlier HEAD receipts. No code or commit history was changed for readiness.

Findings: at this declaration there are zero review threads, zero submitted reviews, and no reported unresolved Codex/CodeRabbit findings. CodeRabbit previously skipped the Draft; this checkbox is not a claim that an automated review or the separate filesystem/config-ownership review has completed. New findings remain subject to review and resolution.

Limits: the earlier 900-second expanded local comparisons remain incomplete with their recorded failures, including the unresolved isLinkPort timeout; the documented focused-test resource exception is used rather than claiming a green full local suite. Windows/macOS CI lanes were skipped under repository policy, Windows POSIX skips and the picker FIFO early-return/mode omissions remain explicit, and no actual Desktop library or end-to-end Desktop app was tested. Readiness does not remove those limits or the need for maintainer security/ownership review.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Changed paths were reviewed for secrets, auth changes and unsafe defaults; privacy scan passed. This is not a claim of an independent security audit.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

Summary by CodeRabbit

  • Safety
    • Desktop library profiles and metadata are limited to 4 MiB. Oversized files or generated updates are rejected before existing files are replaced.
    • Connection-owned stores continue to use their stricter size limits.
  • Documentation
    • Updated the Claude Desktop guide with the file-size limits and information about oversized updates.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 0d98dc0a-8239-4263-98eb-1221126316b8
📥 Commits

Reviewing files that changed from the base of the PR and between 8640c2f and d908f21.

📒 Files selected for processing: 10
  • docs-site/src/content/docs/guides/claude-code.md
  • src/claude/desktop-3p-library.ts
  • src/claude/desktop-3p.ts
  • src/claude/desktop-picker-profile.ts
  • src/claude/desktop-remote-store-io.ts
  • structure/clients/claude-desktop-config-reads.md
  • tests/claude-integration/claude-desktop-picker-profile.test.ts
  • tests/clients/desktop-3p-removal.test.ts
  • tests/clients/desktop-3p-write-files.test.ts
  • tests/clients/desktop-3p.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Desktop library reads and generated JSON now use a 4 MiB byte ceiling. Reads are bounded and verify the path still identifies the opened file. Profile and metadata flows check serialized output before replacing or removing files. Tests and documentation cover the limits and boundary behavior.

Changes: Desktop library size limits

Layer / File(s) Summary
Bounded reads and byte validation
src/claude/desktop-3p-library.ts:47-116, src/claude/desktop-remote-store-io.ts:5,48, tests/clients/desktop-3p-removal.test.ts:286-333,373-497, structure/clients/claude-desktop-config-reads.md:11-13
The shared reader enforces a configurable limit up to 4 MiB, bounds reads, and checks that the path still identifies the opened file. StoreIO.read applies the smaller of its limit and the Desktop library limit. Tests cover size limits and file changes during reads.
Preflight profile and metadata changes
src/claude/desktop-3p.ts:645-688,842-863,907, src/claude/desktop-picker-profile.ts:52,194-207,251-286, tests/clients/desktop-3p.test.ts:681-748, tests/clients/desktop-3p-write-files.test.ts:277-360, tests/clients/desktop-3p-removal.test.ts:335-371,499-533, tests/claude-integration/claude-desktop-picker-profile.test.ts:1-360, docs-site/src/content/docs/guides/claude-code.md:257-262, structure/clients/claude-desktop-config-reads.md:40-55
Profile and metadata JSON use a serializer that checks UTF-8 byte size. Apply and removal flows prepare serialized metadata before writing or unlinking profiles. Tests cover exact-limit and over-limit output, unchanged files on refusal, and failure handling. Documentation describes the limits and affected flows.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to d908f

No actionable merge-blocking issue is established; normal checks and the outstanding ownership review can proceed.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 8 files. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main changes: bounded Claude Desktop library reads and preflight checks before writes. It matches the implementation and PR objectives.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 8 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • missing_coauthor_credit — This pull request says it reimplements, supersedes, carries, or rebases another author's pull request, but no Co-authored-by trailer names that author. Prose in a commit body is not read by anything; the trailer is what GitHub counts. Add it to the description or a commit, or obtain attribution-approved. Paths: #7129.

@github-actions github-actions Bot added the bug Something isn't working label Oct 18, 2026
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed.

Hygiene

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot removed the intake: hygiene-blocked Deterministic PR hygiene checks failed label Oct 18, 2026
@Ingwannu

Copy link
Copy Markdown
Owner

Scoped review at fc800e1214: the descriptor-based bounded read and pre-serialized metadata checks address distinct failures; preserve the tests proving growth after stat is bounded and a size refusal happens before profile/metadata deletion or pivot. Please retain exact-limit/limit+1, empty/growing-file, changed inode/ctime, failed rollback and foreign-field preservation receipts across picker and standard removal paths, with platform skips explicit. Current Ubuntu CI succeeded. The filesystem/config ownership review remains separate, and this Draft has not been promoted or approved by this comment. No actual Desktop library was accessed.

@luvs01

luvs01 commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

@Ingwannu, the scoped test additions are published in d908f21615d3645ff12b423e54ce0d6f12476b34.

Scoped test receipts at d908f21615d3645ff12b423e54ce0d6f12476b34

This normal GitHub-signed follow-up adds 25 test cases in three existing test files (+251 lines, all mode 100644). Author: luvs01; signature: Verified / valid. Sole parent: fc800e121437275d43daf8b8d0fc5e8fe5246f8d; tree: cb5b40ba3fa2e670419fce1e39f6cb8d4694a1db. Product source files are unchanged. It follows the scoped review; it is not an overall approval or completion of filesystem/config ownership review. The PR remains a Draft.

The exact-HEAD CI run completed successfully, including all four enabled Ubuntu test shards. The shard 2 log and shard 4 log contain actual (pass) lines for all 25 additions, including both newly POSIX-only cases below. Test-name parameters in the table enumerate the literal names generated by each test loop; every listed expansion was checked in those logs.

Scenario File and exact test name / parameter expansions Result and assertion scope
Empty / growing read desktop-3p-removal.test.ts: bounded reader accepts an empty regular file unchanged; bounded reader caps growth from an empty file at limit plus one Ubuntu pass; Windows pass. Empty result/file stay empty; growth from zero consumes exactly 65 bytes at a 64-byte cap and refuses.
Same-byte inode replacement Same file: bounded reader refuses a same-byte regular inode replacement before open; bounded reader refuses a same-byte regular inode replacement after read Both actually passed on Ubuntu using real regular-file replacement with identical bytes and different inode. Windows passed before-open; after-read is explicitly skipped because rename-over-open raised EPERM before the intended recheck.
ctime observations and real transition Same file: bounded reader refuses a ctime-only observation change at descriptor; bounded reader refuses a ctime-only observation change at path recheck; bounded reader refuses a real ctime-only change after read on POSIX All Ubuntu pass. First two inject only observed ctimeNs and also passed on Windows; they are comparison coverage, not real OS timestamp receipts. The real POSIX case checks changed ctime with unchanged dev/ino/size/mode/mtime and file bytes; explicitly skipped on Windows.
Standard removal boundary / extensions Same file: standard removal preflights exact output limit+{extra}, selected gateway={selectedGateway}, extra=0,1, selectedGateway=true,false (4 cases) All Ubuntu and Windows pass. Exact limit succeeds and preserves arbitrary nested metadata, foreign entry extension and foreign profile bytes. Limit+1 refuses with zero unlink calls and all file bytes/list preserved, covering selected-gateway pivot and foreign-selected residue cleanup.
Picker apply/removal boundaries / extensions claude-desktop-picker-profile.test.ts: {operation} preflights exact metadata output limit+{extra} and preserves extensions, operation=apply, remove selected, remove fallback, remove unselected, extra=0,1 (8 cases) All Ubuntu and Windows pass. Exact limit succeeds with nested foreign metadata/entry extensions and foreign file bytes preserved. Limit+1 leaves profile/backup/metadata/state bytes and library list unchanged, before pivot/deletion. Apply also retains the existing picker entry extension.
Picker rollback itself fails Same file: picker reports failure when metadata publication and rollback restore fail; picker reports failure when metadata publication and rollback cleanup fail Both Ubuntu and Windows pass. The opaque result is write_failed, original metadata and foreign profile bytes remain, and the real new-profile residue is asserted; a failed rollback is not claimed to have restored all artifacts.
Writer output boundaries desktop-3p-write-files.test.ts: normal writer preflights exact {kind} output limit+{extra}, kind=profile,metadata, extra=0,1 (4 cases) All Ubuntu and Windows pass. Exact limit publishes the full-sized output and exact old-byte backup; limit+1 preserves profile/metadata bytes and creates no backup.

Existing regressions are retained: Desktop library reads accept the byte boundary and refuse a larger file tests exact 4 MiB / +1; Desktop library reads bound growth after descriptor validation and StoreIO limits a growing read to its tighter cap plus one sentinel byte bound post-stat growth. The pre-serialized metadata checks run before mutation in picker apply/removal and standard removal. The current shard 4 log also confirms actual passes for normal writer never opens a backup path swapped to a FIFO and normal writer never opens a rollback path swapped to a FIFO. Existing general-writer rollback-failure, foreign-field preservation and cleanup-interruption cases remain in their owning files.

Local Windows validation used the verified Bun 1.4.3 runtime and OCX_TEST_RUNNER_BUN, with the official pinned wrapper, isolation, one worker and an unchanged 20-second per-test default:

bun scripts/test-with-pinned-bun.ts --isolate --parallel=1 --timeout 20000 \
  ./tests/clients/desktop-3p-removal.test.ts \
  ./tests/claude-integration/claude-desktop-picker-profile.test.ts
bun scripts/test-with-pinned-bun.ts --isolate --parallel=1 --timeout 20000 \
  ./tests/clients/desktop-3p-write-files.test.ts

Results: 47 passed / 7 skipped / 0 failed, 305 assertions across the first two files; 21 passed / 4 skipped / 0 failed, 127 assertions in the writer file. Of the 25 additions, 23 executed and passed on Windows; two were explicitly skipped as described above. Typecheck, file-size, privacy and diff checks passed. The initial local trial was 47 passed / 6 skipped / 1 failed: Windows rename-over-open returned EPERM during the after-read inode injection. That trial remains part of the record; only the unsupported Windows injection was then explicitly skipped. No timeout or runtime assertion was weakened.

Platform/coverage limits remain explicit: the 11 reported Windows skips include nine existing POSIX-only cases. In addition, the existing picker FIFO test returns early on Windows while the runner reports pass, and its POSIX permission-mode assertions are also omitted there. Those are not Windows FIFO/mode receipts. Current PR CI skips Windows/macOS lanes under the existing policy. The original 123 passed / 10 skipped result belongs to the initial tree, and the successful FIFO run at fc800e1 belongs to that earlier HEAD. Neither is relabeled as a run of this follow-up. The earlier expanded local comparisons remain incomplete at the unchanged 900-second limit, including their recorded failures; they were not rerun or declared green. No full security/config-ownership approval or review-readiness attestation is made.

The original @luvs01 / Devin AI contribution and #7084/#7129 provenance remain in the PR description. The initial failed CI and incomplete local expanded-run history are retained.

@Ingwannu

Copy link
Copy Markdown
Owner

Intake/scoped review at d908f21615d3645ff12b423e54ce0d6f12476b34.

The follow-up adds the requested boundary fixtures for descriptor-based reads, pre-serialized write refusal and removal/growth behavior. The named new test receipts and current successful functional CI are useful; they remain Linux/fixture evidence rather than a real Desktop lifecycle receipt. Complete the current-head author readiness step before taking this Draft forward.

Existing current-head CI was inspected; its success is limited to the jobs actually selected, with skipped platforms retaining no runtime certification. No local suite was run for this branch in this intake. No workflow approval, new scan, live configuration change or merge was performed. Required review/readiness gates remain in force.

@luvs01
luvs01 marked this pull request as ready for review October 19, 2026 06:35
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-19T06:35:14.342643Z d908f21 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Establish a real ctime transition before requiring the reader to reject it — tests/clients/desktop-3p-removal.test.ts:481–495.

At d908f21615d3645ff12b423e54ce0d6f12476b34, my capped Linux/Bun 1.4.3 run of the four changed Desktop test files is 111 pass / 1 fail. The new real-ctime case returns {} instead of throwing at line 491. A separate isolated observation of the same utimes-to-the-same-mtime shape measured 19 unchanged ctimes / 1 changed ctime in 20 iterations; the reader's behavior agreed with the actually observed ctime in every iteration. Calling utimes again does not guarantee that the filesystem advances ctime within its timestamp resolution.

Please make this fixture deterministically establish a changed ctime under a short finite deadline (or use the existing deterministic descriptor/path-change seam for that contract), while preserving the production comparison and the real-filesystem boundary control. Do not remove the rejection assertion, silently skip the case, or infer a production race failure where the timestamp never changed. The surrounding byte-boundary/growth/preflight-write and rollback fixtures passed here; current hosted CI is green but does not remove the local reproducibility failure. No real Desktop profile/service was touched. Holding approval until the fixture is stable.

lidge-jun added a commit that referenced this pull request Oct 19, 2026
…ate paths and shim token handling (B5) (#7397)

* fix(oauth): allow dismissing a settled failed login flow (carry #7379)

Co-authored-by: Ingwannu <186453546+Ingwannu@users.noreply.github.com>

* fix(providers): preserve explicit API-key mode when keys are unavailable (carry #7304)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(dsh): bound account profile response reads (carry #7333)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* test(dsh): drain server fixtures before removing homes (carry #7305)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(claude-cli): keep local errors out of subscription cooldown (carry #7343)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(claude): bound Desktop library reads and preflight writes (carry #7301)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(service): stop scheduler wrappers before Windows reinstall (carry #7313)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(startup): require RunAtLoad before Desktop relaunch (carry #7277)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(codex): strip OpenSSL startup variables from updater child env (carry #7342)

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(codex): keep the service token out of bypassed shim launches

All three Codex shims loaded the service API token into OPENCODEX_API_AUTH_TOKEN before checking OCX_SHIM_BYPASS, so a bypassed native passthrough launch handed the proxy data-plane token to the real Codex and its descendants. The POSIX, CMD and PowerShell shims now skip the token load under bypass; the Unix revision marker moves to 4 so installed wrappers are refreshed.

loadServiceTokenFromFile reads the inherited OCX_API_TOKEN_FILE through a no-follow, nonblocking descriptor, accepts only a regular file of at most 4 KiB and returns only a single-line printable token.

* test(claude): wait for a real ctime transition before requiring rejection

Re-applying the same utimes moves ctime only after the kernel clock ticks, so the real ctime case from #7301 often observed no change and the reader correctly returned {}. Repeat the call until ctime has advanced (bounded), assert that precondition, then require desktop_config_not_regular.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(google-antigravity): admit a probed project_id fallback in account import (carry #7385)

An Antigravity account whose Cloud Code Assist project is not auto-discovered was rejected with missing_project even when the operator knows its project. A Cockpit record may now carry a bounded project_id (Google Cloud project grammar, 6-30 chars, exact match). Discovery keeps precedence; the fallback is admitted only after the account quota probe succeeds with that account own access token against that project. Probe refusal, probe errors and cancellation write nothing. No shared default project.

Co-authored-by: yobushka <51312852+yobushka@users.noreply.github.com>

* fix(quota): name a closed reason when an OAuth account quota probe fails

Anthropic per-account quota rows reported quotaUnavailable with no reason, and Antigravity reported account_unavailable for any credential-side throw. The Anthropic usage read now reports a closed code for an HTTP status class, an unusable body or a thrown dispatch, and a probe that cannot obtain a credential reports account_unavailable without dispatching. Antigravity distinguishes a credential refresh deadline (timeout). Codes reuse the existing closed QUOTA_FAILURE_CODES set, carry no upstream text and never change account health.

* test(claude): match the symlink-resolved rollback target in the picker restore case

The atomic writer renames onto the symlink-resolved target, so on macOS (/var -> /private/var) the rollback rename never matched the fixture path and the restore-failure injection did not fire. Compare both spellings.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(google-antigravity): cancel the import project probe with the import request

probeAntigravityUsageQuota accepts an optional caller signal that cancels every request of the probe and stops before the models fallback; the import verifier forwards the import signal and never admits after cancellation.

* fix(codex): upgrade installed Windows shims that load the token before bypass

CMD and PowerShell wrappers now carry a Windows revision marker. A marked wrapper without it (installed before the bypass-token fix) is obsolete: installCodexShim and autoRestoreCodexShim rewrite it in place over the same backing launcher instead of reporting it healthy or already installed, and diagnostics report it unhealthy. On Windows the service token reader also binds the opened descriptor to the path by identity, so a link swapped in after lstat is refused.

* test(codex): mark the in-place Windows diagnostics fixture with the current revision

* fix(codex): refresh obsolete Windows shims without clobbering a concurrent replacement

The obsolete-wrapper refresh truncated whatever occupied the wrapper path. It now moves the observed wrapper aside and proves it is the probed file, stages the current wrapper beside it, and publishes with a hard link that refuses an occupied path. A concurrent replacement is restored or kept untouched and the refresh reports deferred.

* docs(structure): record the Windows shim refresh transaction

* fix(codex): create the Windows shim refresh stage exclusively and resume partial upgrades

The refresh stage is now created with O_CREAT|O_EXCL (and O_NOFOLLOW on POSIX) under an independent random name, and publication, withdrawal and cleanup are bound to the identity of the file this refresh created, so a planted link is never written through. Auto-restore resumes a Windows revision-only sibling mix (current plus obsolete wrappers) instead of deferring forever after one transient publish failure.

* fix(codex): bind Windows shim refresh rollback and recovery to observed identities

Publication keeps a linked entry only if it is the created stage, and withdraws a link to a swapped stage (file or symlink) while leaving independent replacements. Restoring the moved wrapper requires the aside to still be the moved file. Automatic revision recovery now refreshes only the admitted obsolete wrappers and first re-verifies every current sibling, so a replacement during the stability wait is never adopted.

* fix(codex): never displace a Windows shim where its replacement cannot be published

The refresh proves hard-link support in the wrapper directory with a throwaway pair before moving the wrapper aside, and if a restore link still fails it falls back to an identity-checked rename onto the empty path, so the launcher never stays missing.

---------

Co-authored-by: Ingwannu <186453546+Ingwannu@users.noreply.github.com>
Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: yobushka <51312852+yobushka@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Carried into #7397 (squash dbb908f) on dev with a Co-authored-by trailer (B5 bundle). The maintainer P2 is fixed: the real-ctime test now advances ctime with a bounded loop, asserts it actually changed, then requires rejection (10/10 runs). A macOS-only picker fixture path was also fixed. It ships in the next release (2.90.0). Thanks!

@lidge-jun lidge-jun closed this Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants