Skip to content

docs(internet-identity): split into five skills, one file each - #454

Merged
sea-snake merged 4 commits into
mainfrom
docs/split-internet-identity-skill
Nov 6, 2026
Merged

sea-snake merged 4 commits into
mainfrom
docs/split-internet-identity-skill

Conversation

@sea-snake

Copy link
Copy Markdown
Contributor

Summary

The Internet Identity skill was 424 lines plus six files in references/, covering five jobs that are loaded at different times by different people. It becomes five skills, each a single SKILL.md:

Skill What it is for
internet-identity Sign-in, the client lifecycle, local development, and the primary origin an app's principals are permanently tied to
internet-identity-shared-sessions One sign-in across sibling subdomains
internet-identity-attributes A verified email or display name in the backend, Motoko and Rust
internet-identity-notifications Web push from a canister while the app is closed
internet-identity-sso The organization's own single sign-on, an IT admin's job

references/ is gone. Every reference file is absorbed into the body of the skill that owns it, so each skill reads end to end in one file and the Rust/Motoko asymmetry in attributes (Motoko inline, Rust one hop away) disappears.

Routing

No new frontmatter field: routing is the description plus backticked skill names in prose, which is what the repo already does. Each leaf skill opens with a one-line prerequisite pointing at internet-identity; internet-identity closes with the four follow-ups. Nothing outside the suite links to attributes, notifications or SSO, so those three cut cleanly. Origins do have inbound links, which is why they stay in core.

Content changes beyond moving text

  • Mistake 9 was wrong and is replaced. It claimed that setting derivationOrigin to the gateway domain breaks authentication. It does not: validateDerivationOrigin.ts:31-37 compares the two origins in canonical form and returns valid before any fetch, so it is a no-op. The real mistake, letting the first origin with users be one you can lose, takes its place.
  • Derivation origins are reframed around the canister id and moved after sign-in works rather than before it. The default, setting nothing, is already correct, so the section teaches a decision instead of a configuration.
  • ii-derivation-origin is documented here for the first time. It appeared only in service-discoverability. Both directions of the relation are now described together with who reads each.
  • shared-sessions.md built its client with derivationOrigin: \"https://auth.example.com\", a custom domain, which is the one thing the origins section advises against. Corrected to the canister address.
  • app-metadata.md's opening was a heading and a sentence spliced together by a bad edit. Repaired.
  • Notifications gains the parallel sign-in and consent pattern, a notifications toggle, the composed notification title (II shows the app's name then the title the canister sent, falling back to the origin's hostname), and the per-browser fan-out, informed by docs(authentication): notifying a user through Internet Identity developer-docs#438.
  • Implementation details of our own libraries are out. A skill documents the public API, the documented errors and observable behaviour; where an internal fact has a consequence, the consequence is written instead of the mechanism.

Verified

  • node scripts/check-project.js passes for 39 skills. The two warnings are pre-existing (autosync-ic-skills, sns-launch have no eval files); all five skills here do.
  • Every code fence carries the language the original source used, checked block by block against the files being replaced.
  • No em dashes and no banned vocabulary in any of the five.
  • The two claims in the rewrite come from reading dfinity/internet-identity at origin/main, not from the existing docs: the derivation-origin no-op from validateDerivationOrigin.ts, and what the sign-in header shows from AuthorizeHeader.svelte. The notification title composition and the per-browser queue come from the service worker's wake-up path and the canister's notifications module.

Evaluations

Existing cases follow the skill that now owns them: 6 attribute cases, 4 shared-session cases and 3 notification cases move out of evaluations/internet-identity.json, which keeps 22. internet-identity-sso had no cases, since the three SSO cases in the old suite all test the app-side ssoDomain check that stays in core, so it gets three new adversarial ones for the discovery file: whole-file rejection on one bad value, exact-origin app_clients keys, and stable_identifier_claim on Entra ID. Trigger evals are rewritten per skill, with each sibling's queries as negatives.

Eval runs are in progress and will be posted as a comment on this PR.

