fix: address Copilot review round 15 on #2251

Round 15 generated no new inline comments; all five suppressed findings
were real and unaddressed.

- SkillsScreen: claim the attempt BEFORE issuing the request, for both the
  SKILL.md preview and `skills/get`. Recording it only on settle left a
  window where an older request that happened to resolve first was still
  considered current and published while a newer one was in flight —
  `verifyRow` already claimed its row up front, these two did not. Tests
  answer the OLDER request first and assert nothing is published.
- SkillsScreen: "No skills listed", not "No skills". SEP-2640 lets a server
  return an empty or partial catalog and says an empty result is not proof
  it has none — an unlisted skill is still fetchable by URI — so the old
  copy had the tool asserting something the protocol explicitly does not.
- SkillsScreen: each row's Verify button gets an `aria-label` carrying its
  URI. Every row's visible text is "Verify" and the URI cell is not
  programmatically associated with the button, so a screen-reader user had
  no way to tell which file each control checked.
- inspectorClient: attribute a rejected `skills/get` envelope with
  `markResponseRejected`, so the Protocol tab stops rendering it as a clean
  success while the screen shows an error. In the client rather than a
  store because `skills/get` has none. Gated on `isClientDecodeRejection`,
  with a test that a transport failure is NOT attributed.

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 02:21:41 -04:00
commit a561cd1daa

View file

@ -203,6 +203,7 @@ import {
LIST_MAX_PAGES,
ModernResultEnvelopeSchema,
isSalvageableRejection,
isClientDecodeRejection,
listPaginationExceeded,
toolItemSchemaForEra,
nextCursorOf,
@ -5603,15 +5604,33 @@ export class InspectorClient extends InspectorClientEventTarget {
};
// `GetSkillResultSchema` unwraps the envelope, so there is nothing to
// unwrap here.
return this.invokeMcpClient(
() =>
this.client!.request(
{ method: SKILLS_GET_METHOD, params },
GetSkillResultSchema,
this.getRequestOptions(this.progressTokenOf(metadata)),
),
{ method: SKILLS_GET_METHOD },
);
try {
return await this.invokeMcpClient(
() =>
this.client!.request(
{ method: SKILLS_GET_METHOD, params },
GetSkillResultSchema,
this.getRequestOptions(this.progressTokenOf(metadata)),
),
{ method: SKILLS_GET_METHOD },
);
} catch (err) {
// Attribute a rejected envelope to the exchange it came from, so the
// Protocol tab stops rendering it as a clean success while the Skills
// screen shows an error — the same handling every managed list gets
// (#1953). Done here rather than in a store because `skills/get` has
// none: the screen calls it directly. Must happen in this catch, while
// the correlation window is still current, and ONLY for a decode
// rejection — a request that never produced a response would otherwise
// stamp an earlier, successful exchange.
if (isClientDecodeRejection(err)) {
this.markResponseRejected(
SKILLS_GET_METHOD,
err instanceof Error ? err.message : String(err),
);
}
throw err;
}
}
/**