Skip to content

Fix: atomic plugins.toml writes β€” registry race made plugin subcommands vanish#419

Merged
0xLeif merged 3 commits into
mainfrom
fix/registry-atomic-write
Jun 12, 2026
Merged

Fix: atomic plugins.toml writes β€” registry race made plugin subcommands vanish#419
0xLeif merged 3 commits into
mainfrom
fix/registry-atomic-write

Conversation

@0xLeif

@0xLeif 0xLeif commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • save_registry wrote plugins.toml with fs::write (truncate-in-place). A concurrent fledge invocation reading the registry mid-rewrite parsed partial TOML, and because dispatch loads the registry with .ok()?, every registered plugin command silently resolved to unrecognized subcommand.
  • Observed in the wild on merlin's CI: the trust-gate's fledge run augur-gate failed with unrecognized subcommand 'augur' three times in two days β€” every time a plugin registration (merlin init/update, fledge plugins install) overlapped the gate. The plugin was perfectly installed each time; the red builds were pure race.
  • Fix: write-then-rename in save_registry (same-directory rename is atomic on POSIX β€” readers see the old registry or the new one, never a torn one), plus a one-shot 50ms retry in load_registry for rewrites by older non-atomic writers, plus a stderr warning when the registry is persistently unreadable so the failure is diagnosable instead of masquerading as a missing plugin.

Test plan

  • New regression test: 400 reads against a continuously rewriting writer thread β€” zero torn reads (fails reliably against the old fs::write implementation)
  • New test: corrupt registry surfaces an error rather than parsing as empty
  • fledge lanes run ci β€” all 6 steps green

πŸ€– Generated with Claude Code

…ds vanish

save_registry used fs::write, which truncates in place. Any fledge
invocation reading the registry during a re-registration parsed
partial TOML, and dispatch silently resolved every registered plugin
command to 'unrecognized subcommand'. Observed in the wild: merlin's
CI trust-gate failing 'fledge run augur-gate' with 'unrecognized
subcommand augur' whenever a plugin registration (merlin init/update,
fledge plugins install) overlapped the gate β€” three times in two days,
each costing a misleading red build.

- save_registry: write-then-rename (same-dir rename is atomic on
  POSIX); readers now see either the old or the new registry, never a
  truncated one
- load_registry: one 50ms retry rides out rewrites by older
  (pre-atomic) writers, and persistent corruption prints a warning
  naming the path instead of silently resolving as an empty registry
- Tests: a writer-hammer concurrency regression (400 reads against a
  continuous rewriter) and a corruption-surfaces-error case

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@0xLeif 0xLeif requested a review from a team as a code owner June 12, 2026 14:43
@0xLeif 0xLeif requested review from 0xGaspar, Kyntrin and tofu-ux June 12, 2026 14:43

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces atomic registry saving using a write-then-rename pattern to prevent concurrent readers from observing a truncated plugins.toml file. It also adds a retry mechanism when loading the registry to handle potential concurrent writes from older versions, along with corresponding unit tests. The review feedback suggests cleaning up the temporary file if the write or rename operation fails to prevent leaking temporary files.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/plugin/mod.rs Outdated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@github-actions github-actions 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.

❌ Corvin says...

      _
    <(;\  .oO(oh no...)
     |/(\
      \(\\
      " "\\

"Even the dumpster of code seems empty today."

CI Summary

Check Status
Dependency Audit βœ… Passed
Integration (3 OS) ❌ failure
Lint (fmt + clippy) βœ… Passed
Spec Validation βœ… Passed
Tests (3 OS) βœ… Passed

Powered by corvid-pet

@github-actions github-actions Bot dismissed their stale review June 12, 2026 15:10

Superseded by updated review.

github-actions[bot]
github-actions Bot previously approved these changes Jun 12, 2026

@github-actions github-actions 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.

βœ… Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Looking sharp! Like a beak should be."

CI Summary

Check Status
Dependency Audit βœ… Passed
Integration (3 OS) βœ… Passed
Lint (fmt + clippy) βœ… Passed
Spec Validation βœ… Passed
Tests (3 OS) βœ… Passed

Powered by corvid-pet

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@github-actions github-actions 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.

βœ… Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Caw! Found a shiny new spec!"

CI Summary

Check Status
Dependency Audit βœ… Passed
Integration (3 OS) βœ… Passed
Lint (fmt + clippy) βœ… Passed
Spec Validation βœ… Passed
Tests (3 OS) βœ… Passed

Powered by corvid-pet

@0xLeif 0xLeif merged commit 12547a3 into main Jun 12, 2026
13 checks passed
@0xLeif 0xLeif deleted the fix/registry-atomic-write branch June 12, 2026 15:33
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