🤖 Generated with Claude Code

The Internet Identity skill carried 424 lines plus six reference files
covering five separate jobs. Each job now loads on its own:

- internet-identity: sign-in, the client lifecycle, local development,
  and the primary origin an app's principals are tied to
- internet-identity-shared-sessions: one sign-in across sibling subdomains
- internet-identity-attributes: a verified email or name in the backend
- internet-identity-notifications: web push from a canister
- internet-identity-sso: the organization's own single sign-on

No references/ directory: every reference file is absorbed into the body
of the skill that owns it, so each skill reads end to end in one file.

Routing is the description plus backticked skill names in prose, as the
repo already does. Each leaf opens with a one-line prerequisite pointing
at internet-identity; core closes with the four follow-ups.

Content changes beyond moving text:

- Mistake 9 claimed that setting derivationOrigin to the gateway domain
  breaks authentication. It does not: II compares the two origins in
  canonical form and skips validation, so it is a no-op. Replaced with
  the real mistake, letting the first origin with users be one you can
  lose.
- Derivation origins are reframed around the canister id and moved after
  sign-in works, since the default (set nothing) is already correct.
- ii-derivation-origin is documented here for the first time; it only
  appeared in service-discoverability.
- shared-sessions set derivationOrigin to a custom domain, which the
  origins section advises against. Corrected to the canister address.
- app-metadata's opening had a heading and a sentence spliced together.
- Notification consent gains the parallel sign-in pattern, the toggle,
  the composed notification title, and the per-browser fan-out.

Evaluation cases follow their skill, and internet-identity-sso gets three
new ones for the discovery file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sea-snake
sea-snake requested review from a team and JoshDFN as code owners November 6, 2026 10:28
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown

Skill Validation Report

Project Checks


✓ Project checks passed for 5 skills (0 warnings)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The SSO example now references undeclared client and timer identifiers, causing a runtime failure before the SSO check can be scheduled.

2 open issues
What changed in this PR

Splits the Internet Identity documentation into five task-focused, single-file skills so agents can load the guidance relevant to each integration step.

Changes:

  • Keeps sign-in, origin selection, app metadata, and API migration guidance in the core skill.
  • Separates shared sessions, identity attributes, notifications, and organization SSO into dedicated skills.
  • Redistributes evaluation cases and adds SSO configuration checks and skill-specific trigger examples.
File Description
skills/​internet-identity/​SKILL.md Refocuses core guidance and links to specialized skills.
skills/​internet-identity/​references/​rust-identity-attributes.md Removes reference absorbed into the attributes skill.
skills/​internet-identity/​references/​older-api.md Moves older API guidance into core.
skills/​internet-identity/​references/​app-metadata.md Moves metadata guidance into core.
skills/​internet-identity-sso/​SKILL.md Provides standalone organization SSO setup guidance.
skills/​internet-identity-shared-sessions/​SKILL.md Covers cross-subdomain sessions and state updates.
skills/​internet-identity-notifications/​SKILL.md Adds dedicated consent, sending, and delivery guidance.
skills/​internet-identity-attributes/​SKILL.md Combines frontend requests and both backend implementations.
evaluations/​internet-identity.json Narrows evaluations to core responsibilities.
evaluations/​internet-identity-sso.json Adds discovery-file and provider configuration cases.
evaluations/​internet-identity-shared-sessions.json Moves shared-session cases and adds focused triggers.
evaluations/​internet-identity-notifications.json Moves notification cases and adds focused triggers.
evaluations/​internet-identity-attributes.json Moves attribute verification cases and adds focused triggers.

Review effort: Balanced

Comment thread skills/internet-identity/SKILL.md Resolved
Comment thread skills/internet-identity/SKILL.md Resolved
sea-snake and others added 2 commits November 6, 2026 11:38
Two review findings.

The SSO typing example lost `let client` and `let timer` when its caption
was rewritten, so the first input event threw a ReferenceError before the
check could be scheduled. Optional chaining does not cover an undeclared
identifier.

"Never replaced or reinstalled" was wrong about reinstalling. Principals
derive from the origin, and a reinstall keeps the canister id, so the code
and state behind that origin are free to change. What cannot change is
which canister id holds the role.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…teaches

The access-gating case expected the backend to look up the verified_email
key directly. The skill teaches the library, which resolves verified_email
into the email field of the struct it hands onVerified, so an answer that
follows the skill read attrs.email and was marked down for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sea-snake

Copy link
Copy Markdown
Contributor Author

Eval results

Run with node scripts/evaluate-skills.js <skill>, full suite per new skill, with baseline.

Skill Output evals (WITH / WITHOUT) Triggers
internet-identity-notifications 12/14 / 3/14 5/5 should, 4/4 should-not
internet-identity-shared-sessions 19/20 / 14/20 4/4 should, 4/4 should-not
internet-identity-attributes 20/26 / 15/26 5/5 should, 4/4 should-not
internet-identity-sso 14/14 / 9/14 5/5 should, 4/4 should-not

Two cases scored below baseline, and neither is a content gap:

  • Shared sign-in across sibling subdomains (8/9 vs 9/9). The one miss is the judge not accepting showResumeDialog(...) from the skill's own example as "a button". The baseline happened to write a literal <button>.
  • Adversarial: using email instead of verified_email for access gating (3/5 vs 4/5). One of its expected behaviors asked the backend to look up the verified_email key directly. The skill teaches the library, which resolves verified_email into the email field of the struct handed to onVerified, so an answer that follows the skill reads attrs.email and was marked down for being right. Corrected in 1577b27 and re-run.

Adversarial: reading attribute data without verifying signer passes 2/5 with the skill against 1/5 without. Its expected behaviors want hand-written Rust showing the signer check, the trap on mismatch and the ICRC-3 decode, while the skill directs agents to the library and covers the hand-written path in a single sentence of its mistakes list. That gap predates this PR; worth either writing that path out or rescoping the case, in a change of its own.

Per-case results
===== internet-identity-notifications =====
  Notification consent returns a record, not a boolean:        WITH 5/5 | WITHOUT 1/5
  The sender list is required before any send works:           WITH 4/5 | WITHOUT 0/5
  Delivery is not the app's to control:                        WITH 3/4 | WITHOUT 2/4
  Trigger evals: should-trigger 5/5 | should-not-trigger 4/4

===== internet-identity-shared-sessions =====
  Shared sign-in across sibling subdomains:                    WITH 8/9 | WITHOUT 9/9
  Adversarial: shared cookie but different principals:         WITH 4/4 | WITHOUT 3/4
  Adversarial: silent re-issue without hint:                   WITH 4/4 | WITHOUT 1/4
  Adversarial: redirecting an already-open page to /reauth:    WITH 3/3 | WITHOUT 1/3
  Trigger evals: should-trigger 4/4 | should-not-trigger 4/4

===== internet-identity-attributes =====
  Adversarial: frontend-generated attribute nonce:             WITH 3/4 | WITHOUT 4/4
  Adversarial: reading attribute data without verifying signer:WITH 2/5 | WITHOUT 1/5
  Adversarial: substituting tid in the Microsoft scoped-key:   WITH 3/3 | WITHOUT 0/3
  Adversarial: using email instead of verified_email:          WITH 3/5 | WITHOUT 4/5
  Motoko backend uses the identity-attributes library:         WITH 5/5 | WITHOUT 5/5
  Adversarial: verified_email under an SSO scope:              WITH 4/4 | WITHOUT 1/4
  Trigger evals: should-trigger 5/5 | should-not-trigger 4/4

===== internet-identity-sso =====
  Adversarial: one bad value rejects the whole discovery file: WITH 5/5 | WITHOUT 1/5
  Adversarial: app_clients key must be the exact origin:       WITH 5/5 | WITHOUT 5/5
  Adversarial: stable identifier claim on Entra ID:            WITH 4/4 | WITHOUT 3/4
  Trigger evals: should-trigger 5/5 | should-not-trigger 4/4

