fix: address Copilot review round 12 on #2251

All three findings were the same underlying inconsistency: URI comparisons
on raw strings, in a flow that elsewhere deliberately treats normalized
equivalents as the same resource. One `skillUriIdentity` helper now backs
every comparison, so they cannot disagree.

- skills.ts: `manifest-missing-self` compares identities, so a manifest
  listing the RFC-equivalent `skill://demo/%53KILL.md` is recognized as the
  entry's own file — it is fetchable as that file, so reporting it missing
  was the tool disagreeing with itself.
- skills.ts: `skillEntriesMatch` normalizes the entry URI and every manifest
  URI before comparing, so a server that canonicalizes an escape between the
  listing and the fetch is not reported as a changed snapshot.
- SkillsScreen: the `wrongUri` check uses the same identity, so a
  canonicalizing server is not accused of answering for a different skill —
  the read path already accepts exactly that equivalence.
- skills.ts: apply the Agent Skills name grammar to the RAW value. Trimming
  first let `" demo "` pass, and whitespace is not in the grammar — so an
  entry could report "Conforms" with a name that can never equal its URI
  path segment. The trimmed copy now only distinguishes absent from invalid.
- skills.ts: add `malformed-description` for a description above the
  1024-character Agent Skills limit, with boundary coverage.
- inspectorClient-skills.test: justify the `as unknown as` per AGENTS.md —
  both fields are `private` with no public setter, the asserted shape is
  exactly what the class declares, and the alternative is a live connection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018kfrH3uRNUM4JAk1reKtyY
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-05 00:46:18 -04:00
commit bd28a36f23

View file

@ -24,6 +24,7 @@ import {
checkSkillConformance,
skillDisplayName,
skillEntriesMatch,
skillUriIdentity,
totalSkillBytes,
verifySkillResource,
type SkillIssue,
@ -544,7 +545,12 @@ export function SkillsScreen({
writeFetched({
entry,
issues: checkSkillConformance(entry),
wrongUri: entry.uri !== selected.uri,
// Compared by identity, not raw string: a server that canonicalizes
// an escape has answered for the same resource, and calling that
// "a different URI" would be the tool disagreeing with the read
// path, which accepts exactly that equivalence.
wrongUri:
skillUriIdentity(entry.uri) !== skillUriIdentity(selected.uri),
// Compared semantically — see `skillEntriesMatch` for why a
// `JSON.stringify` comparison would report key order and manifest
// order as differences.