Skip to content

fix: replace the pointer on override instead of writing through it - #52

Open
blindchaser wants to merge 1 commit into
mainfrom
fix/pointer-override-aliasing
Open

fix: replace the pointer on override instead of writing through it#52
blindchaser wants to merge 1 commit into
mainfrom
fix/pointer-override-aliasing

Conversation

@blindchaser

Copy link
Copy Markdown
Contributor

Fixes both review findings on #51 — Codex's aliasing report and Bugbot's failed-set report. Worth landing before the rollout, because the second one silently produces the exact state the new field exists to make deliberate.

Both come from the same mistake: resolving a pointer field by reusing whatever pointee was already there.

Aliasing

ResolveIncrementalIntent shallow-copies the caller's config:

copied := *current
cfg := &copied

Every pointer field is shared between the two. Overriding a non-nil pointer field wrote through the shared pointee, so current changed too — and current is what later diff, retry, and rollback logic reads as the pre-override state.

Failed set leaves a pin

Allocating before parsing meant a rejected value stayed behind as a pointer to the zero value. For storage.state_commit.write_mode_enable_auto that zero is false, which renders sc-write-mode-enable-auto = false — a pin. ResolveEnv only warns on a bad value and continues, so a typo in an env var could take a node off a governance-driven migration with nothing in the logs to say so.

That is the failure mode the field was added to prevent being accidental. A reserve node is pinned on purpose; a node pinned by a typo diverges from the chain and nobody finds out until a digest disagrees.

The fix

Parse into a fresh pointee, replace the pointer only once parsing succeeds. Nothing else aliases the write, and a failure leaves the field exactly as it was.

Tests

TestApplyOverrides_PointerDoesNotAliasCaller drives the real ResolveIncrementalIntent path and asserts the caller's config is untouched. TestApplyOverrides_PointerRejectedValueLeavesFieldUnset asserts a rejected value leaves the field nil.

Both fail against the previous resolve.go:

--- FAIL: TestApplyOverrides_PointerDoesNotAliasCaller
    the caller's config was mutated: got 0x1400009ac18, want unchanged pointer to true
--- FAIL: TestApplyOverrides_PointerRejectedValueLeavesFieldUnset
    rejected value left the field set to false; a pin must never come from a parse failure

The rest of the suite passes unchanged.

Made with Cursor

Both defects come from resolving a pointer field by reusing whatever pointee
was already there.

ResolveIncrementalIntent shallow-copies the caller's config, so the copy and
the original share every pointer. Overriding a non-nil pointer field wrote
through the shared pointee and silently changed the caller's config, which
later diff, retry, and rollback logic reads as the pre-override state.

Allocating before parsing also left a rejected value behind as a pointer to
the zero value. For storage.state_commit.write_mode_enable_auto that zero is
false, which renders sc-write-mode-enable-auto = false — a pin. ResolveEnv
only warns on a bad value and keeps going, so a typo in an env var could take
a node off a governance-driven migration with no error surfaced.

The value now parses into a fresh pointee and the pointer is replaced only on
success, so nothing else aliases the write and a failure leaves the field as
it was.

Reported by Codex and Bugbot on #51. Both regression tests fail against the
previous resolve.go.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 28, 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-08-28T21:19:35.655120Z b85e8b3 PR opened
ℹ️ 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.

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.

1 participant