Trigger evals are the part that matters most for a split: every skill fires on its own queries and on none of its siblings', including the three pairs most likely to collide (Let my app send users to their company's SSO goes to internet-identity, not internet-identity-sso; Add Internet Identity login to my frontend triggers none of the four leaves).

internet-identity keeps 22 cases and was not re-run in full: its content moved rather than changed, apart from Mistake 9 and the origins section. Say the word if you want that suite run too.

… email

The Keys table names the two routes, an OpenID provider marking the address
verified or the user verifying it with II, and the mistakes list did not.
An agent quoting the mistake told the reader to gate on verified_email
without saying what makes it trustworthy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sea-snake

Copy link
Copy Markdown
Contributor Author

Follow-up on the two below-baseline cases from the results above.

Adversarial: using email instead of verified_email for access gating, after correcting the stale expectation in 1577b27: WITH 4/5 | WITHOUT 4/5. No longer below baseline, and no delta either.

Its one remaining miss is identical on both sides: neither answer says how Internet Identity establishes a verified email. The Keys table named the two routes and the mistakes list did not, so e851345 added them there too. Re-running after that change left the score at 4/5: the fact is now stated in two places and the answer still does not repeat it on a prompt about access gating. I have stopped there rather than keep editing the skill against the judge. The content is present; this is the case asking an answer to recite a mechanism the prompt does not ask about.

Shared sign-in across sibling subdomains (8/9 vs 9/9) stands as reported: the miss is showResumeDialog(...) from the skill's own example not reading as "a button" to the judge.

@sea-snake
sea-snake merged commit 5b70e9d into main Nov 6, 2026
6 checks passed
@sea-snake
sea-snake deleted the docs/split-internet-identity-skill branch November 6, 2026 11:13
sea-snake added a commit that referenced this pull request Nov 9, 2026
)

`checks / validate` failed on the push of #454 to `main`
([run](https://github.com/dfinity/icskills/actions/runs/42208650264/job/127861532696)),
so `deploy-ic.yml` never ran and the five Internet Identity skills are
not on https://skills.internetcomputer.org/.

## The error

```
Validating skill: skills/internet-identity
Frontmatter
  ✗ description exceeds 1024 characters (1029)
```

Dropping two words ("It also covers" → "Also covers", "wallet
integration or ICRC signer flows" → "wallet or ICRC signer flows")
brings it to 1014, in line with `cloud-engine-canisters` (1014) and
`deploy-to-cloud-engine` (1010). No routing term is lost:
`wallet-integration` still appears in the same sentence as the skill to
load instead.

## Why CI did not catch it on the PR

`skill-validator check` takes **exactly one** path:

```
$ skill-validator check skills/internet-identity skills/internet-identity-sso --skip links
Error: accepts 1 arg(s), received 2
$ echo $?
3
```

The changed-skills step builds a space-separated list of paths, so on
any PR touching two or more skills the validator exited 3 (usage error)
with an empty report, and the gate only failed on exit code `1`. Every
such PR has been going green without being validated. On a push to
`main` the same step passes the single path `skills/`, which is why the
error only surfaced after the merge.

Two changes:

- each path gets its own invocation, aggregating the exit codes with an
error (1) winning over a warning (2);
- the gate fails on any code other than 0 (clean) or 2 (warnings), so a
usage error can never read as a pass again.

## Verification

Per-path loop over the five skills from #454, with the description fix
applied:

```
skills/internet-identity                  -> 2
skills/internet-identity-attributes       -> 0
skills/internet-identity-notifications    -> 0
skills/internet-identity-shared-sessions  -> 0
skills/internet-identity-sso              -> 0
aggregate=2        # warnings only, gate passes
```

Same loop before the description fix returns `1` for
`skills/internet-identity`, so the new gate catches exactly the failure
that got through. `skill-validator check skills/ --skip links` now exits
2 (was 1), and `node scripts/check-project.js` passes for 39 skills with
the two pre-existing eval-file warnings.

No skill body changed, so no evals were re-run.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants