Skip to content

feat(core): harden the Unix file removal against a same-user actor in the quarantine directory (#445) - #1072

Closed
qnbs wants to merge 15 commits into
mainfrom
feat/445-gate4e-h1a2-unix-hardening
Closed

qnbs wants to merge 15 commits into
mainfrom
feat/445-gate4e-h1a2-unix-hardening

Conversation

@qnbs

@qnbs qnbs commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

User description

Part of #359 (R-15 Gate 4, slice H1a-2), part 2b of 5, stacked on #1069 (base = its branch). test-support only: no production caller, no production deletion release.

Why a part of its own: the Unix file removal reached 1,306 meaningful lines through its review waves (tier 1,200). The hardening against a same-user actor in the quarantine directory and the durability of the failure paths is a separate proof boundary; it is cut out of #1069 (one revert commit there, no force-push) and re-applied here, so the removal itself stays reviewable at 1,109 lines and this part at 263.

What this adds

  • Durability of failure paths: the parent is synced after a rollback, a stranded entry, a put-back and a vanished name (best effort after a failure, so the sync's own failure does not replace the failure being reported); an entry that stays in the quarantine directory is retained with a best-effort sync of that directory too. The BeforeSync stage is reached, which tests observe.
  • Cleanup bound to what was created: the quarantine directory records its identity; discard removes the name only if it still leads to that directory (at most an empty directory can be removed by a name-based rmdir).
  • Renamed-away quarantine: an entry is reported as retained (Swapped { restored: false }, Stranded) only if the recorded name still leads to its directory; otherwise the outcome is Disturbed (the entry cannot be found by name). Disturbed documentation widened accordingly.
  • Owner-masking umask: a quarantine directory that cannot be opened is refused (PermissionDenied, fail closed), pinned by a test.
  • Stated, not closable: that the directory behind the quarantine name is the one mkdirat made cannot be proved (no descriptor is returned); the quarantine is not exclusive anyway.

Proof

Forced cases: failure at each stage with the sync stage observed after a move and not before it; the put-back blocked by taking the name again (Stranded, whoever runs it, also for root); a quarantine directory renamed away and replaced by an empty one (cleanup leaves it alone); a quarantine renamed away while the put-back is blocked (Disturbed, the entry intact in the renamed directory); a vanished name (sync reached); the permission-based Stranded case skips for root with a message. Local: fmt, clippy -D warnings on Linux / Windows / macOS targets, lib and isolation tests.

Answers the Codex / CodeRabbit / CodeAnt findings on #1069 raised after its first head (sync after rollback, replacement quarantine directory, root, creation race, quarantine sync, vanish sync, owner-masking umask, adopt-error sync, renamed quarantine).

Status: H1A_2_UNIX_HARDENING=IMPLEMENTED_TEST_SUPPORT_ONLY, H1_PROVED=NO, R1_PRODUCTION_RECLAMATION=NOT_ADMITTED, GATE4_TERMINAL=NO.

Summary by Sourcery

Harden the test-only Unix file removal flow against quarantine tampering while preserving durable and truthful failure outcomes.

Bug Fixes:

  • Harden test-only Unix file removal against quarantine-directory tampering and same-user interference.
  • Ensure failed removal paths preserve and accurately report stranded, disturbed, vanished, and unconfirmed outcomes.

Enhancements:

  • Add durability handling for both the parent and quarantine directories across rollback, put-back, stranded, vanished, and removal paths.
  • Restrict quarantine cleanup and retention reporting to the directory and entry identities originally observed.
  • Fail closed when a quarantine directory cannot be opened and document the remaining limits of name-based race protection.

Documentation:

  • Document the quarantine durability, identity checks, tamper outcomes, and remaining Unix race boundary in the Gate 4 evidence journal.
  • Record the test-support hardening in the unreleased changelog.

Tests:

  • Add Unix tests covering sync failures, renamed or replaced quarantine directories, blocked put-backs, vanished entries, cleanup identity checks, and permission failures.

Summary by cubic

Hardens the Unix file removal in test-support against a same-user actor manipulating the quarantine directory, making failure paths durable and reporting tampering clearly. No production caller touches this code.

  • The parent is synced after a rollback, a failed move, a put-back and a vanished name, best effort so a failed sync never replaces the failure reported. The quarantine directory is synced after every move through it; for a put-back, a kept entry, or an entry found gone at the inspection or unlink, a failed sync is an error reported after the parent was synced (not another outcome), for a rollback, a stranded entry and tampering it is best effort, and for an unlink it leaves the removal Removed(NotConfirmed).
  • The quarantine records the identity of the directory it created; discard removes the name only if it still leads to that directory, and the same identity check applies when a failed setup is cleaned up. An entry is reported as retained only if the recorded name still leads there and the entry is still the authenticated object (or the identity a swapped-in object was seen with); a proven absence or a replacement is Disturbed.
  • A quarantine directory the owner cannot open is refused (PermissionDenied, fail closed).
  • Tests force each failure case, including failed syncs through a test-only seam, the quarantine renamed away, the put-back blocked, and the cleanup identity check, so they also run for root. No new unsafe or package.

Written for commit ae96312. Summary will update on new commits.

View guided diff Turn on auto-fix

Summary by CodeRabbit

  • Bug Fixes
    • Unix file removal now checks that the quarantine directory remains unchanged before reporting retained entries or removing the directory.
    • Removal durability is confirmed only when both relevant directories sync successfully. Sync failures after removal are reported as unconfirmed; failures during rollback or when an entry is stranded preserve the corresponding error or outcome.
    • Removal is refused if the quarantine directory cannot be opened.
  • Documentation
    • Updated Unix removal guidance to describe directory-sync requirements and removal outcomes.

CodeAnt-AI Description

Harden test-only Unix file removal against quarantine tampering and sync failures

What Changed

  • Unix file removal now checks that a quarantine name still leads to the directory it created before removing it, and reports entries as retained only when they remain there.
  • Syncs the quarantine directory after entries move through it. A failed sync makes a removal unconfirmed or returns an error after a put-back, without changing where the entry was left.
  • Syncs the parent after rollback, stranded-entry, and vanished-name paths; refuses removal if the quarantine directory cannot be opened. These changes are limited to test-support and have no production caller.

Impact

✅ Test-only removal leaves replacement quarantine directories untouched
✅ Tampered or missing quarantine entries are reported as disturbed
✅ Failed quarantine syncs do not report removals as confirmed

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

qnbs added 10 commits October 15, 2026 13:09
…rt only (#445)

The removal primitives of the pinned module exist only with the test-only
feature and have no caller. On Unix an object is moved into a quarantine
directory with RENAME_NOREPLACE, the open object is identified there and only
then unlinked, with the link count checked afterwards; a swap of the last name
or a manipulation inside the quarantine directory is moved back and never
unlinked. No exclusivity of that directory is assumed: the one window no Unix
primitive closes is forced in a test and reported as Disturbed.

On Windows the object is opened through the handle-relative lookup with delete
access and read-only sharing, so no writer can change the authenticated bytes,
and the POSIX delete disposition is set on that same handle. There is no
FILE_FLAG_DELETE_ON_CLOSE and no fallback.

Leftovers of a crash are finished only after authenticating them again. A
scan of the sources and a compile of a dependent with and without the feature
keep the primitives out of every other build.
…on (#445)

The first draft of the removal was one pull request of 2,172 meaningful lines,
over the 1,200-line hard-advisory tier. This commit keeps what has to land
first: the API behind test-support, the proofs that it exists in no other build
(a scan of the sources and a compile of a dependent with and without the
feature), PinnedDir::sync, and the contract with the limit no Unix primitive can
remove. Every removal answers Unsupported here; the Unix file removal, the Unix
directory removal with the leftovers, and the Windows delete follow as stacked
pull requests, each with its own tests.
…the open object (#445)

The authenticated file is moved into a quarantine directory with
RENAME_NOREPLACE, the open object is identified there and only then unlinked.
Anything else is put back, never unlinked. Every failure after the move is
handled at one point: the entry goes back, or the outcome is Stranded and says
where it is. The parent is synced whenever it changed.

The quarantine directory is not assumed to be exclusive. The one window no Unix
primitive closes is forced in a test and reported as Disturbed: detected after
the fact by the link count of the authenticated object, not prevented.
… its own (#445)

CodeScene counted the table as part of a 73-line test; the table is data, the
test is the loop over it.
…unix-file-removal

# Conflicts:
#	CHANGELOG.md
#	crates/worldscript-secure-storage/src/pinned/tests/mod.rs
#	crates/worldscript-secure-storage/src/pinned/tests/remove.rs
#	crates/worldscript-secure-storage/src/pinned/unix/removal.rs
#	docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md
…tory that was made (#445)

Review findings on the Unix file removal:

- A failure after the move that put the entry back, or left it in the
  quarantine directory, returned without syncing the parent. The sync is now
  attempted after both (best effort, so it does not replace the failure being
  reported) and the BeforeSync stage is reached, which a test observes.
- The quarantine directory was removed by its name alone, so a directory that an
  actor put at that name after renaming the held one away could be removed. The
  cleanup now checks that the name still leads to the directory that was
  created; a test forces the swap.
- The permission-based Stranded test failed for root. It now skips with a
  message for root, and the same logic is covered without permissions by a case
  that blocks the put-back by taking the name again.
…creation race (#445)

More review findings on the Unix file removal:

- The path where the name vanished before the move created and removed a
  quarantine directory and reported no change; the parent is now synced.
- An entry that stays in the quarantine directory (put-back blocked, or
  stranded) is retained with a best-effort sync of that directory as well as of
  the parent.
- That the directory behind the quarantine name is the one mkdirat made cannot
  be proved, since mkdirat returns no descriptor. The documentation says so and
  why it does not widen the window the contract already states.
…ove, refuse an unopenable one (#445)

Further review findings on the Unix file removal:

- An entry that stays in the quarantine directory is only reported as being
  there if the recorded name still leads to that directory. If an actor renamed
  it away the outcome is Disturbed, which now also covers an entry that cannot
  be found by name. A test forces the rename.
- A failure to move the entry at all (renameat2 unsupported or failing) created
  and removed a quarantine directory without syncing the parent; it now goes
  through the same best-effort sync as a rollback.
- A umask that takes the owner's read access away leaves a quarantine directory
  that cannot be opened. The removal is refused (fail closed) and a test pins
  that, instead of the documentation promising more.
…own part (#445)

The Unix file removal had grown to 1,306 meaningful lines through review
waves, over the 1,200-line tier. Its hardening against a same-user actor in the
quarantine directory and the durability of the failure paths (sync after a
rollback or a stranded entry, an identity-checked cleanup of the quarantine
directory, a quarantine that was renamed away, an unopenable one, the creation
race stated) is a separate proof boundary and is re-applied as the next part
on top of this one. What stays here is the removal itself with its single
rollback point, the Stranded outcome, the sync of changes that are made, the
forced swap tests and the link-count window. The root-skip of the permission
case stays.
… the quarantine directory (#445)

The parent is synced after a rollback, a stranded entry, a put-back and a
vanished name, and an entry that stays in the quarantine directory is retained
with a best-effort sync of that directory too. The quarantine directory is
removed by its name only if the name still leads to the directory that was
created. An entry is reported as retained only if the recorded name still leads
to the directory that holds it; otherwise the outcome is Disturbed. A quarantine
directory that cannot be opened is refused. Tests force each case, including the
quarantine renamed away and the put-back blocked, so they also run for root.
@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
worldscript-studio Ready Ready Preview Oct 15, 2026 6:20pm UTC

@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Sorry @qnbs, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 4 hours and 24 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 61 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: f27c8720-1c96-42a3-aee6-ae09e60384d7
📥 Commits

Reviewing files that changed from the base of the PR and between aff1678 and ae96312.

📒 Files selected for processing: 7
  • CHANGELOG.md
  • crates/worldscript-secure-storage/src/pinned/removal.rs
  • crates/worldscript-secure-storage/src/pinned/tests/mod.rs
  • crates/worldscript-secure-storage/src/pinned/tests/remove_unix_cleanup.rs
  • crates/worldscript-secure-storage/src/pinned/tests/remove_unix_sync.rs
  • crates/worldscript-secure-storage/src/pinned/unix/removal.rs
  • docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md
📝 Walkthrough

Unix pinned removal now checks quarantine-directory identity and syncs both the parent and quarantine directories on relevant paths. Removal outcomes distinguish rollback, stranded, disturbed, and successful removal states. Tests and R-15 documentation cover directory changes and sync results.

Changes: Unix file removal

Layer / File(s) Summary
Quarantine identity and access
crates/worldscript-secure-storage/src/pinned/unix/removal.rs, crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
Quarantine records the identity of its created directory. Cleanup and retention checks verify that identity. Tests cover inaccessible or replaced directories and removal through a renamed directory.
Removal outcomes and durability
crates/worldscript-secure-storage/src/pinned/unix/removal.rs, crates/worldscript-secure-storage/src/pinned/removal.rs, crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs, docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md
Removal distinguishes rollback, stranded, and disturbed outcomes. Successful removal is confirmed only when both directory syncs are confirmed. Tests and R-15 documentation cover these outcomes and sync behavior.
Quarantine sync validation
crates/worldscript-secure-storage/src/pinned/tests/remove_unix_sync.rs, crates/worldscript-secure-storage/src/pinned/tests/mod.rs, crates/worldscript-secure-storage/src/pinned/unix/removal.rs, CHANGELOG.md
A test-only seam counts quarantine syncs and injects sync failures. Unix tests check sync counts and outcomes across removal, restoration, stranding, tampering, and early failures. The changelog records the safeguards and test cases.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to aff16

Unix removal in test-support may skip a directory sync in a narrow race where the entry vanishes right after being moved. It has no production caller and does not report a false success. It is mergeable, with a small follow-up to sync the quarantine directory on this path.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

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 ✅ Completed 2026-10-15T18:27:41.634062Z ae96312 New commits
ℹ️ 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.

@codeant-ai

codeant-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed aff1678 Oct 15, 2026 · 17:46 17:46
✅ Incremental review completed e39e837 Oct 15, 2026 · 14:55 14:55
✅ Reviewed your PR 30882e5 Oct 15, 2026 · 13:41 13:48

@codeant-ai

codeant-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

codescene-access[bot]

This comment was marked as outdated.

@codeant-ai

codeant-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: ae96312f
Scan Time: 2026-10-15 18:20:34 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.3% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

This test-support-only change hardens Unix authenticated file removal by tracking quarantine identity, avoiding deletion of replacement directories, reporting renamed-away entries as Disturbed, failing closed on owner-masked quarantines, and synchronizing affected directories on failure paths; comprehensive forced tests and durability documentation cover the new behavior.

Sequence diagram for hardened Unix quarantine removal

sequenceDiagram
    participant Caller
    participant Removal
    participant Quarantine
    participant Parent
    participant Observer

    Caller->>Removal: remove_authenticated_file
    Removal->>Quarantine: mkdirat
    Quarantine-->>Removal: identity
    Removal->>Quarantine: adopt
    Quarantine-->>Removal: entry moved
    Removal->>Removal: judge
    alt removal succeeds
        Removal->>Quarantine: unlinkat
        Removal->>Observer: BeforeSync
        Removal->>Parent: sync
    else rollback succeeds
        Removal->>Quarantine: restore
        Removal->>Observer: BeforeSync
        Removal->>Parent: sync
        Removal-->>Caller: error
    else put-back fails and quarantine remains named
        Removal->>Quarantine: retained
        Quarantine->>Quarantine: sync_all
        Removal->>Observer: BeforeSync
        Removal->>Parent: sync
        Removal-->>Caller: Stranded
    else quarantine was renamed away
        Removal->>Quarantine: retained
        Removal-->>Caller: Disturbed
    end
Loading

File-Level Changes

Change Details Files
Hardens Unix quarantine cleanup and failure handling against same-user interference while preserving fail-closed outcomes.
  • Records quarantine directory identity and validates the recorded name before cleanup or reporting retained entries.
  • Classifies renamed-away quarantine directories as Disturbed and refuses owner-inaccessible quarantine directories.
  • Adds best-effort parent/quarantine syncing after rollback, stranded, put-back, and vanished-name paths without masking the primary failure.
  • Expands forced tests for replacement directories, renamed quarantines, blocked put-back, root behavior, umask masking, and sync-stage observability.
crates/worldscript-secure-storage/src/pinned/unix/removal.rs
crates/worldscript-secure-storage/src/pinned/removal.rs
crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
Documents the new durability, identity-checking, and tamper-detection guarantees as test-support-only behavior.
  • Updates the changelog with scope, outcomes, and security limitations.
  • Extends the durable-evidence journal with sync semantics, cleanup bounds, renamed-quarantine behavior, and proof coverage.
CHANGELOG.md
docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 15, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 30882e5012

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/worldscript-secure-storage/src/pinned/unix/removal.rs Outdated
Comment thread crates/worldscript-secure-storage/src/pinned/unix/removal.rs Outdated
@codeant-ai

codeant-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

CodeAnt PR Risk: Medium Risk

  • The PR needs attention before merging.
  • On quarantine setup failure, Quarantine::create still removes the directory by name without checking identity, so a same-user rename race can remove a replacement empty directory.

Assessed commit: aff16785dc62

@qnbs
qnbs changed the base branch from feat/445-gate4e-h1a2-unix-file-removal to main October 15, 2026 14:00
…unix-hardening

# Conflicts:
#	CHANGELOG.md
#	crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
#	crates/worldscript-secure-storage/src/pinned/unix/removal.rs
#	docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md
codescene-access[bot]

This comment was marked as outdated.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

[check-pr-size] PR size is over the hard tier (normal profile): 8 files, 932 meaningful lines, 15 commits — limit ≤20 files / ≤1200 lines / ≤10 commits. Consider splitting into smaller, independently reviewable PRs.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b42f3fbd5

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
Comment thread crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
Comment thread crates/worldscript-secure-storage/src/pinned/unix/removal.rs
@codecov

codecov Bot commented Oct 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

…eld directory, narrow the cleanup claim (#445)

Review findings on the hardening:

- retained() checked the name of the quarantine directory but not that the entry
  is still inside; an actor that removed it and blocked the put-back got
  Stranded. A proven absence is now Disturbed (an error while looking is not
  a proof of absence).
- When the held quarantine directory was renamed away, the removal goes on in it
  and now syncs it as well as the parent before reporting Removed.
- A failure while constructing the quarantine directory synced nothing after
  removing it; it syncs the parent best effort.
- The identity check before the name-based rmdir narrows the race and does not
  close it; code and documentation say so.
- The permission-based tests skip for root only where the permission is the
  subject; the renamed-away and removed-entry cases run for every user.
codescene-access[bot]

This comment was marked as outdated.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe7158ea30

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/worldscript-secure-storage/src/pinned/unix/removal.rs Outdated
Comment thread crates/worldscript-secure-storage/src/pinned/unix/removal.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: ccfa2d0f-f20d-4b32-9846-83631fdef3c3
📥 Commits

Reviewing files that changed from the base of the PR and between 45ce3d0 and fe7158e.

📒 Files selected for processing: 5
  • CHANGELOG.md
  • crates/worldscript-secure-storage/src/pinned/removal.rs
  • crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
  • crates/worldscript-secure-storage/src/pinned/unix/removal.rs
  • docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Comment thread crates/worldscript-secure-storage/src/pinned/removal.rs Outdated
Comment thread crates/worldscript-secure-storage/src/pinned/unix/removal.rs Outdated
…antine directory, document the disturbed cases (#445)

Review findings on the hardening:

- retained() accepted any object at the recorded entry name, so an actor that
  moved the authenticated entry away and put another object there while
  blocking the put-back got Stranded. The entry must now still be the
  authenticated object (for a swapped-in object: the identity it was seen
  with); a proven absence or another object is Disturbed, and an error while
  looking proves neither.
- The held quarantine directory is synced after the unlink in every case
  (removed afterwards, renamed away, or kept alive by an entry an actor put
  into it); a sync that fails makes the removal Removed(NotConfirmed) instead
  of confirmed.
- The documentation of Disturbed names the removed and the replaced entry.
codescene-access[bot]

This comment was marked as outdated.

…a failed sync as itself (#445)

The cross-directory rename that puts an entry back, or moves it aside, changes
the quarantine directory as well as the parent. Only the parent was synced, so
after a put-back or a kept entry a crash could undo the change in the
quarantine directory while the parent already showed the name (CodeAnt risk
note on e39e837).

The quarantine directory is now synced once after the rename was tried, in
every path that moved an entry through it. The rename alone decides `restored`
and the sync is a separate result, so a sync that fails after a rename that
worked is not taken for a put-back that did not happen: no false `Disturbed`
or `Stranded`, which a `sync_all()?` inside the rename step would have caused.

- Swapped (put back, or kept aside): a failed sync is an error reported after
  the parent was synced, like a failing parent sync, and not another outcome.
- RolledBack and Stranded: best effort, the reported cause is not replaced.
- Disturbed (tampering) outranks a failed sync.
- Removed: unchanged, `Removed(NotConfirmed)` when the sync fails.
- Where nothing moved, nothing is synced.

An fsync error on a directory cannot be provoked portably, so the failing cases
use a small cfg(test) seam in `Quarantine::sync`; the cases without a failure
run the real sync and count how often it was asked for. The seam also closes
the gap that the Removed(NotConfirmed) path had no end-to-end test.

Mutation-checked: leaving out the sync after a put-back, after a rollback,
dropping a failed sync, a `sync_all()?` inside `restore()` and confirming a
removal despite a failed sync each fail at least one of the new cases.

test-support only, no caller, no new unsafe, no new package. Issue #359.
@codeant-ai codeant-ai Bot removed the size:L This PR changes 100-499 lines, ignoring generated files label Oct 15, 2026
@qnbs

qnbs commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Disposition of the CodeAnt "PR Risk: Medium" note on e39e83773c91 (put-back paths sync the pinned parent but not the quarantine directory changed by the cross-directory rename).

Valid. Fixed in aff16785 (head of this PR now), not deferred to H1b.

The finding holds on the code at e39e8377. The rename that moves an entry into the quarantine directory and the rename that puts it back each change that directory as well as the parent. Removed already synced it (e39e8377); Swapped { restored: true }, Swapped { restored: false } and the rollback path did not. A crash could keep the old state of the quarantine directory while the parent already showed the name (or the reverse).

What changed

  • The quarantine directory is synced once after the rename was tried, in every path that moved an entry through it. The rename alone decides restored; the sync is its own result.
  • That separation is the point of the fix. A sync_all()? inside restore() would make a failed sync look like a put-back that did not happen and would then be reported as Disturbed (the entry is gone from x). The tests below pin that this does not happen.
  • Swapped (put back, or kept aside): a failed sync is an error, reported after the parent was synced, like a failing parent sync. It is not another outcome.
  • Rollback and Stranded: best effort. The reported cause is not replaced by a sync error, and RemovalOutcome::Stranded now says so: a crash may undo where the entry is.
  • Disturbed (tampering) outranks a failed sync. Removed is unchanged (Removed(NotConfirmed) when the sync fails). Where the name led nowhere, or the observer failed before the move, nothing is synced in the quarantine directory.

What pins it (tests/remove_unix_sync.rs)

  • The quarantine directory is synced once for a removal, a put-back, a kept entry, a rollback and a stranded entry, and not when nothing moved.
  • With a failing sync, the put-back and the kept entry end in exactly that error with the entry where the rename left it, and the report is otherwise identical to the run without a failure. Rollback, stranded, tampering and the early outcomes are unchanged. A removal becomes Removed(NotConfirmed).
  • Mutation-checked, each caught by at least one of these cases: no sync after the put-back path; no sync in the rollback/stranded path; settle dropping the failed sync; the blind sync_all()? inside restore(); Removed confirmed despite a failed sync.

Limits, stated

  • An fsync error on a directory cannot be provoked portably. The failing-sync cases use a small cfg(test) seam in Quarantine::sync, which models the error. The cases without a failure run the real sync and count the calls. No crash is simulated, so nothing here proves what a given filesystem persists.
  • A failed quarantine sync is reported even if the directory is removed right afterwards. That is conservative on purpose: a failed sync proves nothing about what reached the disk.
  • Rollback and Stranded remain best effort (documented above), so they do not promise durability.
  • Unchanged, and not claimed by this commit: the unlinkat(name) race against a same-user actor and the link-count heuristic (see the residual in the module docs and on Desktop fs-data key-rotation migration is not crash-resumable (mixed-key state possible) #359). test-support only, no caller.

Local evidence only, hosted CI pending on this head: the removal unit tests (pinned::tests::remove*) pass on Linux; cargo fmt --check and cargo clippy --all-targets -D warnings are clean on the host, and compile clean for aarch64-apple-darwin and x86_64-pc-windows-msvc (compile only, not a run on those systems). pr:budget against main: CONVERGENCE, OK, 7 files, 14 of 15 commits. Everything green on e39e8377 is a different epoch and does not carry over to this head.

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Oct 15, 2026
codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Sync the quarantine directory when x vanishes after adoption. · removal.rs:364

crates/worldscript-secure-storage/src/pinned/unix/removal.rs:364
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Sync the quarantine directory when x vanishes after adoption.

If adopt moves an entry and another actor removes x before inspect, this branch returns Vanished. It skips Quarantine::sync, although the move changed that directory. Sync it before settlement, and report a failed sync after the parent sync. The updated durability contract in docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md requires a quarantine-directory sync after a move.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 4bb3cf30-9051-43da-aad2-2b4939e6ede9
📥 Commits

Reviewing files that changed from the base of the PR and between fe7158e and aff1678.

📒 Files selected for processing: 7
  • CHANGELOG.md
  • crates/worldscript-secure-storage/src/pinned/removal.rs
  • crates/worldscript-secure-storage/src/pinned/tests/mod.rs
  • crates/worldscript-secure-storage/src/pinned/tests/remove_unix.rs
  • crates/worldscript-secure-storage/src/pinned/tests/remove_unix_sync.rs
  • crates/worldscript-secure-storage/src/pinned/unix/removal.rs
  • docs/native/r15/GATE4D-JOURNAL-DURABLE-EVIDENCE.md
🚧 Files skipped from review as they are similar to previous changes: 1
  • CHANGELOG.md

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Inside::Absent => Ok(Step::Changed(RemovalOutcome::Vanished)),

P2 Badge Sync the quarantine when inspection finds the entry absent

When a same-user actor removes x before this inspection and leaves another entry in the quarantine so discard cannot remove the directory, this branch returns Vanished after syncing only the parent. The held quarantine was changed by the move and removal, so a crash can restore x there despite the operation having returned; route this path through a quarantine sync just like the other post-move outcomes, and report a sync failure rather than claiming completion.

AGENTS.md reference: AGENTS.md:L31-L33

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…ck the identity when a failed setup is cleaned up (#445)

Two findings on aff1678, both valid.

An entry that was moved aside and then found gone ("Vanished" at the
inspection, or an ENOENT at the unlink) had changed the quarantine directory
too, and the previous commit did not sync it for that outcome (Codex P2 and
CodeRabbit, independently): the earlier claim "every path that moved an entry
through it" was too broad. Both paths now go through Step::Moved like a
put-back: the directory is synced after the parent, and a sync that fails is
the error instead of a completed removal. The unlink path used to run the sync
and drop its result for this outcome.

Quarantine::create removed a directory by its name after a failed setup without
the identity check that discard() has (CodeAnt). The identity is now taken right
after mkdirat, which returns no descriptor, and the cleanup removes the name
only if it still leads to that directory; without an identity nothing is
removed. discard() and the cleanup share remove_if_named(). rmdir takes a name,
not a descriptor, so this narrows the race and does not close it: what a lost
race can remove is at most an empty directory.

Tests: two scenes (entry gone before the inspection, gone before the unlink)
in the sync cases, with and without a failing sync; the removal by name is
exercised on its own (the directory that was made goes; one renamed away and
replaced, a non-empty one and a name without an identity stay). The setup
failure that reaches the cleanup in create() needs a changed process umask and
is not forced. Mutation-checked: skipping the sync for an entry gone at the
inspection, dropping it for one gone at the unlink, and removing the identity
comparison each fail at least one case.

CHANGELOG and the evidence document say what the tests pin. test-support only,
no caller, no new unsafe, no new package. Issue #359.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gates Passed
3 Quality Gates Passed

See analysis details in CodeScene

Quality Gate Profile: The Bare Minimum
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@qnbs

qnbs commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Second wave, on aff16785: three findings, all valid, fixed in ae96312f.

Correction first. My disposition above says the quarantine directory is synced "in every path that moved an entry through it". That was too broad. One path was missing, and Codex (P2) and CodeRabbit (Minor, outside the diff) found it independently.

1. Entry found gone after it was moved aside (Codex P2, CodeRabbit, removal.rs:364, Inside::Absent). The move changed the quarantine directory, an actor then removed the entry, and the outcome Vanished was reported after syncing the parent only. Same class as the put-back, so it is handled the same way: the quarantine directory is synced after the parent, and a sync that fails is the error instead of a completed removal.
While checking siblings I found the second place with the same hole: an ENOENT at the unlink also gives Vanished. That path did run the sync but dropped its result. Both paths now go through the same step.

2. Cleanup after a failed setup removed the directory by name without an identity check (CodeAnt, Quarantine::create). discard() had the check, the failure path of create() did not, so the CHANGELOG and evidence sentence "removed by its name only if the name still leads to the directory that was created" was not true for it. The identity is now taken right after mkdirat (which returns no descriptor), and the cleanup removes the name only if it still leads to that directory; without an identity nothing is removed. discard() and the cleanup share one function. As before, rmdir takes a name and not a descriptor: this narrows the race and does not close it, and what a lost race can remove is at most an empty directory.

What pins it

  • Two new scenes in remove_unix_sync.rs (gone before the inspection, gone before the unlink): the quarantine directory is synced once, and with a failing sync the result is exactly that error with the same state as the run without a failure.
  • remove_unix_cleanup.rs: the removal by name takes the directory that was made, and leaves one that was renamed away and replaced by an empty directory of the actor, a non-empty one, and a name without an identity.
  • Mutation-checked, each caught: skipping the sync for an entry gone at the inspection; dropping it for one gone at the unlink; removing the identity comparison (also caught by the existing renamed-away test).

Limits, stated. The call in create() is not forced end-to-end: reaching it needs a setup that fails after mkdirat, which the tests could only provoke by changing the process umask, so they exercise the shared function instead. As before, the failing sync is modelled through the cfg(test) seam; no crash is simulated.

Budget, plainly. This is commit 15 of 15. The budget tool reports SATURATED / FREEZE for the head (the ceiling is reached, not exceeded; exit 0). No further commit can be added to this PR: a further finding means a clean successor that carries the reviewed net state. Local evidence for ae96312f: fmt and cargo clippy --all-targets -D warnings on the host and, compile-only, for aarch64-apple-darwin, and the pinned:: unit tests pass on Linux, all on the final tree (x86_64-pc-windows-msvc compiled clean before a comment-only edit in a Unix-only file, which that target does not build). Hosted CI and review of this head are pending; results on aff16785 do not carry over.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae96312f19

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CHANGELOG.md

## [Unreleased]

- **R-15 Gate 4:** hardening of the Unix file removal against a same-user actor in the quarantine directory, `test-support` only (slice H1a-2, part 2b of 5). The parent is synced after a rollback, a stranded entry, a put-back and a vanished name, and the quarantine directory an entry moved through is synced too (a sync of it that fails is an error after a put-back, a kept entry or an entry an actor removed again, and makes a removal `NotConfirmed`, never another outcome); the quarantine directory, also when its setup fails, is removed by its name only if the name still leads to the directory that was created; an entry is reported as retained only if the recorded name still leads to its directory, otherwise `Disturbed`; a quarantine directory that cannot be opened is refused. Tests force each case, including the quarantine renamed away and the put-back blocked. No new `unsafe`, no new package. Issue #359. PR #1072.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Qualify the name-based cleanup guarantee

When a same-user actor replaces the checked quarantine with an empty directory between identity_at and the name-based unlinkat, remove_if_named can remove that replacement, so this release note's “only if” statement overstates the preservation guarantee. Fresh evidence after the earlier finding is that the final changelog still makes the absolute claim even though the code and evidence document now acknowledge that the identity check only narrows the race; qualify this summary the same way.

AGENTS.md reference: AGENTS.md:L29-L30

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Valid, wording only. remove_if_named checks the identity and then calls unlinkat by name, so a swap between the two can still remove an empty replacement directory. The code comment and the evidence document say the check narrows the race and does not close it; this CHANGELOG line still says "only if".

Not fixed on this head: the PR is at 15 of 15 commits (budget SATURATED), and a 16th commit is not allowed. The thread is left unresolved on purpose. The wording belongs in the next change that touches this entry (or in a clean successor), which is the maintainer's call; I will not describe it as fixed before a commit exists.

Ok(Step::Changed(RemovalOutcome::Swapped { restored }))
}
Inside::Object(_, found) => self.swapped(Some(found)),
Inside::Foreign => self.swapped(None),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track the identity of foreign retained entries

When the swapped-in entry is classified as Foreign (for example a symlink, directory, FIFO, or inaccessible file), this arm passes no expected identity, so if a same-user actor replaces x after inspection and blocks the restore, holds(None) accepts any replacement and the operation returns Swapped { restored: false } even though the observed entry is no longer at the recorded recovery location. Fresh evidence beyond the prior retained-object finding is this distinct Inside::Foreign arm explicitly bypassing the identity check now supplied by the Object arm; retain the no-follow stat identity for foreign entries and report Disturbed on a mismatch.

AGENTS.md reference: AGENTS.md:L29-L33

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Valid. Inside::Foreign is passed as swapped(None), so holds(None) accepts any object at x: if a same-user actor replaces a foreign entry (link, directory, FIFO, inaccessible file) after the inspection and also blocks the put-back, the outcome is Swapped { restored: false } although the entry is no longer where it says. e39e8377 closed this for the Object arm (the identity it was seen with); Foreign is the remaining gap in "reported as retained only if the entry is where the outcome says".

The fix is small: carry the no-follow stat identity of the foreign entry in Inside::Foreign and report Disturbed on a mismatch. It also needs a test seam, because no observer stage sits between the inspection and the retained check, so the replacement cannot be forced deterministically today.

Not fixed on this head: the PR is at 15 of 15 commits (budget SATURATED), and a 16th commit is not allowed. The thread is left unresolved on purpose. Whether this is fixed in a clean successor or in the next change to this file is the maintainer's decision; until a commit exists I do not call it fixed.

qnbs commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Operator control checkpoint — 2026-10-15, exact head ae96312f

MERGE HOLD. This PR is saturated at the repository's absolute 15-commit limit. The final-head Codex review just reported two new actionable P2s: CHANGELOG overstates identity-bound cleanup and Inside::Foreign / swapped(None) can misreport retained entry identity after another swap. These are not resolved by the commit-15 sync fix. Current review census: 11 threads / 2 open. A green CodeQL or PR-size status is not a substitute for disposition of these findings or terminal exact-head CI. Do not merge #1072, add commit 16, rewrite/force-push its history, or bypass the cap.

Next admitted corrective path: Follow the existing SATURATED → clean signed replacement/successor procedure from the then-current protected main. Carry forward the entire verified semantic net diff of #1072 and focused regression evidence, and fix both P2s in one bounded correction wave. For Foreign, prove no-follow foreign-entry identity and a second same-user replacement plus failed restore cannot produce a false Swapped { restored: false } recovery-location claim. For CHANGELOG, explicitly qualify the inherent POSIX name-based check→rmdir residual. Do not trade away preservation, tests, feature isolation, security review, or exact-head CI for budget. Cross-link and only then supersede this frozen PR when the new PR is published and verified; never reuse this PR's CI/reviews as replacement proof.

Downstream: replacement must remain test-support only; no productive H1/R1 sweep, Gate5, Gate7 or release admitted. After legitimate protected replacement squash and exact-main postmerge/Production/SAFE-retention #627 evidence, safely reconstruct #1070 and #1071 from clean main rather than blindly retargeting their old stacked bases. Same-user unlink/rmdir residual still needs explicit production threat-model owner decision.

Canonical fully curated Linear directions: Gate4–7 live decision ledger; current issue heads QNB-168, QNB-11, QNB-145. These reflect the newest Codex findings and correct the prior Q1-D1 "pending" wording: GH #359/6186463012 already confirmed the Core-sealed clock refinement, without attesting OS time or approving the release.

qnbs commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Maintainer decision already recorded — execute clean successor, do not wait for another choice (2026-10-15)

The latest replies at 18:54 UTC correctly acknowledge both Codex P2s but again describe the corrective route as a "maintainer's call." That call has already been made and published: canonical Linear Gate4–7 ledger, QNB-168, and the earlier PR control comment.

FINAL DECISION FOR THIS BRANCHING QUESTION: #1072 SATURATED → clean signed replacement PR from the refreshed protected main, not a 16th commit, not a cosmetic deferral, not an unsafe merge, no force-push or policy exception. Start this authorized work autonomously now, without requesting another owner vote on the same decision.

Carry forward the reviewed net semantic diff of #1072 and preserve test-support isolation; in one bounded correction wave fix P2 cleanup wording and P2 foreign-object retained identity including a deterministic adversarial interleaving/test seam, honest no-follow identity handling and safe fail-closed behavior. Audit and sign the new commits, verify actual base-diff, run budget/prepush and hosted Windows/macOS/Ubuntu evidence, request/collect substantive exact-head three-channel review, resolve all findings. #1072 remains frozen and unmerged until the replacement is published, crosslinked, reviewed and merged; retire #1072 only as superseded with evidence.

As of the last audit, #1072 exact head ae96312f now has CI/CD and CodeQL SUCCESS, but two Codex P2 threads OPEN, so success is not merge admission. Do not replay its green checks for the new PR. Protected squash with head pin only after all gates; then resulting-main CI/CodeQL, Vercel Production READY/aliases/HTTP and #627 SAFE retention, next safe #1070/#1071 restack. No H1 productive deletion, Gate5/6/7 release or Dependabot train.

Unattended execution rule: Re-read AGENTS.md and repository safety/merge rules. Distinguish a new required owner decision from routine engineering decisions already delegated. Do not turn an existing authorization, a CI wait or a reviewer quota into an unnecessary idle stop. Continue bounded safe investigation/preparation during waits; refuse only the action genuinely blocked by a security or merge gate. Report verifiable heads, links, gates and progress succinctly; keep QNB-168 and #359 up to date without duplicated narrative. No instruction here authorizes bypassing deterministic gates.

@qnbs

qnbs commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Successor opened: #1073. This PR stays frozen and must not be merged.

#1072 is at 15 of 15 commits (budget SATURATED) with two valid P2 findings open (the CHANGELOG "only if" wording, and the Inside::Foreign identity). #1073 is one signed commit on main that carries the reviewed net tree of ae96312f plus fixes for both, with a deterministic test and a control for the second. The two threads here stay unresolved on purpose; they are closed in #1073, not here.

This PR is closed as superseded, not merged, once #1073 is integrated. CI and review results on ae96312f do not carry over to #1073.

qnbs added a commit that referenced this pull request Oct 15, 2026
…ity proof (#445) (#1073)

* feat(core): harden the Unix file removal against a same-user actor in the quarantine directory (#445)

Clean successor of #1072 (head ae96312, 15 of 15 commits, budget SATURATED):
the reviewed net state of that PR plus the two findings that were still open
on it. test-support only, no caller, no new unsafe, no new package.

Carried from #1072: the parent is synced after a rollback, a stranded entry, a
put-back and a vanished name; the quarantine directory an entry moved through
is synced too, and a sync of it that fails is an error after a put-back, a kept
entry or an entry an actor removed again and makes a removal NotConfirmed,
never another outcome; the rename alone decides `restored`; the quarantine
directory is removed by its name only after an identity check, also when its
setup fails; an entry is reported as retained only if the recorded name still
leads to its directory, otherwise Disturbed; a quarantine directory that cannot
be opened is refused.

Fixed on top of it:
- A foreign entry (link, directory, FIFO, inaccessible file) carries the
  identity it was seen with (a look that does not follow a link), as an opened
  object does, so an actor that replaces it after the inspection while the
  put-back is blocked gets Disturbed instead of Swapped { restored: false }.
  An identity that cannot be taken is a failed inspection and not a retained
  entry, so an unobservable state earns no unproven success. The test replaces
  the entry at the sync of the quarantine directory, the one point between the
  inspection and the check that a test can reach, and a control leaves the
  same entry untouched (it is still kept).
- The changelog no longer says the name-based cleanup removes "only if" the
  name leads to the directory that was created: the check narrows the race and
  does not close it, as the code and the evidence document already said.

Mutation-checked, each caught: passing no identity for a foreign entry, plus
the mutations recorded on #1072 for the sync, the identity comparison and the
vanished-entry paths. The failing-sync cases use a small cfg(test) seam that
models the error; no crash is simulated. Issue #359. The PR number in the
changelog entry is to be set to the number of this PR when it is opened.

* docs(changelog): set the PR number of the Unix quarantine hardening entry (#445)

* fix(core): treat a failed look at a retained entry as no proof, state the limit of a foreign identity (#445)

Two findings on 8f8c988, both valid.

A look at the entry that failed (a search permission lost after the move) was
counted as "the entry is there", so a removal whose put-back was blocked
reported Stranded, and a kept foreign entry reported Swapped (restored false),
without having seen the entry (Codex P2). An outcome that says where an
entry is needs a proof, and a failed look proves nothing: it is now Disturbed.
The identity that is compared is no longer optional (the "any object counts"
path had no caller left), so there is no lenient path to fall into. The
Stranded case that is proved, with the name taken again, is unchanged and runs
for root as well; the permission case now expects Disturbed and still checks
that the entry is in fact the authenticated inode, which is why this is
conservative and not a claim that the directory was tampered with.

The identity of a foreign entry is a stat identity without a held handle (Codex
P2): a link, a FIFO or a device node is not opened, because opening has
effects, and no handle can be taken for every kind without them. A file system
may give the identity of a removed object to its replacement, which the check
cannot tell apart. This is stated where the outcome is described (the variant,
the Disturbed contract, the evidence document and the changelog) rather than
covered by a mechanism that would hold for Linux only; it is part of the
same-user residual that stays owner-gated before any production use.

Mutation-checked: counting a failed look as present fails the permission case.
test-support only, no caller, no new unsafe, no new package. Issue #359.
@qnbs

qnbs commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #1073, not merged.

#1073 was integrated as 1f37cd83 (protected squash, tree identical to its validated head 1602d79e). It carries the reviewed net tree of this PR plus the fixes for the two findings that were still open here, and the two further findings its own review raised. The two threads here stay unresolved on purpose: they are answered and closed in #1073.

This PR was never merged, no history was rewritten and the branch is kept. Checks and reviews on ae96312f did not carry over to #1073.

@qnbs qnbs closed this Oct 15, 2026

This branch was successfully deployed

1 active deployment
Preview — ae96312f Deployed Oct 15, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant