docs(service-discoverability): add the agent traversal order and label pitfall - #369
docs(service-discoverability): add the agent traversal order and label pitfall#369aterga wants to merge 4 commits into
Conversation
…l pitfall Audited the skill against the current upstream guide (docs.internetcomputer.org/guides/frontends/service-discoverability/). The five layers, field rules, serving rules, deployment checklist and acceptance tests were already covered — and in two places the skill already corrects the guide (the `-e ic` vs `--network ic` pairing for a raw canister ID, and the claim that certified-assets needs a `/.well-known/*` SPA exemption). The gaps were: - No statement of the order in which an agent actually consumes the layers. Added "How an Agent Traverses This": the five-step walk from a bare URL to a correctly-encoded call, framed so each step shows which published fields have to be good for the next one to be cheap. - New pitfall 10: a manifest of bare `id` values is schema-valid but useless, since the agent then has to fetch `candid:service` for every canister and guess from method names — the exact work the manifest exists to avoid. - Checklist now carries the guide's routing line, scoped to the hosts where it is not automatic (legacy asset canister, non-IC hosts). - Concrete forward-compat examples on the unknown-fields rule, and a link to the Candid interface guide. Evals: two cases added for the new content, both run with baseline — role labels 4/4 with skill vs 2/4 without, traversal order 5/5 vs 2/5. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvQiPZ2t6HuCDM8ie7MerC
Skill Validation ReportValidating skill: /home/runner/work/icskills/icskills/skills/service-discoverabilityStructure
Frontmatter
Tokens
Markdown
Tokens
Content Analysis
Contamination Analysis
Result: 1 warning Project Checks |
…added CI flagged the body crossing the 5,000-token spec recommendation (4,753 -> 5,361). Rather than trim substance, cut the duplication the new section actually introduced: - The traversal restated each layer's rationale, which the layer sections and pitfall 10 already own. Reduced to the ordered requests plus the round-trip cost argument. - Layer 5's ii-alternative-origins bullet re-explained the inverse relation at length; pitfall 8 now owns that, and the bullet keeps only the derivationOrigin coupling and the default-origin caveat. Down to 5,206 tokens. Still over the recommendation, and left there: 11 of the other 28 skills exceed it too (writing-motoko 11k, internet-identity 8.8k), so cutting load-bearing content to reach a soft threshold would be the wrong trade. Re-running the touched cases caught two regressions from the first pass, both fixed here and re-verified: - Compressing traversal step 5 dropped "act as the signed-in user" and the derivation-origin behavior stopped surfacing (5/5 -> 4/5, now 5/5). - Compressing the Layer 5 bullet dropped "silently"; pitfall 8 said "yields the wrong principal" without it, and the silent-failure warning stopped surfacing (4/4 -> 3/4, now 4/4 with the wording restored to the pitfall). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvQiPZ2t6HuCDM8ie7MerC
There was a problem hiding this comment.
Pull request overview
Expands service-discoverability guidance with agent traversal order and stronger manifest labeling guidance.
Changes:
- Documents the five-step discovery traversal.
- Adds routing and unlabeled-manifest pitfalls.
- Adds evaluation coverage for traversal and labels.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
skills/service-discoverability/SKILL.md |
Extends discovery and deployment guidance. |
evaluations/service-discoverability.json |
Adds traversal and labeling evaluations. |
Suppressed comments (1)
skills/service-discoverability/SKILL.md:236
- This checklist prescribes the wrong universal remedy for legacy and non-IC hosts. Legacy
enable_aliasingonly servesindex.htmlwhen no file matches; its actual.well-knownrequirement is explicitly uploading the hidden directory via.ic-assets.json5(skills/custom-domains/SKILL.md:75). Other hosts also vary in whether real files shadow SPA rewrites, so an exemption is only needed when their routing precedence requires one.
- [ ] **Routing (non-static-site hosts only):** `/.well-known/*` is exempt from the SPA catch-all rewrite. Automatic on the static-site (certified-assets) canister, where a real file wins over the rewrite; needed on the legacy `@dfinity/asset-canister` or any non-IC host.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…nd soften pitfall 10 Addresses the review on #369. All four inline findings plus the suppressed one checked out against the repo, so all five are applied. The suppressed finding was the significant one, and it exposed a pre-existing error the new checklist line had propagated: the legacy @dfinity/asset-canister does NOT need a /.well-known/* SPA exemption. Its enable_aliasing serves index.html only when no file matches (static-site references/legacy-asset-canister.md:69), exactly like certified-assets, so a real file wins there too. Its actual requirement is an .ic-assets.json5 un-ignore rule so the hidden directory ships at all (custom-domains SKILL.md:75, static-site pitfall 12) — an upload problem, not a routing one. Corrected in all three places that carried it: the "Serve it correctly" bullet, pitfall 4, and the checklist line (now "Reachability", and no longer prescribing one universal remedy; non-IC hosts are told to check their own routing precedence). Also from the review: - The traversal ran execute(...) before obtaining the delegation, which fails against a data surface that requires a signed principal. Identity now precedes the data step, with a note that step 3 is where the agent learns which methods are gated, so only unauthenticated reads may run ahead of it. - The traversal and pitfall 10 named role/description as the identifying labels, omitting name — which the manifest examples themselves use to identify the backend. Now "whatever identifies it". - Pitfall 10 called a bare-ID manifest "useless". It is not: it still enumerates the canisters without out-of-band discovery. Reworded to the real cost — a candid:service fetch per entry to recover the routing. Evals: cases 8 and 9 encoded the same overstatements and were rewritten to check identifying labels and the extra-fetch cost rather than mandate role; 9 gained an expectation that identity is resolved before any gated call. Case 4 asserted the disproved legacy claim and was corrected. All re-run: 4 -> 4/4, 8 -> 4/4, 9 -> 6/6. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvQiPZ2t6HuCDM8ie7MerC
marc0olo
left a comment
There was a problem hiding this comment.
Reviewed the content changes, re-verified the factual claims independently, and re-ran the touched evals plus the full trigger suite. The changes are sound and a real improvement — approving. Two follow-ups below are left to your judgment; neither blocks the merge.
What I verified
The factual corrections are right — checked against the actual source, not just our own references. In dfinity/sdk's ic-certified-assets, FALLBACK_FILE = "/index.html" is served only when no asset matches the path, so a real uploaded file always wins — the legacy @dfinity/asset-canister needs no /.well-known/* SPA exemption, and its actual requirement is the .ic-assets.json5 un-ignore rule (matching skills/custom-domains/SKILL.md:75). Correcting all three places carrying the old claim was the right call; fixing only the new checklist line would have left the skill self-contradictory.
The traversal section matches upstream and improves on it. It formalizes the guide's closing "End to end" paragraph, and the identity-ordering fix from the review is correct — getApiDoc (step 3) is where the agent learns what is gated, so the delegation now precedes the first protected call. Side observation: the upstream guide's checklist still prescribes a universal "/.well-known/* is exempt from the SPA catch-all rewrite," which this PR's finding shows is overbroad — worth filing as an upstream docs issue.
Structure: pitfall renumbering (old 10 → 11) is consistent, all internal references (Pitfalls 7, 8, 10) point at the right targets, and no other skill references these pitfalls by number. npm run validate passes all 29 skills; the 5,410-token warning matches the PR's disclosure and leaving it over the soft threshold looks like the right trade.
Triggers — all pass. The PR added a trigger query but included no trigger-eval results, so I ran the suite: 9/9 should-trigger (including the new query) and 8/8 should-not-trigger.
Output evals — 3 of 4 claims reproduce. Independent re-runs with the skill: case 4 → 4/4, case 6 → 4/4, case 9 → 6/6.
Follow-up 1 (your call): eval case 8 is flaky as committed
Both of my independent runs scored 3/4, failing the same expectation each time:
❌ "Says a bare-ID manifest is schema-valid and still useful for enumerating the canisters without out-of-band discovery"
→ The output only concedes bare IDs are 'technically valid' while framing the overall answer as 'No', never affirming the enumeration value.
The cause looks like a prompt/expectation mismatch: the prompt ends with "Just the key point", so the with-skill answer leads with "No, add labels" and compresses out the affirmative nuance — even though Pitfall 10 itself contains it ("it still earns its keep"). As committed, this case will intermittently fail future regression runs. Two easy fixes, either works:
- Reword expectation 1 into the negative form the answer reliably satisfies: "Does NOT dismiss a bare-ID manifest as invalid or useless (acknowledges it still enumerates the canisters)", or
- Drop "Just the key point" from the prompt so the answer has room for the nuance.
Follow-up 2 (non-blocking nit)
The new wording attributes the legacy canister's index.html behavior to enable_aliasing ("its enable_aliasing also only serves index.html when no file matches"). Strictly, per the sdk interface doc, enable_aliasing is the per-path /x → /x.html / /x/index.html rule; serving index.html when nothing matches is the separate built-in FALLBACK_FILE mechanism. The conclusion is unaffected (a real file wins either way), and the same shorthand already exists in static-site's legacy reference, so this is repo-consistent rather than a new error — fine to leave or tighten opportunistically.
Review generated with Claude Code
…ck attribution Both follow-ups from the review on #369, neither blocking; both verified before applying. Eval case 8's first expectation was positively phrased ("Says a bare-ID manifest is ... still useful for enumerating"), but the prompt ends with "Just the key point", so the answer leads with "No, add labels" and compresses the affirmative nuance out — the reviewer scored 3/4 on two independent runs where the local run had scored 4/4. Reworded to the negative form, which checks the thing that actually matters (the skill must not teach that a bare-ID manifest is useless) and does not require the answer to spend words affirming it. Kept the prompt scoped rather than dropping "Just the key point", since CONTRIBUTING wants prompts tight enough to stay inside the eval timeout. Now 4/4 on two runs. The legacy fallback sentence attributed serving index.html-on-no-match to enable_aliasing. Strictly that flag is the per-path /x -> /x.html aliasing rule; the no-match fallback is the separate built-in. The conclusion (a real file wins, so no SPA exemption is needed) is unaffected, so this just names the behavior without naming the wrong mechanism — which also avoids contradicting static-site's legacy reference, where the same shorthand lives. Evals: 8 -> 4/4 twice. Case 4 covers the edited sentence and was re-run three times: 4/4, 4/4, and one 3/4 on an unrelated expectation (.well-known auto-upload), which is pre-existing variance in that case rather than a regression from this change. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvQiPZ2t6HuCDM8ie7MerC
|
Thanks — both follow-ups verified and applied in 06e74e4. Follow-up 1 — eval 8 flakiness. Confirmed and fixed. Your diagnosis was right: the prompt ends with "Just the key point", so the answer leads with "No, add labels" and compresses the affirmative nuance out, even though Pitfall 10 carries it. I took the first of your two options — rewording expectation 1 into the negative form:
I kept Follow-up 2 —
That keeps it accurate without contradicting One honest note on eval 4. I re-ran it three times after editing the sentence it covers: 4/4, 4/4, and one 3/4 that failed a different expectation ( On your trigger-suite run — thank you for filling that gap. You're right that the PR added a should-trigger query without including trigger-eval results; your 9/9 and 8/8 is now the record for it. On the upstream docs issue — agreed the guide's universal " Generated by Claude Code |
What
Audited
skills/service-discoverability/SKILL.mdagainst the current upstream guide, Service discoverability, and closed the gaps it surfaced.The skill already covered the guide's five layers, field rules, serving rules, deployment checklist and acceptance tests — and in one place already corrects it: the guide's acceptance test pairs a raw canister ID with
-e ic, but-e/--environmentresolves canisters by name within a project, so a bare principal needs--network ic(seeicp-cliskill, Pitfall 2). That stays as it is.Added:
## How an Agent Traverses This— the guide closes by describing the order in which an agent consumes the layers; the skill had the five layers but never stated the walk. Five ordered requests from a bare URL to a correctly-encoded call, framed for a publisher: each step names which published field saves work at the next.idis the only required field, so a bare-ID manifest is valid and still enumerates the app's canisters — but with nothing to tell them apart, the agent pays acandid:servicefetch per entry to recover the routing.A pre-existing bug this surfaced
Copilot's review included a suppressed comment on the checklist line I added. It checked out, and it turned out to be a pre-existing error my line had propagated:
The legacy
@dfinity/asset-canisterdoes not need a/.well-known/*SPA exemption. Itsindex.htmlfallback fires only when no file matches — exactly like certified-assets, so a real file wins there too. Its actual.well-knownrequirement is an.ic-assets.json5un-ignore rule so the hidden directory is uploaded at all (skills/custom-domains/SKILL.md:75,static-sitePitfall 12). That is an upload problem, not a routing one.Corrected in all three places carrying it — the "Serve it correctly" bullet, Pitfall 4, and my checklist line (now "Reachability", and no longer prescribing one universal remedy; non-IC hosts are told to check their own routing precedence). Two of those three predate this PR; fixing only mine would have left the skill self-contradictory.
@marc0olo independently confirmed this against
dfinity/sdkitself (ic-certified-assets,FALLBACK_FILE = "/index.html"), not just our own references.Review fixes (42ac85a)
All four inline Copilot findings verified and applied:
execute(...)before obtaining the delegationgetApiDoc(step 3) is where the agent learns which methods are gated, so only unauthenticated reads run ahead of itrole/description, omittingname"name": "backend"to identify the target; now "whatever identifies it"roleCase 4 also asserted the disproved legacy claim and was corrected.
Review follow-ups (06e74e4)
Both non-blocking items from @marc0olo's approving review, verified and applied:
Eval case 8 was flaky as committed. Their two independent runs scored 3/4 where the local run scored 4/4, failing the same expectation each time. Cause confirmed: the prompt ends with "Just the key point", so the answer leads with "No, add labels" and compresses out the affirmative nuance — even though Pitfall 10 carries it. Took the first suggested fix, rewording expectation 1 into the negative form:
Kept the prompt scoped rather than dropping "Just the key point", since CONTRIBUTING wants prompts tight enough to stay inside the eval timeout. The negative form checks the thing that matters — the skill must not teach that a bare-ID manifest is useless — without requiring the answer to spend words affirming it. 4/4 on two consecutive runs.
enable_aliasingattribution tightened. Strictly, that flag is the per-path/x→/x.htmlrule; servingindex.htmlon no match is the separate built-in. The conclusion is unaffected, so the sentence now names the behavior without naming the wrong mechanism — which also avoids contradictingstatic-site's legacy reference, where the same shorthand lives. Tightening that one too is a separate change to a skill this PR does not touch.Note on skill size
The body went from 4,753 tokens to 5,361, crossing the validator's 5,000-token recommendation. A compression pass cutting the duplication the new section introduced brought it to 5,206; the review fixes then took it to 5,406 — the corrected legacy explanation and the identity-ordering note are both longer than what they replaced.
Left over the threshold deliberately: 11 of the other 28 skills exceed the recommendation too (
writing-motoko11,069;internet-identity8,790;icp-cli7,713), so cutting load-bearing content to reach a soft threshold looked like the wrong trade — particularly for content a reviewer established was needed.Validation
npm run validate— 29 skills validated, all passed.Evals
Every case touched was re-run. Two were added for the new content and run with baseline; three existing cases were re-run because this PR edited content they cover.
Final state: 4 → 4/4, 6 → 4/4, 8 → 4/4 (×2), 9 → 6/6.
Triggers: 9/9 should-trigger (including the query this PR adds), 8/8 should-not-trigger — run by @marc0olo during review, filling a gap in the original submission.
Eval 8 — manifest entries need identifying labels, not just IDs (WITH 4/4 | WITHOUT 2/4)
Expectation 1 is the de-flaked negative form from 06e74e4; the run below is post-fix. Baseline was run against this case's original wording.
Eval 9 — Discovery traversal order from a bare URL (WITH 6/6 | WITHOUT 2/5)
Gained a sixth expectation from the Copilot review — that identity is resolved before any gated call.
Evals 4 and 6 — existing cases re-run (4/4 each)
Case 4 covers the legacy-canister claim corrected above; its third expectation asserted the disproved version and was rewritten:
Known variance in case 4: re-run three times after 06e74e4 edited a sentence it covers — 4/4, 4/4, and one 3/4 failing a different expectation (
.well-known/auto-upload). That edit was to a separate bullet, so this reads as pre-existing variance in the case rather than a regression. Not fixed here, since it is outside what the review raised — flagged so it is not a surprise in a future regression run.Case 6 covers the Layer 5 bullet edited during compression. It dipped to 3/4 when "silently" was dropped — the pitfall said only "yields the wrong principal" — and is back to 4/4 with that wording restored to Pitfall 8.
The remaining five existing cases were not re-run: no content they cover was modified.
Known follow-ups, not in this PR
skills/static-site/references/legacy-asset-canister.mdcarries the sameenable_aliasingshorthand. Different skill, untouched here./.well-known/*is exempt from the SPA catch-all rewrite", which this PR's finding shows is overbroad. Worth adfinity/developer-docsissue.🤖 Generated with Claude Code
https://claude.ai/code/session_01QvQiPZ2t6HuCDM8ie7MerC