Skip to content

Fix hosted cargo pins for multi-line inline tables (#757) - #1735

Merged
Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-cargo-multiline-inline-table
Oct 27, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-cargo-multiline-inline-table

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Refs #757

Root cause

Hosted cargo plans Cargo.toml pins with a line scanner that treated every inline table as one line (plan_cargo_toml in crates/socket-patch-core/src/patch/redirect/mod.rs). It refused a valid TOML 1.0 declaration whose array value wraps, which is rustfmt's style for a long feature list:

serde = { version = "1.0.0", features = [
    "derive",
] }

scan --mode hosted warned redirect_cargo_toml_dep_unrewritable and left the crate unpatched, although the same project vendors fine. The pin readers (CargoRegistryPins and its test oracle cargo_socket_registry_pin) and upstream restore's unpin_line made the same one-line assumption.

Changes

  • cargo_inline_table: one brace-, bracket-, string- and comment-aware reader for an inline table. It returns the closing line and the text between the braces, borrowed when the table closes on its own line. It allows a line break only inside an array or a multi-line string, as TOML 1.0 does, so {⏎ version = … } is still refused. Comment text is left out, so a key named only in a comment is not read as one.
  • Planner: every inline table goes through that reader. A wrapped table is classified on its whole text with the existing rules and pinned on its opening line ( registry = "<reg>", straight after {). An earlier Socket pin in that spelling is superseded in place. An explicit registry = "crates-io" (Hosted cargo redirect refuses a crates.io dependency declared with an explicit registry = "crates-io", treating crates.io as "another registry" #386) anywhere in the wrapped table is dropped and the pin written in that spelling, so remove leaves the declaration without the redundant key, as it does for one-line tables. Any other registry already named in a wrapped table is still refused, because restore could not unpin it. Lines inside a wrapped table are skipped, so they are never read as entries or headers.
  • CargoRegistryPins and the oracle read pins out of the same span.
  • Upstream restore drops exactly the inserted text from the opening line, so rollback / remove leave the manifest byte-identical.

Not in this PR (why Refs, not Fixes)

#757's acceptance table also lists serde.version = "1.0.0" (dotted keys) and serde = '1.0.0' (literal string). Both stay refused. Supporting them, and deleting the regex scanner, is the larger toml_edit planner rewrite the issue proposes. I'm leaving the issue open for that.

Tests (red → green)

Before the fix, run against origin/main's core:

  • patch::redirect::tests::cargo_multiline_inline_table_is_pinned_on_its_opening_line (LF and CRLF; comments in the array; a rename): red, refused.
  • patch::redirect::tests::cargo_multiline_inline_table_is_classified_by_its_whole_text (path key after the array; renamed-away table; keys and braces only in comments; "a}" and """q"""" strings; already pinned): red.
  • patch::redirect::tests::cargo_socket_registry_pin_reads_back_every_written_shape with two new multi-line shapes: red.
  • patch::redirect::tests::cargo_supersede_replaces_previous_multiline_pin: re-pin to a newer patch.
  • upstream::cargo::tests::multiline_inline_table_opening_line_loses_only_the_pin and restore_unpins_a_multiline_inline_table_byte_exact (LF, CRLF, BOM, CRLF+BOM): red, restore refused.
  • Real cargo, e2e_redirect_cargo_shapes::cargo_hosted_multiline_inline_table_is_pinned (LF and CRLF; cargo 1.93.1): red on main (skipped: no lockfile entry pinning it could be rewritten). Green with the fix: scan, fresh-checkout cargo fetch --locked + offline cargo build --locked links the patched symbol, vex attests, remove is byte-identical.
  • I also checked by hand that cargo 1.97.0 metadata reads a pinned wrapped declaration (with comments and default-features = false) as serde from the Socket registry, with its features kept.

mode_migration_cargo::takeover_over_unrewritable_spelling_keeps_vendored listed the wrapped table as a spelling hosted mode refuses. Now that it's pinned, I removed that row. The dotted and literal rows stay.

Per issue:

Local gates

  • cargo fmt --all -- --check: clean.
  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features --lib: 6795 passed, 4 failed. All 4 are chmod/unremovable-file tests that can't fail as root, which this container runs as: copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_… and pypi_requirements::wire_failure_…. Fix berry unwind under a user yarn patch (#962) #1733 lists the same tests. None of them touch this diff.
  • CLI suites, with SOCKET_PATCH_CARGO_E2E_REQUIRED=1: e2e_redirect_cargo_shapes 34/34, e2e_redirect_cargo_build 23/23, e2e_vendor_cargo_build 40/40, mode_migration_cargo 30/30, in_process_cargo_apply 23/23, e2e_cargo 25/25.
  • I didn't run the full cargo test --workspace: building every CLI test binary ran this container out of disk. CI runs the rest.
  • No wrapper changes (npm/, pypi/, gem/ only dispatch the binary).

/code-review high

Fixed in 20f1ef5:

  • comment text leaking into classification;
  • the find('}') dispatch, which a } in a comment or string broke, in planner, readers and restore;
  • no supersede for an earlier multi-line pin;
  • multi-line strings that end in extra quotes;
  • the dispatch block repeated three times, now one helper;
  • a new allocation per single-line table, now Cow.

Nothing was left unfixed.

CI

All checks green on 319351f (37 success, the rest skipped). Cursor Bugbot found no new issues on 319351f.

Bugbot

  • 20f1ef5: wrapped tables naming crates-io were refused. Fixed in bde22cd, with unit tests for three placements and a crates-io-wrapped real-cargo e2e row. Thread resolved.
  • bde22cd: the wrapped crates-io matcher missed 'crates-io' and "registry" spellings. Fixed in 319351f, with tests. Thread resolved.

https://claude.ai/code/session_011f9aeSkjzuMR7wJ9h5JWZd


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Hosted cargo scan refused a Cargo.toml dependency written as an inline
table whose array wraps onto later lines, the usual rustfmt style for
a long feature list:

    serde = { version = "1.0.0", features = [
        "derive",
    ] }

It warned redirect_cargo_toml_dep_unrewritable and left the crate
unpatched. The planner, the pin readers and upstream restore all
assumed an inline table closes on its own line.

A shared reader now follows such a table to its closing brace. It only
lets a line break happen inside an array or a multi-line string, as
TOML 1.0 does, so a break straight after "{" is still refused. The
planner classifies the whole table text and pins it on the opening
line, right after "{". The pin readers read that pin back, and restore
removes exactly the inserted text, so rollback leaves the manifest
byte-identical for LF, CRLF and BOM files.

Refs #757

Assisted-by: Claude Code:claude-opus-5-5
Follow-ups from review of the multi-line inline table support:

- Keys or braces that appear only in a comment inside a wrapped
  features array no longer get the dependency refused as a path,
  workspace or other-registry entry, or pinned inside the comment.
- Every inline table, one-line or wrapped, is now read by the same
  brace- and string-aware scanner. A "}" inside a string or comment no
  longer ends it, and a multi-line string may end in extra quotes.
- A rescan that picks a newer patch moves an earlier pin on a wrapped
  table to the new registry, the same as it does for every other
  spelling.
- Restore uses the same scanner to tell a wrapped table's opening line
  from a one-line table.
- A real-cargo e2e shape covers the wrapped spelling for LF and CRLF:
  scan, offline locked build, vex, and a byte-exact remove. The
  migration test no longer lists it as a spelling hosted mode refuses.

Refs #757

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 27, 2026 00:58
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/patch/redirect/mod.rs
A wrapped inline table with an explicit registry = "crates-io" was
still refused, while #386 pins the one-line form. Hosted scan now
drops that redundant key, with one adjacent comma, and pins the table
on its opening line in the spelling restore unpins. remove then leaves
the declaration without the key, which cargo resolves the same way, as
it does for the one-line form. Unit tests cover the key after "{", in
the middle of the opening line and on the closing line. A real-cargo
e2e row covers the closing-line spelling.

Refs #757

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/patch/redirect/mod.rs
The crates-io repin for a wrapped inline table found only the bare key
with a double-quoted value. registry = 'crates-io' and
"registry" = "crates-io" mean the same thing, and the one-line form
already accepts them, but in a wrapped table they still got the
dependency refused. The matcher now takes the same key and value
spellings as the one-line rewrite.

Refs #757

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 319351f. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 27, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at head 319351f.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at head 319351f.

  • CI: 56/56 check runs on 319351f passed or skipped (37 success, 19 skipped); mergeable with main, no conflicts. No main-wide failures.
  • Cursor Bugbot: reviewed 319351f, no new issues. Its two earlier findings (wrapped tables naming crates-io; 'crates-io'/"registry" spellings) were fixed in bde22cd and 319351f; both threads resolved.
  • CHANGELOG.md untouched.
  • Reviewer note: this is Refs #757, not Fixes. Dotted-key and literal-string spellings stay refused and are left for the toml_edit planner rewrite.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief (head 319351f)

What it does. Hosted cargo mode refused a dependency written as an inline table whose features array wraps lines (rustfmt's style), so scan --mode hosted left the crate unpatched (#757). This adds one string-, comment- and bracket-aware reader for inline tables. The planner, the pin readers and upstream restore all use it. A wrapped table is pinned right after its opening {, and restore drops exactly that text. Dotted-key and literal-string spellings are still refused (left for the toml_edit rewrite, hence Refs).

Risk: low. The change is limited to cargo manifest spellings that used to be refused. One-line tables take the same path as before, apart from the new close detection, which now ignores } inside strings and comments. Restore is byte-exact for LF, CRLF and BOM, and that is tested.

Look here

Verified

  • I read the full diff. I traced comma handling for the crates-io key at the start, middle and end of the table, CRLF \r carry-through, lines inside a wrapped table being skipped (they used to be scanned as entries), and supersede of an earlier pin.
  • The tests are red→green per the description. The real-cargo e2e covers LF and CRLF.
  • Locally on 319351f: cargo clippy --locked -p socket-patch-core --all-features -- -D warnings is clean. cargo test -p socket-patch-core --lib cargo passed 472/472. cargo fmt --check is clean.
  • CI: 37 success, 19 skipped, 0 failed. ci-ok and clippy are green. Bugbot found no issues on this head. All review threads are resolved. CHANGELOG.md is untouched.

Changes I made: none.

Open questions (non-blocking): in a wrapped table that has registry = "crates-io", a # inside a string on the same line as the key (say features = ["a#b"], registry = "crates-io" }) makes the crates-io drop give up. The crate is then refused with a warning, the same outcome as before this PR. This is conservative, and I didn't change it.

Auto-merge is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

Merged via the queue into main with commit d185db4 Oct 27, 2026
56 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-cargo-multiline-inline-table branch October 27, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants