Skip to content

feat(skills): report hash drift in skill info - #795

Merged
kevincodex1 merged 2 commits into
Gitlawb:mainfrom
Ayush7614:feat/skills-info-hash-drift
Jul 27, 2026
Merged

feat(skills): report hash drift in skill info#795
kevincodex1 merged 2 commits into
Gitlawb:mainfrom
Ayush7614:feat/skills-info-hash-drift

Conversation

@Ayush7614

@Ayush7614 Ayush7614 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Detect when an installed skill's SKILL.md no longer matches skills.lock
  • Surface hashDrift in zero skill info text + JSON (same idea as plugins info)
  • Fix EXTENDING.md docs that claimed a nonexistent skills lock command

Closes #792

Test plan

  • go test ./internal/skills/ ./internal/cli/ -run 'Info|Skill|HashDrift|RunSkill' -count=1
  • go vet ./internal/skills/ ./internal/cli/

Summary by CodeRabbit

  • New Features

    • Added “hash drift” reporting to skill info (text and JSON), indicating whether an installed SKILL.md matches its recorded lock hash.
  • Bug Fixes

    • Skill management commands now write only to the primary skills directory for add/remove operations.
    • Improved detection/reporting when the installed skill content changes or when the locked skill file can’t be read.
  • Documentation

    • Updated the “Skills” documentation to clarify primary vs shared skills roots and how missing directories are handled.

Mirror plugin info: compare on-disk SKILL.md to the skills.lock hash so
edited installs are visible. Also drop the nonexistent skills lock
command from EXTENDING.md.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 18a9c513-2650-49e8-a638-0349e1a584bd

📥 Commits

Reviewing files that changed from the base of the PR and between 2531c39 and 1ca2352.

📒 Files selected for processing (5)
  • docs/EXTENDING.md
  • internal/cli/distribution_test.go
  • internal/skills/export_test.go
  • internal/skills/install.go
  • internal/skills/install_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/EXTENDING.md
  • internal/cli/distribution_test.go

Walkthrough

Adds HashDrift detection for installed skills by comparing SKILL.md with the lockfile hash, exposes the result in skill info, tests clean and modified states, and updates skills-directory documentation.

Changes

Skill hash drift reporting

Layer / File(s) Summary
Skill metadata drift detection
internal/skills/install.go, internal/skills/export_test.go, internal/skills/install_test.go
SkillInfo now includes HashDrift; information paths compare installed SKILL.md content with the recorded lock hash, including unreadable-file and missing-hash cases.
CLI output and documentation
internal/cli/distribution.go, internal/cli/distribution_test.go, docs/EXTENDING.md
skill info reports hash drift in text and JSON output, tests verify clean and modified states, and skills-directory behavior documentation is updated.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SkillCLI
  participant SkillInfo
  participant SkillFile
  User->>SkillCLI: run skill info
  SkillCLI->>SkillInfo: request skill metadata
  SkillInfo->>SkillFile: read SKILL.md and recorded hash
  SkillInfo-->>SkillCLI: return HashDrift
  SkillCLI-->>User: print or serialize drift status
Loading

Possibly related PRs

Suggested reviewers: kevincodex1, vasanthdev2004

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: reporting hash drift in skill info.
Linked Issues check ✅ Passed The PR computes hash drift from the lock hash, exposes it in text/JSON, and updates EXTENDING.md as requested.
Out of Scope Changes check ✅ Passed The changes stay within the requested skills info, output, docs, and test coverage scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
internal/cli/distribution_test.go (1)

86-101: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add JSON regression assertions.

This test only exercises human-readable output, while the PR also promises hashDrift in JSON. Check both false before editing and true after editing; otherwise the omitempty regression can pass unnoticed.

As per coding guidelines, add regression tests for behavior changes and keep them beside the source.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/distribution_test.go` around lines 86 - 101, Extend the
distribution regression test around runWithDeps and the skill info command to
request JSON output and assert hashDrift is false before editing the skill, then
true after editing it. Parse the JSON response and verify the field is present
with the correct boolean value, while preserving the existing human-readable
assertions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/EXTENDING.md`:
- Line 111: Update the management-command examples in the documentation to use
the actual singular `zero skill add` and `zero skill remove` names, while
preserving the existing descriptions of their directory behavior.

