Skip to content

fix(session): remove Darwin replacement predecessors - #3799

Closed
snowykr wants to merge 9 commits into
Yeachan-Heo:devfrom
snowykr:fix/darwin-managed-replace
Closed

fix(session): remove Darwin replacement predecessors#3799
snowykr wants to merge 9 commits into
Yeachan-Heo:devfrom
snowykr:fix/darwin-managed-replace

Conversation

@snowykr

@snowykr snowykr commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • remove persisted Darwin replacement receipts and predecessor retention
  • unlink the authenticated, verified predecessor before parent durability sync
  • complete macOS-only native lint requirements
  • cover artifact-free Darwin transcript replacement

Verification

  • Rust scope check passed:
  • crates/pi-natives: N-API addon boundary for native, CPU-bound, blocking I/O, and OS integration primitives.
  • crates/pi-shell: Embedded shell, PTY, and process-management runtime used behind native bindings.
  • crates/pi-ast: Tree-sitter parsing and summarization hot paths shared by native code.
  • crates/pi-iso: Native filesystem isolation backends such as clone, reflink, overlay, and ProjFS.
  • crates/gjc-sdk: Gajae-Code SDK Rust core for loopback WebSocket transport, endpoint discovery, and planned N-API integration.
  • crates/brush-core-vendored: Vendored Rust shell runtime dependency for the native shell boundary.
  • crates/brush-builtins-vendored: Vendored Rust shell builtin dependency for the native shell boundary.
  • 1 non-crate Rust fixture(s) allowed for tests only.
  • Building pi-natives for darwin-arm64 (ci)…
    Normalizing native addon filename: pi_natives.darwin-arm64.node → pi_natives.darwin-arm64.node
    Generated 89 explicit ESM exports in index.js, fixed 9 const enums in index.d.ts
    Build complete.
  • Checked 2509 files in 1961ms. No fixes applied.
  • bun test v1.3.14 (0d9b296a)
  • bun test v1.3.14 (0d9b296a)

snowykr added 9 commits August 3, 2026 12:44
Managed transcript rewrites were explicitly rejected on macOS after exact replacement hardening.

Use a Darwin-only flock-authorized atomic exchange and retain the predecessor as durable recovery evidence.

Lore-id: darwin-managed-replace
Constraint: preserve Linux and Windows replacement paths
Scope-risk: medium
Reversibility: revertable
Tested: cargo check -p pi-natives; bun --cwd=packages/natives run check:types; bun --cwd=packages/coding-agent run check:types; bun test packages/coding-agent/test/darwin-managed-replace.test.ts
The Darwin replacement regression test previously only proved that a replacement artifact existed.\n\nAssert that the exchanged predecessor retains the prior transcript bytes.\n\nLore-id: darwin-managed-replace-test\nScope-risk: low\nReversibility: revertable\nTested: bun test packages/coding-agent/test/darwin-managed-replace.test.ts
The new Darwin native implementation must satisfy the repository Rust formatter.\n\nLore-id: darwin-managed-replace-format\nScope-risk: low\nReversibility: revertable\nTested: cargo fmt --check; cargo check -p pi-natives; bun test packages/coding-agent/test/darwin-managed-replace.test.ts
Darwin append and direct replacement paths must share the exact-exchange admission lock.\n\nAvoid re-running the stale expected-identity fence after a successful exchange, retain optional loader validation context, and document the shipped surface.\n\nLore-id: darwin-managed-replace-review\nConstraint: preserve Linux and Windows paths\nScope-risk: medium\nReversibility: revertable\nTested: bun --cwd=packages/coding-agent run lint; bun --cwd=packages/natives run lint; bun test packages/coding-agent/test/darwin-managed-replace.test.ts; bun test packages/natives/test/memory-guard-native.test.ts; bun --cwd=packages/coding-agent run check:types; bun --cwd=packages/natives run check:types; cargo fmt --check; cargo check -p pi-natives
The generated declaration must match the formatted native documentation.\n\nLore-id: darwin-managed-replace-generated\nScope-risk: low\nReversibility: revertable\nTested: bun --cwd=packages/natives run build; bun --cwd=packages/natives run check:types
Managed append must fail closed while the exact replacement lock is held.\n\nLore-id: darwin-managed-replace-admission-test\nScope-risk: low\nReversibility: revertable\nTested: bun test packages/coding-agent/test/darwin-managed-replace.test.ts; bun --cwd=packages/coding-agent run lint; bun --cwd=packages/coding-agent run check:types
Managed removal and consume must not race the Darwin exact replacement exchange.\n\nLore-id: darwin-managed-replace-removal\nConstraint: preserve Linux and Windows paths\nScope-risk: medium\nReversibility: revertable\nTested: bun test packages/coding-agent/test/darwin-managed-replace.test.ts; bun --cwd=packages/coding-agent run lint; bun --cwd=packages/coding-agent run check:types
A completed managed transcript rewrite left old transcript payloads and receipt state in the session directory.\n\nThe authenticated native exchange now removes the verified predecessor before syncing the parent, while failed cleanup remains explicit and recoverable.

Lore-id: darwin-managed-replace-cleanup

Constraint: preserve exact-exchange admission and durable publication

Scope-risk: medium

Reversibility: revertable

Tested: bun run build:native; bun --cwd=packages/coding-agent run check; bun test packages/coding-agent/test/darwin-managed-replace.test.ts; bun test packages/natives/test/path-identity-posix.test.ts
The macOS-only replacement path was not checked by Linux CI, leaving its safety annotations and lint requirements incomplete.\n\nMake its descriptor ownership and syscall preconditions explicit so platform checks exercise the same quality bar.

