Skip to content

fix(update): release the restart lease before the service refresh (#5760) - #6404

Merged
lidge-jun merged 11 commits into
devfrom
devin/1790861312-restart-lease-release
Oct 2, 2026
Merged

lidge-jun merged 11 commits into
devfrom
devin/1790861312-restart-lease-release

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Release the dashboard update worker's ownership-mutation lease immediately before ocx service repair. Task Scheduler, launchd, and systemd start the managed child outside the worker's process tree, so that child cannot inherit the worker's delegated token and otherwise times out acquiring the lease.
  • Reacquire the lease before the direct-start fallback, then recheck ownership while holding it through reclamation and startup. A claim acquired during refresh or a lease that remains busy refuses destructive fallback; the job remains succeeded with restarted: false.
  • The service-manager refresh window is intentionally unleased; the pre-refresh reclaim and direct-start fallback remain protected. Ownership and exact-runtime checks are preserved.

Verification

  • Refreshed from dev at 06cc3815c06a10120d89ef986c46be95be37f28a without rewriting branch history, including the accepted upstream role-route and owner-registry ACL fixture corrections. Targeted verification: role routes 7 passed; owner-registry ACL 6 passed. Required CI passed at e572fde011a0116a2e8a205c10a167a59632372a: zero failed and zero pending checks (platform/path-inapplicable checks remain skipped).

  • Initial regression reproduced the failure with a real token-less bun src/cli/index.ts start child on Windows, then verified successful health publication after release.

  • Follow-up regressions exercise an installed-service refresh with a claim arriving during the released window, competing acquisition during fallback, and bounded failure when a claimant keeps the lease. All seven restart-lease tests passed on Windows after the correction.

  • Linux CI exposed mismatched parent/child fixture authority. The child now forwards the parent's captured protected home and uses that guarded home on POSIX, while Windows retains its sandbox home. The authority equality assertion and real CLI lease refusal remain enforced. A cancellable watchdog bounds exit and output draining. All seven focused tests and typecheck passed locally; the latest Linux shard and aggregate CI passed at e572fde011a0116a2e8a205c10a167a59632372a.

  • Initial focused update regressions: 126 passed. Follow-up typecheck, privacy scan, structure check, and focused update/service/CLI regressions passed.

  • Required CI is green. Native desktop takeover evidence for the separate Windows lifecycle PRs does not exercise this dashboard-update path.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • 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).

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Link to Devin session: https://app.devin.ai/sessions/a8795b27d4114aa6be37a88565e404b2
Open in Devin Desktop: https://app.devin.ai/desktop/session/a8795b27d4114aa6be37a88565e404b2?variant=devin

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • 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).

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

)

The dashboard update worker held the ownership-mutation lease across the
entire restart, including 'ocx service repair'. The service manager's
'ocx start' child is not a descendant of the worker — Task Scheduler,
launchd and systemd spawn it with the stored registration environment —
so it carries no delegated token, dies at the lease acquire deadline,
and the repair's serving probe fails. Every service-installed dashboard
update then fell through to an unmanaged detached start beside a dead
supervisor — the same class bin/ocx.mjs fixed for failed npm updates.

The restart callback now receives an UpdateRestartLeaseControl: the
worker releases the lease immediately before the service refresh and
re-runs the recorded-owner veto at the fallthrough boundary, so a claim
landing during the unleased refresh stops the direct start instead of
killing or replacing it. The job records the veto as
succeeded/restarted:false.

Regression coverage spawns a real supervised 'ocx start' against the
held lease (dies with the lease error), then drives restartAfterUpdate
through the service path post-fix (child binds and serves) and through
the fallthrough boundary under a mid-refresh claim (no kill, no spawn).

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

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 1cce787c-2186-4baa-8f54-7ec67903d30e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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 bug Something isn't working label Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

…lthrough (#5760)

The previous seam released the lease for the service refresh but left the
fallthrough's veto re-check, killAllOcxOnPort reclaim and pinned start
unleased — a desktop claim landing in that window could be killed.
reacquireForDirectStart takes the lease back (bounded; a still-claimed
lease fails closed), re-arms the delegation token, then re-runs the veto
under the held lease. Regression coverage: claim-in-window veto,
lease re-held through reclaim/direct start with a live contender probe,
and a real lease-holding child proving the bounded acquire fails closed.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
lidge-jun and others added 2 commits October 1, 2026 15:13
…does

The suite pinned the lock dir to home/.opencodex, which is only the
Windows authority: USERPROFILE tracks the sandbox home there. On POSIX
homedir() ignores $HOME and the armed test-home guard drops that legacy
entry from serviceStatePaths(), so the lease binds to the OPENCODEX_HOME
record instead — existsSync(lockDir) was false at every step and the
spawned child's contention was never the path the test probed.

Resolve the authority with serviceStatePaths().at(-1) and mirror
leasePath()'s realpath fallbacks, so both the lock-dir assertions and the
foreign-claimant probes address the path the module actually locks.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… watchdog

The POSIX CI shard keeps this child alive past the watchdog; without its
stdout the stuck point is invisible. Log the captured output before the
watchdog throws.

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

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/0 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/0).

Review readiness checklist

  • ⬜ 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).
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/0 boxes ticked.

Automatic draft conversion failed. Please convert this pull request to a draft manually until every box above is ticked.

@github-actions
github-actions Bot marked this pull request as draft October 1, 2026 15:53
lidge-jun and others added 5 commits October 1, 2026 16:16
The Linux child bound and served under the held lease, so its
serviceStatePaths() resolved a different authority — likely the legacy
home entry that the armed guard did not drop in the spawned env. Log the
candidates (os-homedir lock, env REAL_HOME, resolved paths) so the next
CI run shows which path it used.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 1, 2026 17:54
@github-actions
github-actions Bot marked this pull request as draft October 1, 2026 17:54
# Conflicts:
#	structure/ops/service-and-sidecars.md
@lidge-jun

Copy link
Copy Markdown
Owner

Maintainer integration record (MAINTAINERS.md, dev-only)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 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-02T00:13:43.281139Z e7a5905 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.

@lidge-jun
lidge-jun merged commit 0f2ec7a into dev Oct 2, 2026
33 checks passed
@lidge-jun
lidge-jun deleted the devin/1790861312-restart-lease-release branch October 2, 2026 00:10

@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: e7a590533c

ℹ️ 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 src/update/job.ts
// `service repair` re-activates a manager whose `ocx start` child is not a
// process descendant and cannot join this lease; held through its serving
// wait, the lease makes that child die at the acquire deadline (#5760).
io.releaseForServiceManagerFn?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep ownership serialized until the repair is authorized

On macOS and Linux, releasing the ownership-mutation lease before spawning the entire service repair command creates a takeover race: repairService() reads ownership once at src/service/repair.ts:169, but a desktop claim can commit immediately afterward while the repair continues to rewrite, stop, and restart the service. If that refresh serves successfully, the early return at line 1234 skips the new reacquisition/veto entirely, so the updater can mutate a service that became desktop-owned during the released window. Keep the lease through the repair's ownership check and service mutations, releasing it only at the OS-manager activation boundary, then revalidate ownership before accepting success.

Useful? React with 👍 / 👎.

Comment thread structure/runtime.md
manager children. A replacement refusal passes through owner-aware recovery: only the same CLI
owner revives the stopped runtime; foreign ownership stays transferred and unknown ownership
remains a reported recovery requirement. Dashboard restart delegates the lease token to its repair child. Direct start holds the same lease through bind plus PID and runtime-address publication. If listener rollback cannot prove the socket closed, the process retains its lease until exit.
remains a reported recovery requirement. Dashboard restart delegates the lease token to its `service repair` child, and releases the lease before that refresh: the service manager spawns `ocx start` outside the worker's process tree with no token, so the managed child must take the lease itself (#5760). Before the direct-start fallthrough kills or spawns on the port, the worker takes the lease back — a refresh that stayed claimed stops the restart — and re-runs the recorded-owner veto under it because a claim could have landed during the unleased refresh window. Direct start holds the same lease through bind plus PID and runtime-address publication. If listener rollback cannot prove the socket closed, the process retains its lease until exit.

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 Remove the obsolete lease-delegation claim

This contract now says the dashboard delegates its lease token to service repair while also saying it releases the lease before spawning that refresh. The new releaseForServiceManager() path restores or removes OCX_OWNERSHIP_MUTATION_LEASE_TOKEN before runService, so the repair child is intentionally not delegated the current token. Describe the refresh as unleased instead; otherwise this architecture document gives maintainers the opposite concurrency model from the implementation.

AGENTS.md reference: structure/AGENTS.md:L9-L10

Useful? React with 👍 / 👎.

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.

1 participant