diff --git a/clients/tui/__tests__/SkillsTab.test.tsx b/clients/tui/__tests__/SkillsTab.test.tsx index 7d3d6408..e4b44f8c 100644 --- a/clients/tui/__tests__/SkillsTab.test.tsx +++ b/clients/tui/__tests__/SkillsTab.test.tsx @@ -141,6 +141,28 @@ describe("SkillsTab (#2248)", () => { expect(frame).toContain("! gen"); }); + it("does not claim the listing conforms while verification is failing", async () => { + // The two verdicts sat in one pane and contradicted each other: the static + // checks pass on `clean` (its advertised digest is well-formed), while the + // bytes do not hash to it. The heading now names what it actually covers. + const { lastFrame, stdin } = render( + , + ); + stdin.write(ENTER); + await tick(); + const frame = lastFrame() ?? ""; + expect(frame).toContain("Verification FAILED"); + expect(frame).toContain("Listing checks: no structural issues"); + expect(frame).not.toContain("conforms"); + }); + it("shows the selected skill's URI, description, findings and manifest", () => { const { lastFrame } = render( { const frame = lastFrame() ?? ""; expect(frame).toContain("skill://clean/SKILL.md"); expect(frame).toContain("A clean skill"); - expect(frame).toContain("Conformance: conforms"); + // Named for what it covers: the static checks against the listing. + expect(frame).toContain("Listing checks: no structural issues"); expect(frame).toContain("Manifest (1)"); expect(frame).toContain("SKILL.md"); expect(frame).toContain("(51 B)"); diff --git a/clients/tui/src/components/SkillsTab.tsx b/clients/tui/src/components/SkillsTab.tsx index 65857ba9..fb86c8eb 100644 --- a/clients/tui/src/components/SkillsTab.tsx +++ b/clients/tui/src/components/SkillsTab.tsx @@ -357,9 +357,17 @@ export function SkillsTab({ )} + {/* Named for the checks it actually covers. An unqualified + "conforms" sat directly above "Verification FAILED" in the + same pane and flatly contradicted it — these are the static + checks against the LISTING, and passing them says nothing + about the bytes the server serves (Copilot). Same wording + problem, and the same fix, as the web screen's "No structural + issues". */} - Conformance{issues.length === 0 ? ": conforms" : ":"} + Listing checks + {issues.length === 0 ? ": no structural issues" : ":"} {issues.map((issue, idx) => ( diff --git a/docs/test-servers.md b/docs/test-servers.md index 3fdca7d6..7acfe797 100644 --- a/docs/test-servers.md +++ b/docs/test-servers.md @@ -76,13 +76,23 @@ handler cannot reach it. To exercise the *undeclared* case, connect to any config **without** `"skills"`, where the Inspector must refuse to send the call locally rather than letting the server answer it. -Every result carries the modern base envelope (`resultType` / `ttlMs` / -`cacheScope`). `skills/*` are consumer-owned, so the SDK stamps nothing for -them; without it a 2026-era connection would receive a result missing the -envelope. It is stamped unconditionally rather than per era — the modern leg -builds a fresh server per request, so there is no era to branch on when the -handlers are registered, and on the legacy leg they are three extra members no -codec inspects. +**Both `skills/*` results carry the full modern base envelope** (`resultType` / +`ttlMs` / `cacheScope`). They are consumer-owned methods, so the SDK stamps +nothing for them; without it a 2026-era connection would receive a result +missing the envelope. It is stamped unconditionally rather than per era — the +modern leg builds a fresh server per request, so there is no era to branch on +when the handlers are registered, and on the legacy leg they are three extra +members no codec inspects. + +⚠️ **`resources/directory/read` deliberately carries `resultType` alone.** +SEP-2640 states the caching attributes for a modern `skills/list` in as many +words and says nothing of the kind for this method, whose one worked example +carries `resultType` and nothing else. A fixture sending more than the SEP shows +would make a client that wrongly *required* them look correct, which is the +opposite of what a conformance fixture is for — so `readDirectoryPage` stops +where the spec does, and `ModernDirectoryReadResultSchema` requires exactly as +much. + It works on **either era**: `skills/list`, `skills/get` and `resources/directory/read` are consumer-owned extension methods that neither era codec defines, so the SDK's era gate skips them entirely — which is why