In `@internal/skills/install.go`:
- Around line 504-518: Update skillHashDrift and its callers in
Info/InfoFromRoots so a locked skill whose os.ReadFile(skill.Path) fails is
surfaced as an error or marked as HashDrift=true, rather than returning a clean
false state; preserve the existing behavior for empty lock hashes and
successfully read files.
- Line 82: Update the HashDrift field’s JSON serialization so a computed
unchanged result is emitted as hashDrift: false instead of being omitted; remove
omitempty from its json tag, or use an equivalent pointer/custom-marshaler
representation if the no-lock “not computed” state must remain distinguishable.

---

Nitpick comments:
In `@internal/cli/distribution_test.go`:
- Around line 86-101: Extend the distribution regression test around runWithDeps
and the skill info command to request JSON output and assert hashDrift is false
before editing the skill, then true after editing it. Parse the JSON response
and verify the field is present with the correct boolean value, while preserving
the existing human-readable assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 734c8dff-6e45-46b1-bcbe-cc08571f7df2

📥 Commits

Reviewing files that changed from the base of the PR and between d9b882e and 2531c39.

📒 Files selected for processing (5)
  • docs/EXTENDING.md
  • internal/cli/distribution.go
  • internal/cli/distribution_test.go
  • internal/skills/install.go
  • internal/skills/install_test.go

Comment thread docs/EXTENDING.md Outdated
Comment thread internal/skills/install.go Outdated
Comment thread internal/skills/install.go
Merge main (Info moved to export_test). Treat unreadable locked SKILL.md
as drift, always emit hashDrift in JSON, use singular skill CLI names in
docs, and cover JSON output in CLI tests.
@Ayush7614

Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit review and the main conflict in 1ca2352:

  • Merged latest main — production skills.Info stays removed (chore: remove code unreachable on every supported platform #706); test seam in export_test.go now also reports hash drift
  • Unreadable locked SKILL.md now counts as hashDrift: true (not a clean false)
  • JSON always emits hashDrift (dropped omitempty)
  • Docs use singular zero skill add / zero skill remove
  • CLI test asserts JSON hashDrift false→true around an edit

@Ayush7614

Copy link
Copy Markdown
Contributor Author

cc: @kevincodex1

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Both of the earlier findings are genuinely fixed at this head, and I checked the part nobody had: whether the hash comparison itself can report false drift.

The unreadable-SKILL.md fail-open is properly closed (it now returns true with the intent stated in the comment, and the test pins both branches), and the omitempty tag is gone. On the hash side I installed skills whose SKILL.md was all-CRLF, had no trailing newline, and was mixed CRLF/LF: all three installed byte-identical and reported no drift, so the line-ending false-positive risk I was worried about does not exist. Neither side normalises, which is the right choice here. Name-collision and agents-only paths correctly report no hash rather than false drift.

One correction worth recording so it does not get re-raised: the claim that omitempty made a clean result serialise as absent was never user-visible. The only production JSON path goes through redaction.RedactValue, whose struct walk reads just the field name and never consults omitempty. Removing the tag is still the right defensive change, but the reported impact did not exist, and the new JSON assertion cannot catch a regression that re-adds it.

One minor for a follow-up, not blocking and not introduced here: InfoFromRoots swallows a ReadLock error behind if lock, err := ReadLock(...); err == nil, so a corrupt or truncated skills.lock leaves Hash empty and skillHashDrift short-circuits to false. A tampered SKILL.md plus a truncated lockfile then prints no hash, no drift, and exits 0. That is the same fail-open class this PR just fixed for an unreadable SKILL.md, and someone who can tamper with the skills directory can also truncate the lock, which is exactly the threat drift detection is for. It predates the PR, but this change is what makes it load-bearing.

LGTM.

@kevincodex1
kevincodex1 merged commit 9fbdc34 into Gitlawb:main Jul 27, 2026
7 checks passed
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.

feat(skills): report hash drift in skill info

3 participants