Lore-id: darwin-managed-replace-lint

Scope-risk: low

Reversibility: revertable

Tested: bun run check:rs; bun run build:native; bun --cwd=packages/coding-agent run check; bun test packages/coding-agent/test/darwin-managed-replace.test.ts; bun test packages/natives/test/path-identity-posix.test.ts

@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: 74805be197

ℹ️ 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/pi-natives/src/path_identity.rs Outdated
);
},
};
if parent.st_dev as u64 != parent_dev || parent.st_ino as u64 != parent_ino {

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 Bind the exchange parent to the authorization handle

When the managed parent is renamed or replaced after openDarwinReplacementAuth succeeds, this reopens parent_path and compares it only with the caller-supplied identity, which the TypeScript caller captures after opening the authorization. The reopened directory can therefore be a different inode from handle.parent_fd, so the exchange proceeds in a directory where the admission lock is not held and can race another append, removal, or replacement. Compare the reopened parent directly with handle.parent_dev and handle.parent_ino before touching either filename.

Useful? React with 👍 / 👎.

Comment on lines +1876 to +1879
const auth = openManagedDarwinReplacementAuth(root, parent);
if (!auth) throw new Error("managed_replace_exact_unavailable");
const operationId = randomUUID();
const parentIdentity = fs.lstatSync(parent, { bigint: true });

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 the admission handle closed on setup failures

On Darwin, if the parent disappears or becomes unreadable immediately after the authorization is acquired, this lstatSync throws before execution enters the try/finally beginning below. The native object then retains its exclusive flock until nondeterministic garbage collection or process teardown, causing subsequent append, removal, and replacement attempts in that directory to fail with migration_busy. Begin the try immediately after acquiring auth so every setup failure closes it deterministically.

Useful? React with 👍 / 👎.

Comment on lines +5376 to +5382
renameatx_np(
parent_fd,
source_name.as_ptr(),
parent_fd,
destination_name.as_ptr(),
RENAME_SWAP | RENAME_NOFOLLOW_ANY,
)

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 Prevent filename substitution before the exchange

When another same-user process ignores or does not yet implement the new advisory lock, it can replace either regular-file name after darwin_named_identity validates it and before this pathname-based syscall runs. renameatx_np is not tied to the descriptors or identities that were checked, so it can exchange an unvalidated successor; the post-check then reports committed_unproven, but the destination has already been mutated and may contain the substituted file. The exchange must make the preflight identities authoritative at the mutation boundary rather than relying solely on the cooperative flock.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

CLOSE_NOT_ADMITTED

Author gate / ledger

PASS. snowykr is a repository CONTRIBUTOR with multiple merged changes, including #3694, #3687, #3686, and #3684. This decision is based on contract and scope, not contributor standing.

Exact branch state

  • reviewed head: 2e338443763171066b456a2bf999fecbe672983e
  • PR target tip: 5c9e7cb9710f97e6b3714c63a50edebc704829f3
  • actual merge base: 8e23762f676e081d4d1b6377034daeaf9077bfdc
  • diff: 9 commits, 8 files, +1193/-93
  • state: DIRTY / conflicting, with 6 exact conflict files, including both protocol owners: crates/pi-natives/src/path_identity.rs and packages/coding-agent/src/session/internal/managed-session-storage.ts
  • CI: no check runs attached to this head

Contract decision

Current dev already owns Darwin replacement through the #3760 exact-replacement path plus the subsequent predecessor-authority hardening series. Its contract deliberately:

  1. publishes and fsyncs a versioned cleanup receipt before exchange;
  2. retains explicit predecessor/successor/unknown paths when an outcome cannot be proven;
  3. securely scrubs the authenticated predecessor through exactUnlink;
  4. reconciles cleanup receipts on later managed mutations; and
  5. fails closed across exchange, cleanup, and final durability boundaries.

This PR introduces a parallel Darwin-only exchange API and removes that durable recovery evidence. In particular, it performs renameatx_np, then direct unlinkat of the predecessor, then parent fsync, with no persisted operation receipt. A process death after exchange but before unlink/fsync leaves a committed namespace transition without the recovery authority current dev requires. An unlink or fsync failure is reported as committed_unproven, but there is no durable receipt binding the retained predecessor to the operation for restart reconciliation.

Additional admission blockers:

  • The PR has no linked defect report or deterministic crash-window reproducer that justifies replacing the established protocol.
  • The added Darwin test returns early on non-Darwin hosts and proves only the successful path; it does not cover death after exchange, unlink failure, fsync failure, substituted names, or restart reconciliation.
  • The changelog says successful exchanges retain predecessor recovery evidence while the stated and implemented goal is to remove predecessor retention.
  • Resolving the six conflicts would require choosing this parallel protocol over already-landed owner work, not a bounded reconciliation.

Existing owner boundary

No new mutation lane is admitted. The existing session/Darwin batch remains the sole owner of replacement-predecessor cleanup semantics. A future proposal must begin with a concrete issue demonstrating a current post-#3760 defect, preserve or formally replace every crash-recovery invariant above, and receive architecture approval before native/TypeScript implementation.

No source was mutated, no branch was pushed, no workflow was rerun or cancelled, and no merge action was taken.

Signed: GJC — existing session/Darwin batch triage owner, 2026-08-03

@Yeachan-Heo Yeachan-Heo closed this Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants