CXF-274: narrow the skills' prose to what the code actually requires - #19
manuel-ts-14 wants to merge 2 commits into
Conversation
…orer The Output Contract named its field names but never their types, value shapes, or granularity, while evals/runner/stages.ts compares three of them by strict equality or exact set membership. Blind runs that sourced and judged correctly failed P3 and P4 on encoding alone. Documents the contract the scorer already enforces: - authority_rung is scored with nonEmptyString (stages.ts:489, typed at stages.ts:59), but the skill teaches a numbered ladder, so "rung 1" reads as the integer 1 and fails. Note spec_bytes in the same object genuinely is a number, so nothing signals which is which. - spec_version_checked is compared with !== (stages.ts:517), so a richer composite carrying a sha256 and a fetch timestamp fails against "1.2.0". - missing_paths is compared with Array.includes (stages.ts:518), which is exact element match, so descriptions of missing capability families fail against literal path templates. Additive only: every substring asserted by evals/runner/skills_bundle.test.ts is preserved. No behaviour change — this narrows the prose to the code. npm run eval:test exit 0 (139 pass / 0 fail); npm run typecheck exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| - `authority_rung` (string, NOT a number) - the ladder rung as a string, | ||
| e.g. `"1"` for an official published spec. |
There was a problem hiding this comment.
🟡 Suggestion: the string-not-number typing is right (stages.ts:489 uses nonEmptyString), but the example value "1" diverges from the repo's own golden artifacts, which all use the descriptive rung name — "official published spec at a stable URL" in evals/runner/drivers/tier0/canned-pre1-directory-proceed/pre1.json:26, score.test.ts:239, and stages.test.ts:307. Both forms pass P3, so nothing breaks, but a skill that is being written to match the scorer will now teach an encoding no fixture uses. Consider e.g. "official published spec at a stable URL" for rung 1 (or "1" — official published spec at a stable URL) so prose and fixtures agree. (confidence: high on the divergence, medium on which form you want as canonical)
| - `missing_paths` (array of strings) - the literal OpenAPI path templates | ||
| that are absent, e.g. `["/v1/users", "/v1/groups", | ||
| "/v1/groups/{groupId}/members"]`. Not prose descriptions of the missing | ||
| capability families. | ||
| - `vendor_doc` (string) and `revisit_trigger` (string), both non-empty. |
There was a problem hiding this comment.
🟡 Suggestion: P4 also rejects an empty missing_paths array (stages.ts:511, asserted by stages.test.ts:459 "P4 fails when missing_paths is empty"), and every element must be a non-empty string. The new prose spells out "both non-empty" for vendor_doc/revisit_trigger but leaves missing_paths as just "array of strings", so the one array-shaped gate is the one requirement still undocumented — evals/README.md:134 already states it. Suggest "(array of at least one non-empty string)". (confidence: high)
General PR Review: CXF-274: narrow the skills' prose to what the code actually requiresBlocking Issues: 0 | Suggestions: 1 | Threads Resolved: 0 Review SummaryThe new commit Security IssuesNone found. Correctness IssuesNone found. Suggestions
Reviewed head Prompt for AI agents |
…tor-source design-access-model routinely emits provisionable: true, but this skill never mentions grant, revoke, or provisioningResponse, and its only worked skeleton is read-only by its own README. Authoring the provisioning half means deriving it from baton/runtime.d.ts directly, and two declaration-level traps sit on that path: - Two different Grant types ship. baton/helpers.d.ts imports Grant from ./types (baton/types.d.ts:328, no "@type"), which is what provisioningResponse.grant({grants: [...]}) takes. The proto-shaped Grant at baton/sdk-types.d.ts:653 requires "@type". Writing a proto-JSON literal -- which the capabilities section trains you toward -- fails with TS2353. - ResourceTypeSpecShape (runtime.d.ts:2055-2058) accepts both a top-level grant/revoke and a nested provisioning: {grant, revoke}, and both typecheck. The runtime-facing RuntimeResourceTypeSpec (runtime.d.ts:1738) carries only provisioning, so that is the nesting to prefer. Both verified against a provisioning connector authored from this skill and typechecked clean. Kept deliberately terse: the body is at 197 of the 200-line bound that evals/runner/skills_bundle.test.ts:194 enforces, so the fuller worked guidance this skill still lacks does not fit without a references/ mechanism. Noted on CXF-274. npm run eval:test exit 0 (139 pass / 0 fail); npm run typecheck exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| one. Attach the walkers under `provisioning: {grant, revoke}` - the runtime-facing | ||
| `RuntimeResourceTypeSpec` carries only that nesting. |
There was a problem hiding this comment.
🟡 Suggestion: the RuntimeResourceTypeSpec citation isn't the type an author's code is checked against. ConnectorSpec.resourceTypes is readonly AuthoredResourceTypeSpec[] (baton/runtime.d.ts:1954), and AuthoredResourceTypeSpec (1934-1942) carries top-level grant/revoke and nested provisioning — same as ResourceTypeSpecShape (2050-2058). RuntimeResourceTypeSpec (1724) is declared but referenced nowhere in the authoring surface, so it can't be what makes the flat form wrong. Consider either dropping the rationale clause and stating the nesting as the required spelling outright, or replacing it with whatever actually rejects the flat form at build/register time — otherwise an agent that reads runtime.d.ts and finds the flat form on the authored type has a directly contradicting signal. (Medium confidence: the nesting guidance itself may well be correct; only the stated justification doesn't hold.)
mateoHernandez123
left a comment
There was a problem hiding this comment.
Approving. The sourcing field types and the @baton/types Grant (no "@type") match what the scorer and the helpers actually enforce; that is the useful part of the PR.
Nit already on the bot thread, not blocking: rule 6 cites RuntimeResourceTypeSpec as the reason walkers must be nested, but authored connectors are checked against AuthoredResourceTypeSpec, which accepts top-level grant/revoke as well. Keep the nested spelling as the convention if you want, and drop or retarget that citation so an agent reading runtime.d.ts is not taught a type it will never hit.
Summary
Two skills state contracts more loosely than the code enforces them, so correct work fails on encoding and authors hit avoidable compile errors. Both commits are additive prose narrowing the skill to the implementation — no behaviour change, and every substring
evals/runner/skills_bundle.test.tsasserts is preserved.Found by running the pre-1 and authoring skills blind against this repo's own fixture: agents given only the SKILL.md, the provider brief, and the live spec endpoint, barred from reading
evals/scenarios/,evals/results/, andevals/runner/, then graded by replicating the scorer's comparison logic. All runs reached the correct decision — every issue below is the contract around the judgment, not the judgment.1.
source-openapi-spec: type the Output Contract (c30ecc0)The contract names field names but never their types, shapes, or granularity, while
evals/runner/stages.tscompares three of them by strict equality or exact set membership.authority_rungnonEmptyString(stages.ts:489, typedstages.ts:59)1— the skill teaches a numbered ladder, so "rung 1" reads as an integer.spec_bytesin the same object genuinely is a number, so nothing signals which is which.spec_version_checked!==(stages.ts:517)info.version+ OpenAPI version + fetch timestamp + sha256, reasoning the hash makes a park reproducible. Fails against"1.2.0"for being more precise.missing_pathsArray.includes(stages.ts:518)string[], fails against literal path templates.The
pre1-directory-proceedrun scored P0/P1/P2 PASS, P3 FAIL, purely onauthority_rungbeing a number.If you'd rather fix
authority_rungon the scorer side (accept a number) than in the prose, say so and I'll flip it.2.
write-connector-source: name theGranttype and provisioning nesting (0d3979d)design-access-modelroutinely emitsprovisionable: true, but this skill never mentionsgrant,revoke, orprovisioningResponse, and its only worked skeleton is read-only by its own README. Deriving the provisioning half frombaton/runtime.d.tshits two declaration-level traps:Granttypes ship.baton/helpers.d.tsimportsGrantfrom./types(baton/types.d.ts:328, no"@type") — that is whatprovisioningResponse.grant({grants: [...]})takes. The proto-shapedGrantatbaton/sdk-types.d.ts:653requires"@type". Writing a proto-JSON literal, which the capabilities section trains you toward, fails withTS2353. A blind run hit exactly this and burned a fix cycle.ResourceTypeSpecShape(runtime.d.ts:2055-2058) accepts a top-levelgrant/revokeand a nestedprovisioning: {grant, revoke}. The runtime-facingRuntimeResourceTypeSpec(runtime.d.ts:1738) carries onlyprovisioning, so that is the nesting named here. Worth confirming — if the flat form also registers, this is just a preference; if it doesn't, it fails silently rather than erroring, which is why it's worth stating either way.Both verified against a provisioning connector authored from this skill and typechecked clean.
Verification
npm run eval:test→ exit 0 (139 pass / 0 fail)npm run typecheck→ exit 0source-openapi-spec, 17/17 forwrite-connector-sourceskills_bundle.test.ts:194)What this PR deliberately does not fix
write-connector-sourceis at 197 of 200 lines, so the fuller worked provisioning guidance it still lacks — node/walk shapes, the 204-no-body revoke case,CAPABILITY_PROVISIONplacement — does not fit without areferences/mechanism these skills don't currently use. Commit 2 spends the remaining budget on the two traps that cost a build; the rest needs a structural call from you. Recorded on CXF-274 along with the full finding set.🤖 Generated with Claude Code