Skip to content

fix: code-review findings — registry index lock/temp collision, publish auth, CI#195

Merged
aroff merged 2 commits into
mainfrom
fix/code-review-findings
Jun 24, 2026
Merged

fix: code-review findings — registry index lock/temp collision, publish auth, CI#195
aroff merged 2 commits into
mainfrom
fix/code-review-findings

Conversation

@aroff

@aroff aroff commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Attacks the bugs surfaced by a max-effort code review of #194 (registry-publish feature gate). Five findings; the first two are data-integrity bugs in the registry index writer.

Findings & fixes

#1 — Sidecar lock collision (data integrity) · index_manager.rs

atomic_update derived the sidecar lock path with Path::with_extension("lock"), which replaces the last dotted segment. Skill index files are named by the bare package (no fixed extension) and package names may contain dots, so org/web.scraper and org/web.crawler both mapped onto org/web.lock — forcing unrelated publishes to share one lock (false contention, possible 30s lock-timeout failures).
Fix: append_suffix() derives the lock by appending, keeping it distinct per skill.

#2 — Temp-file clobber (data loss) · index_manager.rs

The temp path with_extension("tmp") could equal another skill's real index file: writing org/data used temp org/data.tmp, which is the literal index of skill org/data.tmp, so the atomic rename destroyed that skill's index.
Fix: unique_temp_path() tags the temp name with pid + a monotonic counter, so it can never collide with an index file.

Two regression tests added — both verified to fail on the pre-fix code and pass after.

#3 — Restore the no-token guard · cli/commands/publish.rs

#194 dropped the early validation, so a missing token sent an unauthenticated request and surfaced a raw 401. Restored the actionable error: Run \fastskill auth login``.

#4 — Lint the shipped config in CI · .github/workflows/test.yml

Clippy only ran --all-features. Added a default-feature clippy pass, since release binaries are built from the default (registry-publish OFF) config.

#5 — Stale URLs in orphaned tests · tests/

Updated /api/registry/.../api/v1/registry/... mocks/snapshot to match the #194 contract. (These files live in the virtual-workspace-root tests/ tree and are not compiled — wiring-in vs deleting left as a follow-up.)

Verification

  • cargo fmt, clippy (default and --all-features), and the full test suite (174 tests) pass.
  • The two new regression tests fail on old code, pass on the fix.

Deliberately not done (needs a decision)

  • Release binaries still ship without publishrelease.yml builds the default config and never passes -F registry-publish. Whether the distributed CLI should keep the publish command is a product call; left untouched.

🤖 Generated with Claude Code

aroff and others added 2 commits June 24, 2026 22:54
Attacks the bugs surfaced by the max-effort code review of PR #194.

Finding #1 & #2 (data integrity, index_manager.rs atomic_update):
  Sidecar lock and temp paths were derived with Path::with_extension, which
  REPLACES the last dotted segment. Skill index files are named by the bare
  package (no fixed extension) and package names may contain dots, so:
    - org/web.scraper and org/web.crawler both mapped to org/web.lock,
      forcing unrelated publishes to share one lock (false contention,
      possible 30s lock-timeout failures); and
    - org/data's temp path org/data.tmp was the literal index file of skill
      org/data.tmp, so the atomic rename destroyed that skill's index.
  Fix: append_suffix() derives the lock path by appending (distinct per
  skill), and unique_temp_path() tags the temp name with pid + a monotonic
  counter so it can never equal another skill's index. Two regression tests
  added (both verified to fail on the pre-fix code).

Finding #3 (cli/commands/publish.rs):
  Restore the no-token guard removed in #194 — without a token, publish now
  fails fast with the actionable "Run `fastskill auth login`" message instead
  of sending an unauthenticated request and surfacing a raw 401.

Finding #4 (.github/workflows/test.yml):
  Clippy only linted --all-features. Add a default-feature clippy pass, since
  release binaries are built from the default (registry-publish OFF) config.

Finding #5 (orphaned root tests/):
  Update stale /api/registry/... mocks/snapshots to the /api/v1/registry/...
  contract from #194. (These files live in the virtual-workspace-root tests/
  tree and are not compiled; wiring-in vs deleting is left as a follow-up.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the code-quality review of the first commit.

- Bug CLASS, not two leaves: the collision lived in the canonical
  `utils::atomic_write` too (`path.with_extension("tmp")`). Move the
  collision-safe `append_suffix` into utils, route `atomic_write` through it,
  and derive the index lock path via a new `get_skill_index_lock_path`
  co-located with `get_skill_index_path`. Now every write path is hardened in
  one place instead of patching index_manager alone.

- Kill duplication: `index_manager::atomic_update` re-implemented the
  write-tmp/fsync/rename tail with the ENOSPC "filesystem full" check
  copy-pasted three times. Serialize once into a buffer and classify ENOSPC
  once via `is_disk_full`. index_manager.rs shrinks 571 -> 523 lines.

- index_manager keeps a UNIQUE temp name (it holds its own sidecar lock across
  the read-modify-write, and a deterministic temp could equal another skill's
  index); atomic_write keeps a DETERMINISTIC temp (it doubles as the advisory
  lock token). Tests in utils/lock updated to the new tmp derivation.

- Drop the dead-test churn from the first commit (those root tests/ files are
  uncompiled); wiring-in vs deleting tracked separately.

All four index_manager regression tests still pass (and still fail on the
pre-fix code); utils/lock/registry_index tests green; clippy clean on both
default and --all-features.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aroff
aroff merged commit 8f5c7ba into main Jun 24, 2026
11 checks passed
@aroff
aroff deleted the fix/code-review-findings branch June 24, 2026 23:15
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