Skip to content

fix(skills): stop quarantined skill trust from escalating on relocation - #6712

Merged
bug-ops merged 1 commit into
mainfrom
fix/6707-quarantine-trust-gap
Jul 28, 2026
Merged

fix(skills): stop quarantined skill trust from escalating on relocation#6712
bug-ops merged 1 commit into
mainfrom
fix/6707-quarantine-trust-gap

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • update_trust_for_reloaded_skills's source_kind-mismatch branch previously adopted the new source kind's default trust level whenever the stored level was "active" (not Blocked). Since Quarantined is active and [skills.trust] local_level defaults to Trusted, relocating a Quarantined skill's unchanged-content directory out of managed_dir (Hub -> Local reclassification) silently promoted it to Trusted on the next reload, with no review step. The branch now takes SkillTrustLevel::min_trust of the stored level and the new source kind's default, so a source_kind change can never move a skill's effective trust higher than it already had.
  • The adjacent hash-mismatch branch had the identical defect one line above: any hash mismatch unconditionally assigned hash_mismatch_level (default Quarantined), even when the existing row was an explicit operator Blocked — silently un-blocking it on a one-byte content edit. Also clamped through min_trust.
  • skill_catalog_items (feeds the TUI mention picker) filtered out only Blocked skills, so a Quarantined skill still appeared there as a normal, invocable name, indistinguishable from a reviewed skill. New SkillTrustLevel::is_hidden_from_catalog closes this — SkillCatalogItem carries no trust field, so hiding is the only option on this surface.

Note on scope vs #6710

This branch's base went stale mid-session: PR #6710 (closing #6701) merged concurrently and made the canonical design decision for the other two catalog sites originally in scope for #6706 (reload_skills's system-prompt catalog and apply_skill_trust_and_gating's per-turn catalog) — it takes an annotate, not hide, strategy there: Quarantined/Blocked skills stay visible with a trust="..." XML attribute (format_skills_catalog's trust_levels parameter) so the model/operator can still name and promote them, while #6710's own activation filter already excludes them from active_skill_names/matching. This PR was rebased onto that decision and does not alter it — the mention-picker exclusion above is the only remaining hide-site, and closes the gap #6710 didn't touch.

Test plan

  • cargo +nightly fmt --check
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings
  • cargo nextest run --config-file .github/nextest.toml -p zeph-core -p zeph-common --lib --bins (also verified full workspace suite green during review, 2553 passed / 0 failed)
  • cargo test --doc -p zeph-common (81 passed, incl. new is_hidden_from_catalog doctest)
  • RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --all-features -p zeph-common
  • gitleaks protect --staged
  • 7 new/updated regression tests, including one (update_trust_hash_mismatch_never_unblocks_explicit_block) manually verified load-bearing by reverting the fix and confirming the test fails, then restoring it

Closes #6707
Closes #6706

update_trust_for_reloaded_skills's source_kind-mismatch branch adopted the
new source kind's default trust level whenever the stored level was
"active" (not Blocked). Since Quarantined is active and
[skills.trust] local_level defaults to Trusted, relocating a Quarantined
skill's unchanged-content directory out of managed_dir (Hub -> Local)
silently promoted it to Trusted on the next reload, with no review step.
The branch now takes SkillTrustLevel::min_trust of the stored level and the
new source kind's default, so a source_kind change can never move a
skill's effective trust higher than it already had. Symmetrically, moving
a Trusted skill into a source kind with a stricter default now correctly
demotes it too - an intentional fail-closed side effect of the same fix.

The adjacent hash-mismatch branch had the identical defect one line above:
a hash mismatch unconditionally assigned hash_mismatch_level (default
Quarantined) even when the existing row was an explicit operator Blocked,
silently un-blocking it on a one-byte edit. Also clamped through
min_trust.

skill_catalog_items (feeds the TUI mention picker) filtered out only
Blocked skills, so a Quarantined skill still appeared there as a normal,
invocable name. New SkillTrustLevel::is_hidden_from_catalog closes this -
SkillCatalogItem carries no trust field, so hiding is the only option on
this surface. The other two catalog sites originally in scope for this
fix turned out to already be correctly handled by the concurrently-merged
#6710/#6701 fix, which takes an annotate-not-hide strategy instead
(format_skills_catalog's trust_levels parameter renders a trust="..."
attribute, and #6710's own activation filter already excludes
Quarantined/Blocked from matching); this change does not alter that
design.

Closes #6707
Closes #6706
@github-actions github-actions Bot added bug Something isn't working size/L Large PR (201-500 lines) documentation Improvements or additions to documentation rust Rust code changes core zeph-core crate labels Jul 28, 2026
@bug-ops
bug-ops enabled auto-merge (squash) July 28, 2026 20:04
@bug-ops
bug-ops merged commit b9c8be1 into main Jul 28, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/6707-quarantine-trust-gap branch July 28, 2026 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working core zeph-core crate documentation Improvements or additions to documentation rust Rust code changes size/L Large PR (201-500 lines)

Projects

None yet

1 participant