Skip to content

fix(config): revalidate ownership before honoring a spent publication wait - #8422

Merged
lidge-jun merged 1 commit into
devfrom
codex/ownership-expired-wait-race-20261109
Nov 10, 2026
Merged

lidge-jun merged 1 commit into
devfrom
codex/ownership-expired-wait-race-20261109

Conversation

@luvs01

@luvs01 luvs01 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Follow-up fix for a review finding on #8270 (merged as 25a1bfc): a spent publication wait could be cached under the identity of a completed owner/manifest pair, after which every registration matched the record and returned unowned without reloading. The bounded creation timeout became a persistent registration failure, leaving later paths outside uninstall tracking.

  • On an expired-wait identity match, run one plain loadOwnership() before refusing: the record suppresses the bounded wait, never the strict reload.
  • Only record a spent wait when the observed identity stayed unchanged across the wait, so the record always names the state the last failed read saw; a pair that changed during the wait spends its own wait on the next registration.

Tests

  • a spent wait recorded on a completed pair still reloads and records: child process spends the wait on an invalid pair, completes it under an identical stat identity, then must register instead of sticking unowned (fails as unowned without the fix).
  • a pair changed during the publication wait spends its own wait: a mid-wait pair change is not marked spent; the next registration pays one wait, then suppression resumes.
  • bun run typecheck clean; the two new tests pass on the fix and fail on the unfixed code.

Note: in this elevated Windows shell, 21 unrelated tests in the same file fail identically on the base commit with ELEVATED_UNINSTALL_REFUSAL (uninstall refuses elevated runs); all ownership-registration paths this change touches pass.

Summary by CodeRabbit

  • Bug Fixes
    • Improved configuration ownership registration when ownership metadata is being created or updated concurrently. Registration now reloads ownership after a wait expires, and changes to the metadata during that wait receive a fresh wait before registration proceeds.
    • Prevented homes from being incorrectly treated as unowned when valid ownership metadata becomes available at the end of a wait.

… wait

A spent publication wait was remembered under the identity sampled after
loadOrCreateOwnership() returned. Another process completing the owner
marker between the wait's last failed read and that sampling stored the
completed pair's identity, so every later registration matched it and
returned unowned without reloading - the bounded timeout became a
persistent registration failure.

- On an identity match, run one plain loadOwnership() before refusing:
  the record suppresses the wait, never the reload.
- Only record a spent wait when the identity stayed unchanged across it,
  so it always names the state the last failed read actually saw; a pair
  that changed during the wait spends its own wait next registration.

Reported by chatgpt-codex-connector review on #8270.
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Walkthrough

initializeConfigOwnership now reloads ownership after an expired wait and records that wait only when the metadata identity remains unchanged. New subprocess tests cover unchanged and changed metadata identities.

Changes: Ownership wait revalidation

Layer / File(s) Summary
Revalidate ownership after wait
src/lib/config-ownership.ts
After an unchanged metadata identity completes its wait, initializeConfigOwnership performs a plain ownership load. It clears the expired-wait marker if the load succeeds and records the marker only if the identity remains unchanged.
Test publication-wait identities
tests/config/config-ownership-concurrent-record.test.ts
Subprocess helpers exercise ownership-marker publication races. Tests check registration when valid metadata returns at the spent identity and verify that changed metadata incurs another wait before a later initialization reuses the spent-wait record.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Possibly related PRs

  • lidge-jun/opencodex#8121: Changes registration and metadata revalidation during the same ownership publication wait flow.
  • lidge-jun/opencodex#8270: Changes the first-registration publication wait and tests incomplete owner metadata in the same files.
Merge Risk: Minimal · up to cabf9

No demonstrated issue blocks merging this ownership-wait fix after normal checks.

Pre-merge checks: Passed 5

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the main change: revalidate ownership before honoring an expired publication wait.
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
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 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 2 files.

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • 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

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Nov 9, 2026
@luvs01
luvs01 marked this pull request as ready for review November 10, 2026 01:43
@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

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

Review Status Commit Review trigger
🔒 Security Review ❌ Failed 2026-11-10T01:43:20.261534Z
We couldn’t complete this review. Comment @codex security review to try again.
cabf951 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

The account paying for this security review has reached its Codex usage limits. The payer can check the Codex usage dashboard. For personal accounts, using credits requires enabling “Use credits for security reviews” in Code review settings. If you do not manage the paying account, contact this repository's admins.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional comments: 1

tests/config/config-ownership-concurrent-record.test.ts-119-145: 1

119-145: Trivial · Maintainability & Code Quality · Low value

Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Test depends on the exact count of Date.now reads, which makes it fragile.

Line 135 writes the owner marker when reads === 3. This count matches the current loadOrCreateOwnership loop (the deadline read, then one read per iteration). The test fails if a later change adds or removes a Date.now call on the initializeConfigOwnership path. Examples are a change to createOwnership or to the lock helper. Such a failure would look like a regression in the revalidation logic.

Also, the firstMs >= 90 and secondMs >= 90 checks at lines 983 and 986 use wall-clock time. The wait is 100 ms and Bun.sleepSync has a lower bound, so these checks should be stable. The thirdMs < secondMs check is safe only because the third call skips the wait.

Consider a more robust trigger. For example, write the marker on the first Date.now read made after the first Bun.sleepSync iteration. Alternatively, add a short comment that ties the number 3 to the source loop, so a maintainer knows to update it.

Review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 9e863b1 and cabf951.

Files selected for processing: 2

  • src/lib/config-ownership.ts
  • tests/config/config-ownership-concurrent-record.test.ts

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

@lidge-jun

Copy link
Copy Markdown
Owner

Maintainer judgment

Integrate at cabf95195b43471e031df604e479112b9c7eaf27, subject to an unchanged head and mergeability. This fixes a real ownership-registration race: a completed owner/manifest pair could remain treated as unowned after an earlier publication wait expired, leaving later files outside uninstall tracking. The change reloads the pair without repeating a spent wait and records a spent identity only when it stayed stable during the wait.

There is no GUI or CLI presentation change and no config migration. Existing homes can now register a valid completed pair; invalid, foreign and tombstoned pairs must continue to fail closed. The change uses existing filesystem APIs and path checks. Windows and macOS CI shards did not execute on this head; stat identity and junction behavior on Windows remain unverified.

Fresh independent code and separate security reviews passed this exact head. The security review reports negative probes for tombstoned, foreign-root, symlinked and unsafe-path cases. The code review reports head/base regression tests, focused ownership suites and typecheck; its full local run reported 17 failures, reproduced by that reviewer on clean dev. I checked the diff and reviewed the owned structure contract: it already states later re-reads and a wait of up to 100 ms; explicitly documenting the spent-wait optimization would be clearer but is not required to resolve a contradictory contract.

CI run 42560377866 is successful on the exact head, including gates, test changed and the aggregate. No change-caused CI failure is known.

NOT RUN by maintainer: local tests, full suite, Windows/macOS shards and independent reproduction of the reviewer's probes and base failures. The security review did not run privacy:scan; the code reviewer reports it passing.

Maintainer integration decision: use the dev-only integration policy if GitHub blocks only on the approval requirement; that decision does not replace the independent and security reviews.

@lidge-jun
lidge-jun merged commit 8c28f5f into dev Nov 10, 2026
40 checks passed
@lidge-jun
lidge-jun deleted the codex/ownership-expired-wait-race-20261109 branch November 10, 2026 10:11
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.

2 participants