[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-apollo-graphql-connect-migrate":3,"mdc--rk6cq7-key":34,"related-org-apollo-graphql-connect-migrate":3847,"related-repo-apollo-graphql-connect-migrate":4021},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":24,"repoUrl":25,"updatedAt":26,"license":27,"forks":28,"topics":29,"repo":30,"sourceUrl":25,"mdContent":33},"connect-migrate","migrate Apollo Connectors schemas","Upgrade Apollo Connectors schemas from connect\u002Fv0.2 or connect\u002Fv0.3 to connect\u002Fv0.4, where the SubSelection\u002FLitObject grammar unification changes how some @connect(selection: …) expressions parse. Use this skill when the user: (1) wants to migrate Apollo Connectors to connect\u002Fv0.4 or bump a @link(url: \"...\u002Fconnect\u002Fv0.n\") version to v0.4, (2) has @connect(selection: …) selections needing behavior-preserving `$.` fortification (quoted keys or bare identifiers that v0.4 re-reads as literals), (3) references the connect-migrate CLI, its analyze manifest, or agent-guide output, (4) asks whether their connector schemas are safe to upgrade to connect\u002Fv0.4.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"apollo-graphql","Apollo GraphQL","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fapollo-graphql.png","apollographql",[13,17,18,21],{"name":14,"slug":15,"type":16},"GraphQL","graphql","tag",{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16},"Migration","migration",{"name":22,"slug":23,"type":16},"Engineering","engineering",1,"https:\u002F\u002Fgithub.com\u002Fapollographql\u002Fconnect-migrate","2026-06-06T07:09:04.299719","Elastic-2.0",0,[],{"repoUrl":25,"stars":24,"forks":28,"topics":31,"description":32},[],"Hybrid CLI\u002Fskills to smooth migrations from one version of Apollo Connectors to the next","---\nname: connect-migrate\ndescription: >\n  Upgrade Apollo Connectors schemas from connect\u002Fv0.2 or connect\u002Fv0.3 to\n  connect\u002Fv0.4, where the SubSelection\u002FLitObject grammar unification changes how\n  some @connect(selection: …) expressions parse. Use this skill when the user:\n  (1) wants to migrate Apollo Connectors to connect\u002Fv0.4 or bump a\n  @link(url: \"...\u002Fconnect\u002Fv0.n\") version to v0.4,\n  (2) has @connect(selection: …) selections needing behavior-preserving `$.`\n  fortification (quoted keys or bare identifiers that v0.4 re-reads as literals),\n  (3) references the connect-migrate CLI, its analyze manifest, or agent-guide\n  output,\n  (4) asks whether their connector schemas are safe to upgrade to connect\u002Fv0.4.\nlicense: Elastic-2.0\ncompatibility: Requires the connect-migrate CLI on PATH (see install.sh). Works with Claude Code and similar AI coding assistants.\n---\n\n# Apollo Connectors `connect\u002Fv0.3` → `connect\u002Fv0.4` migration skill\n\nYou are helping a developer upgrade an Apollo Connectors–enabled\nsupergraph from `@link(url: \"https:\u002F\u002Fspecs.apollo.dev\u002Fconnect\u002Fv0.3\")`\nto `connect\u002Fv0.4`. The [SubSelection\u002FLitObject grammar\nunification](https:\u002F\u002Fgithub.com\u002Fapollographql\u002Frouter\u002Fpull\u002F9261) in v0.4\nchanges how a small but important class of `@connect(selection: …)`\nexpressions parse. Across a 7,375-supergraph customer corpus:\n\n- 97.4% of `@connect(selection: …)` strings parse identically in v0.3\n  and v0.4 — no migration needed.\n- 2.1% have at least one site where the v0.4 reading differs from v0.3.\n\nAlmost every divergent site falls into one of two mechanical\ncategories, and the analyzer resolves both **without asking the\ndeveloper anything**:\n\n- **Bare `null` \u002F `true` \u002F `false`** in value position (`status: null`,\n  `success: true`). In v0.3 the parser looked up a *field* named\n  `null`\u002F`true`\u002F`false`, didn't find it, and returned `None`, which\n  response normalization surfaced as `null` — accidentally matching the\n  literal the developer meant. v0.4 reads it as a literal directly. The\n  observable output is the same, so this needs **no edit**.\n- **Quoted tokens** (`\"@odata.nextLink\"`, `\"USD\"`, `\"0.00\"`) and **bare\n  identifiers**. In v0.3 these were *field accesses* (`Key::Quoted` \u002F\n  `Key::Field`); v0.4 silently rereads them as string literals. The\n  behavior-preserving fix is a deterministic `$.` fortification:\n  `\"USD\"` → `$.\"USD\"`, `soldTo` → `$.soldTo`. The analyzer applies these\n  mechanically.\n\nThis is the central design rule: **a quoted token had no string-literal\nmeaning in v0.3, so treating it as a literal would silently change\nbehavior — which a migration must never do on the developer's behalf.**\nWe always preserve the v0.3 reading. If the developer genuinely wants a\nliteral string, that is a deliberate edit they make later, not something\nthe migration guesses at.\n\nWhat remains after those two categories is a small residue of genuinely\nambiguous, structural divergences the analyzer cannot resolve. Those —\nand only those — become **questions for the developer**. The whole point\nof this skill is to distill the work down to those questions: apply\neverything mechanical, then interview the developer about the few real\nforks, rather than making them ratify hundreds of non-decisions.\n\n## How this skill works\n\nThis is a single agent-driven session, not a file you hand a human to\nedit:\n\n1. **Analyze** the project. `connect-migrate analyze` writes a\n   **manifest** sorting every divergent site into three buckets:\n   *Rewrites to apply* (deterministic), *No action needed* (no-op), and\n   *Questions for the developer* (genuine ambiguity).\n2. **Apply** the deterministic rewrites yourself, using the precise\n   locators in each machine block.\n3. **Interview** the developer over the questions — only the genuine\n   forks, distilled — and apply their answers.\n4. **Verify** by re-running `analyze` and confirming the divergence is\n   resolved.\n\n`connect-migrate` has no `apply` subcommand by design: the manifest *is*\nthe interface, and you are the executor. The edits happen inside your\nsession, alongside the conversation, so you never break the developer's\nflow by bouncing them through a separate tool.\n\n**Two entry points.** Either:\n\n- **Fresh** — no manifest yet: start at Step 1 (analyze).\n- **Resume** — you've been handed an existing manifest (often one the\n  developer curated — see [Step 3](#step-3-apply-the-rewrites)): skip\n  analysis and start at Step 3, applying exactly what the manifest now\n  lists. Re-run `analyze` only if you suspect the source changed since\n  the manifest was written (Step 3's `id` check catches that).\n\n---\n\n## Prerequisite: install `connect-migrate`\n\nThe CLI lives at \u003Chttps:\u002F\u002Fgithub.com\u002Fapollographql\u002Fconnect-migrate>.\n\nInstall it with the one-line script (drops the binary in `~\u002F.local\u002Fbin`\nby default — no `sudo`; ensure `~\u002F.local\u002Fbin` is on your `PATH`):\n\n```sh\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002Fapollographql\u002Fconnect-migrate\u002Fmain\u002Finstall.sh | sh\n```\n\nOr download a release asset directly with the GitHub CLI (`gh`):\n\n```sh\ngh release download -R apollographql\u002Fconnect-migrate \\\n  -p \"*$(uname -s | tr 'A-Z' 'a-z')-$(uname -m | sed 's\u002Fx86_64\u002Fx64\u002F;s\u002Faarch64\u002Farm64\u002F')*\" \\\n  --dir \u002Ftmp\u002Fcm && mkdir -p ~\u002F.local\u002Fbin && chmod +x \u002Ftmp\u002Fcm\u002Fconnect-migrate-* \\\n  && mv \u002Ftmp\u002Fcm\u002Fconnect-migrate-* ~\u002F.local\u002Fbin\u002Fconnect-migrate\n```\n\nVerify with `connect-migrate --version`. **If you cannot install or run\nit, stop and tell the developer** — do not hand-migrate (see Step 1).\nWindows users download the `.exe` from the Releases page.\n\n---\n\n## Step 1: confirm scope and run the analyzer\n\n*(Resuming from an existing manifest? Skip to [Step 3](#step-3-apply-the-rewrites).)*\n\nOpen by asking the developer where to look. The analyzer needs a project\nroot containing `.graphql` schema files — typically the repository root\nor a `subgraphs\u002F` subdirectory.\n\n> Suggested prompt:\n>\n> > Where should I look for your connector schemas? A directory path\n> > relative to the project root is enough; if you're not sure, the\n> > repository root is usually the right answer.\n\nConfirm the CLI is available (`connect-migrate --version`); if it errors\nwith \"command not found,\" install it per the\n[Prerequisite](#prerequisite-install-connect-migrate) section, then\nretry. If `install.sh` fails (e.g. no binary for the developer's\nplatform), surface its error verbatim and stop — do not build from\nsource unattended.\n\nThen, from the project root, write a **timestamped** manifest so each\nrun is its own durable artifact and never clobbers a prior one:\n\n```sh\nconnect-migrate analyze subgraphs > connect-migrate-manifest-$(date -u +%Y-%m-%dT%H-%M-%SZ).md\n```\n\n`analyze` walks `.graphql` files, finds every `@connect(selection: …)`\ndirective, dual-parses each selection under the v0.3 and v0.4 grammars,\nand writes the differing sites to stdout. Selections that parse\nidentically are not emitted — they need no migration. Use `-o \u003Cpath>`\nto have `analyze` write the file itself, or `--format json` for one\nJSONL record per site if you'd rather consume typed records than parse\nmarkdown.\n\n**The tool's manifest is the source of truth — do not hand-migrate.**\nEvery edit you make comes from a manifest the tool actually produced.\nIf you cannot install or run `connect-migrate` (no published binary for\nyour platform, install failed, no repo access), **stop and tell the\ndeveloper** — do **not** reconstruct the migration from memory, the\ndocs, or web searches. A hand-built migration silently drops\nfortifications, and (see Step 5) a post-upgrade `analyze` will *not*\ncatch the omission — so a from-memory attempt looks clean while quietly\nchanging behavior. The tool is cheap; guessing is not.\n\n---\n\n## Step 2: read the verdict\n\nRead the `\u003C!-- result: … -->` marker at the top of the manifest and\nswitch on it before reading prose. See [Result kinds](#result-kinds)\nfor the full prescription:\n\n- **`empty-scan`** — no `.graphql` files matched. Report the path you\n  passed; ask for a different one. Stop.\n- **`nothing-to-migrate`** — files scanned, no `@connect` directives.\n  Report it; ask whether to look elsewhere. Stop.\n- **`safe-to-upgrade`** — directives present, zero divergence. State the\n  verdict. No fortifications are needed; the only change is the `@link`\n  bump (Step 6) — bump it with the developer's go-ahead.\n- **`safe-after-rewrites`** — divergence exists but every site is\n  mechanical: apply the rewrites (Step 3), verify (Step 5), then bump the\n  `@link` (Step 6). There are **no questions** — a clean bill of health.\n- **`needs-decisions`** — apply the rewrites (Step 3), interview the\n  developer over the questions (Step 4), verify (Step 5), bump (Step 6).\n\nDo not edit source until you've read the verdict; whoever performs the\n`@link` bump, it is the **last** step and only after Step 5 verifies\n(see Steps 5–6).\n\n---\n\n## Step 3: apply the rewrites\n\nThe manifest's **`## Rewrites to apply`** section lists deterministic\n`$.` fortifications. Each is behavior-preserving by construction — it\nrestores a v0.3 field access that v0.4 would otherwise read as a literal\n— so you apply them without asking the developer to adjudicate. Show the\ndiff; a single batched go-ahead before writing is good practice.\n\n**The `## Rewrites to apply` section is the authoritative work list.**\nApply *exactly* the `site v2` blocks present there, using each block's\n`rewrite_to` verbatim — no more, no less. This is what makes the section\neditable: to **skip** a rewrite, delete its block; to **change** a\nreplacement, edit its `rewrite_to`. Never recompute a fortification the\ndeveloper removed or overrode, and never apply one that isn't listed. If\nthe developer curates the list (in the file or by telling you), honor\nthe curated list as-is.\n\n**Resuming from a handed-in manifest?** Before applying, confirm it's\nstill current: each block's `id` should still match a directive in the\nsource at its `file`\u002F`line`. If an `id` no longer resolves, the source\nchanged since the manifest was written — re-run `analyze` (Step 1) and\nre-collect any decisions rather than applying a stale block.\n\nEach rewrite carries a machine block:\n\n```\n\u003C!-- connect-migrate site v2\n  id: 7470d4c2\n  file: itemlibraryserv.graphql\n  line: 99\n  col: 205\n  byte_offset: 8746\n  coordinate: Query.merchantItem\n  kind: key_quoted_flipped_to_literal_string\n  text: \"USD\"\n  source_range: 99..104\n  followed_by: nothing\n  recommendation: keep-v0.3\n  rewrite_to: \"$.\\\"USD\\\"\"\n-->\n```\n\nTo apply one:\n\n1. Open `file:` and navigate to **`line:`\u002F`col:`** (or `byte_offset:`) —\n   the start of the host `@connect(...)` directive. **Do not use\n   `coordinate:` as the locator** — it isn't unique across files; it's\n   informational only, useful to sanity-check you're at the right\n   directive.\n2. Inside that directive's `selection: \"…\"` (or `\"\"\"…\"\"\"`) argument,\n   the token `text` occupies `source_range` (byte offsets into the\n   selection body). Replace it with `rewrite_to` — the exact\n   replacement text.\n   - **Mind the escaping.** `rewrite_to` is shown JSON-escaped in the\n     machine block (e.g. `\"$.\\\"USD\\\"\"`). The value you actually splice\n     is `$.\"USD\"`. In a **block-string** `\"\"\"…\"\"\"` selection, write it\n     **unescaped** — `currencyCode: $.\"USD\"`. Only in a **single-line**\n     `\"…\"` selection do the inner quotes need escaping —\n     `selection: \"currencyCode: $.\\\"USD\\\"\"`. (Over-escaping inside a\n     block string is the most common splice slip.)\n3. **Preserve quoting and indentation.** Keep `\"\"\"` vs `\"` as the source\n   has it. GraphQL block strings strip common leading whitespace at\n   parse time, so re-indent the spliced text to match the surrounding\n   lines for a clean diff; nothing depends on indentation semantically.\n\n### Safety contract\n\nEvery edit must leave the source **at least as good as it was**:\n\n- **Parsing.** Each touched `.graphql` file must still parse under\n  `connect\u002Fv0.4`. The Step 5 re-analyze is the authoritative check.\n- **Behavior.** Write only the fortification in `rewrite_to`. Do not\n  expand the change set.\n- **Formatting.** Untouched lines stay byte-identical; only the token's\n  byte range inside the selection changes.\n- **Recovery.** If a post-apply check fails for a site, revert that\n  edit and report it before declaring success. Partial success is fine\n  only when the developer can see exactly what did and didn't apply.\n\nIf you can't satisfy these for a site, **stop and escalate.**\n\n---\n\n## Step 4: interview the developer over the questions\n\nOnly `needs-decisions` manifests have a non-empty **`## Questions for the\ndeveloper`** section. Each item is a genuine fork the analyzer cannot\nresolve — a structural divergence where the v0.3 and v0.4 readings are\nboth plausible and the right answer depends on the developer's backend.\n\nDistill, don't interrogate. The manifest already groups sites that share\na single decision (`occurrences:` \u002F `(×N)`), so ask **one question per\ndistinct fork**, not one per site. For each:\n\n- Show the `coordinate`, the `file:line` locator, and the windowed\n  selection context the manifest provides.\n- State the two readings plainly and ask which matches their intent. Do\n  not guess or pre-recommend — their REST API knowledge beats your\n  priors.\n- Apply their answer with the same Step 3 safety contract. If they\n  choose the v0.3 reading, fortify with `$.`; if v0.4, leave it.\n\n> Suggested prompt:\n>\n> > `Query.merchantTax` parses differently under v0.4 here: [context].\n> > In v0.3 this read as X; in v0.4 it reads as Y. Which did you intend?\n> > (This one answer covers all N occurrences.)\n\n---\n\n## Step 5: verify — *before* you bump the `@link`\n\nAfter applying the fortifications (and any interview answers), re-run\n`analyze` **while the schemas are still on their original\n`connect\u002Fv0.n` link** — do **not** bump the `@link` yet. Expected state:\n\n- Sites you fortified no longer appear (their `id` is gone from the new\n  manifest).\n- No-op sites may still appear — their source is byte-identical and that\n  is correct.\n- **No new sites** appear. A new divergence means an edit went wrong —\n  revert it and escalate.\n\n**Why the order matters — this is the load-bearing check.** `analyze`\nonly detects *pre-upgrade* divergence: it diffs each schema's linked\n`connect\u002Fv0.n` against v0.4. The moment you bump a schema's `@link` to\nv0.4, its \"from\" side *is* v0.4, so `analyze` has nothing left to compare\nand will report `safe-to-upgrade` **whether or not you applied the\nfortifications**. A post-bump re-analyze therefore proves nothing — it\nwill happily \"pass\" a migration that silently skipped every fortification\n(leaving field accesses as string literals, a behavior change). Verify\n*here*, on the old version, where a missed fortification still shows up.\n\nThen run the project's build check (`cargo check`, `npm run check`,\netc.) and, if a sandbox is available, spot-check a selection or two\nagainst real backend responses.\n\n---\n\n## Step 6: bump the `@link` to `connect\u002Fv0.4`\n\n**Only after Step 5 comes back clean.** In each connector schema, update\nthe existing `@link(url: \"https:\u002F\u002Fspecs.apollo.dev\u002Fconnect\u002Fv0.n\", …)` to\n`connect\u002Fv0.4` in place; leave every other link (federation, etc.)\nuntouched. This is the **last** edit — it's what actually moves the\nschema onto v0.4, which is exactly why it follows verification rather\nthan preceding it.\n\n---\n\n## Step 7: audit trail and summary\n\nThe timestamped manifest is a durable record of what diverged and what\nwas decided. Suggest committing it alongside the source edits:\n\n    chore(connectors): v0.3 → v0.4 migration\n\n    Driven by `connect-migrate analyze`; manifest preserved as the\n    per-decision audit log.\n\nThen summarize:\n\n> Suggested prompt:\n>\n> > Migration complete. K site(s) fortified, M no-op(s) left as-is, Q\n> > question(s) resolved with you. Post-apply `connect-migrate analyze`\n> > reports zero unintended divergence. The manifest is preserved as the\n> > audit log. Ready to commit?\n\nName any appropriate follow-up (run tests, deploy a canary, check\nstaging) explicitly rather than leaving it to the developer to remember.\n\n---\n\n## Manifest format\n\n`connect-migrate analyze` writes a single markdown file, versioned via\nthe leading `\u003C!-- connect-migrate manifest v2 -->` comment. Refuse to act\non a file whose version you don't recognize.\n\n### Header (machine-readable)\n\nRead these comments before parsing prose:\n\n| Comment | Meaning |\n|---------|---------|\n| `result:` | The verdict in one token — see [Result kinds](#result-kinds). Always present. |\n| `files-scanned:` | `.graphql` files visited. |\n| `directives-analyzed:` | `@connect` directives that parsed cleanly under both grammars. |\n| `divergent-sites:` | Total divergent tokens reported. |\n| `auto-fixes:` | Sites in *Rewrites to apply*. |\n| `no-ops:` | Sites in *No action needed*. |\n| `questions:` | Sites in *Questions for the developer*. The number that matters: zero means a clean bill of health. |\n| `upgrade:` | The source `connect\u002Fv0.n` version(s) found across the schemas → the target (`connect\u002Fv0.4`). |\n| `parse-notices:` | Selections that couldn't be diffed (see *Heads up* below). |\n\n### Title, upgrade, and scope\n\nDirectly under the H1 title:\n\n- An **Upgrade** line names the source `connect\u002Fv0.n` version(s) detected\n  across the schemas and the target — e.g. `connect\u002Fv0.2 (7 schemas) ·\n  connect\u002Fv0.3 (5 schemas) → connect\u002Fv0.4`. The \"from\" version is read\n  per schema from its `@link(url: \"...\u002Fconnect\u002Fv0.n\")`, and each\n  schema's selections are diffed at *its own* version against v0.4 — a\n  v0.2 schema is compared as v0.2, not assumed to be v0.3.\n- A **Scope** line names the project root, the schema count, and the\n  directive count, followed by a `Schemas considered:` list of every\n  `.graphql` file the run walked. Read it first: it's how you confirm\n  the run covered the schemas you expected. If an expected schema isn't\n  listed, the path argument was wrong — re-run before trusting the\n  verdict.\n\n### Body: three buckets\n\n- **`## Rewrites to apply`** — one `site v2` machine block per\n  fortification, followed by a human-readable bullet list. Apply these\n  (Step 3).\n- **`## No action needed`** — a token-frequency rollup of the no-op\n  `null`\u002F`true`\u002F`false` sites. Informational; make no edits.\n- **`## Questions for the developer`** — one entry per genuine\n  ambiguity, each with a machine block, a one-line statement of the\n  fork, and a windowed selection context. Empty unless `result` is\n  `needs-decisions`.\n- **`## After applying — switch to connect\u002Fv0.4`** — the closing\n  section, with the ready-to-paste `@link(...\u002Fconnect\u002Fv0.4)` line each\n  migrated schema should adopt. Bump the `@link` **last** — only after\n  the Step 5 verify passes on the *old* version, since once a schema is\n  on v0.4 `analyze` can no longer detect a missed fortification.\n\n### Heads up — selections not analyzed\n\nA `## Heads up — selections not analyzed (N)` section appears only when\nsome `@connect` selection failed to parse, so it couldn't be diffed.\nThis is **non-fatal** — the rest of the manifest stands — but each entry\nneeds a look:\n\n- *parses under the linked spec but not under `connect\u002Fv0.4`* — the\n  selection would break on upgrade; it must be fixed before migrating.\n- *parses under `connect\u002Fv0.4` but not under the linked spec* — it uses\n  syntax newer than the schema declares (a latent inconsistency).\n- *parses under neither* — a pre-existing syntax error, out of scope.\n\nSurface these to the developer; don't try to auto-fix them.\n\n### `site v2` machine block\n\nOne block per site (grouped sites carry `occurrences: N`):\n\n| Field | Notes |\n|-------|-------|\n| `id` | Stable 8-hex content hash; survives line shifts. |\n| `file` | Path relative to `project-root`. |\n| `line`, `col` | 1-indexed start of the host `@connect` directive. **Authoritative locator.** |\n| `byte_offset` | Same location as a byte index. |\n| `coordinate` | `Type.field`. **Informational only** — not unique across files; never use as a locator. |\n| `from` | The `connect\u002Fv0.n` spec the host schema links — the \"from\" side of this site's upgrade. |\n| `kind` | `key_quoted_flipped_to_literal_string`, `key_flipped_to_literal_null`, `key_flipped_to_literal_bool`, `key_field_flipped_to_literal_string`, or a structural kind. |\n| `text` | The token's source text (JSON-escaped). |\n| `source_range` | Byte range `start..end` of the token *within the selection body*. |\n| `followed_by` | `nothing`, `sub_selection`, `key_access`, `method`, `question`. |\n| `recommendation` | `keep-v0.3` (fortify), `embrace-v0.4` (no-op), or `???` (a question). |\n| `rewrite_to` | Present on auto-fixes: the exact replacement text for `text` (JSON-escaped). |\n\n---\n\n## Result kinds\n\n- **`empty-scan`** — zero `.graphql` files visited (`files-scanned: 0`).\n  Almost always a path mistake. Report the path; ask for another. Stop.\n- **`nothing-to-migrate`** — files scanned, no `@connect` directives\n  parsed cleanly (`directives-analyzed: 0`). Usually means no connectors\n  here; but if a `## Heads up` section is present, there *were* `@connect`\n  directives that failed to parse — read those before concluding. Stop.\n- **`safe-to-upgrade`** — directives present, zero divergence\n  (`divergent-sites: 0`). A trustworthy positive verdict, not the\n  absence of one. State it; the only change is the `@link` bump\n  (Step 6), done with the developer's go-ahead.\n- **`safe-after-rewrites`** — divergence exists, but every site is a\n  deterministic fortification or a no-op (`questions: 0`). Apply the\n  rewrites; no developer decisions are required.\n- **`needs-decisions`** — at least one genuinely ambiguous site\n  (`questions: > 0`). Apply the rewrites, then interview the developer\n  over the questions.\n\nEach schema is diffed at its **own** linked `connect\u002Fv0.n` version\nagainst the v0.4 target (see the `Upgrade` line), so a mixed-version\nproject is handled correctly. A schema already on `connect\u002Fv0.4` shows\nzero divergence — it's at the target.\n\n---\n\n## Classification doctrine\n\nWhy the analyzer sorts sites the way it does:\n\n- **Quoted token → fortify (`keep-v0.3`).** A quoted token was a\n  `Key::Quoted` field access in v0.3 with *no* literal-string meaning\n  available. Treating it as a v0.4 literal would silently change\n  behavior. Always `$.\"…\"`. The character content is irrelevant —\n  `\"USD\"` and `\"@odata.nextLink\"` are handled identically.\n- **Bare identifier → fortify (`keep-v0.3`).** Was a `Key::Field`\n  reference in v0.3; same logic. `foo` → `$.foo`.\n- **`followed_by` not `nothing` → fortify.** A literal can't have a\n  field, method, or sub-selection, so a trailing `.x`, `->m()`, or\n  `{ … }` proves the v0.3 field-reference reading was intended.\n- **Bare `null`\u002F`true`\u002F`false` → no-op (`embrace-v0.4`).** A field\n  literally named `null` is implausible; v0.3 resolved it to null via\n  response normalization, so the output coincides with the v0.4 literal.\n  Leave it.\n- **Structural divergence → question (`???`).** Anything the analyzer\n  can't place in the above (a sub-selection\u002Fobject-literal shape flip it\n  can't prove equivalent). These are the only sites that reach the\n  developer.\n\n---\n\n## Boundary conditions\n\n- **`legacy_object_to_lit_object`** — a cosmetic AST shape difference\n  from the unification; same evaluation semantics. `analyze` does not\n  emit these as sites.\n- **`v04_only_accepts`** — the selection uses v0.4-only syntax (e.g. the\n  `…` spread). Already committed to v0.4; nothing to migrate. Not\n  emitted; if you meet one in source, leave it.\n- **`v03_only_accepts`** — should never appear after the parser fix.\n  Treat as a `connect-migrate` bug and file an issue.\n- **Pre-existing syntax errors** — selections that parse under neither\n  grammar. They appear in the `## Heads up` section; surface them for\n  awareness. They predate the migration and are out of scope. Do not\n  repair them.\n- **Non-selection spec changes** — this tool diffs `@connect(selection:)`\n  *mapping* only. Other version-to-version changes (e.g. the v0.2→v0.3\n  arrow-method shape \u002F URI-validation change, or `@connect`\u002F`@source`\n  argument changes) are **not** analyzed. For a multi-version jump\n  (v0.2 → v0.4), consult the connect spec changelog for anything beyond\n  selection mapping.\n\n---\n\n## Failure modes\n\n- **`connect-migrate` not installed.** Run the install command, retry.\n  On an unsupported-platform error from `install.sh`, surface it\n  verbatim and stop; do not build from source unattended.\n- **Source changed between analyze and apply.** Before editing, confirm\n  each site's `id` still appears in a fresh `analyze` run. If an `id` is\n  gone, the source moved under you — regenerate the manifest and restart\n  rather than guess.\n\n---\n\n## Tone\n\n- Read the developer's code; don't speculate. Their REST API knowledge\n  beats your priors.\n- Lead with the source range and the before\u002Fafter. Don't bury the diff.\n- Apply the mechanical rewrites confidently — they're behavior-preserving\n  by construction. Reserve the developer's attention for the genuine\n  questions.\n- For an ambiguous site, ask. One extra message beats a behavior\n  regression.\n- Don't claim success unless the post-apply `analyze` reports zero\n  unintended divergence.\n\n\u003C!-- FOLLOW-UP: this file is duplicated at\n     apollographql\u002Frouter:apollo-federation\u002Fsrc\u002Fconnectors\u002Fmigration\u002Fagent_guide.md\n     which is embedded into the binary via `connect-migrate agent-guide`.\n     Future work: decide between (a) CI-enforced hash match, (b) fetch\n     this file at build time, or (c) drop the embed entirely and rely on\n     the install having put SKILL.md on disk. -->\n",{"data":35,"body":37},{"name":4,"description":6,"license":27,"compatibility":36},"Requires the connect-migrate CLI on PATH (see install.sh). Works with Claude Code and similar AI coding assistants.",{"type":38,"children":39},"root",[40,66,106,128,141,339,351,363,370,375,463,488,498,544,548,559,570,606,650,662,916,944,947,953,967,988,1005,1033,1045,1108,1155,1199,1202,1208,1229,1339,1358,1361,1367,1390,1451,1496,1501,1511,1516,1732,1739,1750,1814,1824,1827,1833,1856,1883,1924,1952,1955,1973,2011,2041,2111,2131,2134,2152,2183,2186,2192,2197,2206,2211,2233,2238,2241,2247,2265,2271,2276,2508,2514,2519,2591,2597,2728,2734,2760,2805,2810,2821,2833,3198,3201,3206,3355,3388,3391,3397,3402,3596,3599,3605,3732,3735,3741,3797,3800,3806,3841],{"type":41,"tag":42,"props":43,"children":45},"element","h1",{"id":44},"apollo-connectors-connectv03-connectv04-migration-skill",[46,49,56,58,64],{"type":47,"value":48},"text","Apollo Connectors ",{"type":41,"tag":50,"props":51,"children":53},"code",{"className":52},[],[54],{"type":47,"value":55},"connect\u002Fv0.3",{"type":47,"value":57}," → ",{"type":41,"tag":50,"props":59,"children":61},{"className":60},[],[62],{"type":47,"value":63},"connect\u002Fv0.4",{"type":47,"value":65}," migration skill",{"type":41,"tag":67,"props":68,"children":69},"p",{},[70,72,78,80,85,87,96,98,104],{"type":47,"value":71},"You are helping a developer upgrade an Apollo Connectors–enabled\nsupergraph from ",{"type":41,"tag":50,"props":73,"children":75},{"className":74},[],[76],{"type":47,"value":77},"@link(url: \"https:\u002F\u002Fspecs.apollo.dev\u002Fconnect\u002Fv0.3\")",{"type":47,"value":79},"\nto ",{"type":41,"tag":50,"props":81,"children":83},{"className":82},[],[84],{"type":47,"value":63},{"type":47,"value":86},". The ",{"type":41,"tag":88,"props":89,"children":93},"a",{"href":90,"rel":91},"https:\u002F\u002Fgithub.com\u002Fapollographql\u002Frouter\u002Fpull\u002F9261",[92],"nofollow",[94],{"type":47,"value":95},"SubSelection\u002FLitObject grammar\nunification",{"type":47,"value":97}," in v0.4\nchanges how a small but important class of ",{"type":41,"tag":50,"props":99,"children":101},{"className":100},[],[102],{"type":47,"value":103},"@connect(selection: …)",{"type":47,"value":105},"\nexpressions parse. Across a 7,375-supergraph customer corpus:",{"type":41,"tag":107,"props":108,"children":109},"ul",{},[110,123],{"type":41,"tag":111,"props":112,"children":113},"li",{},[114,116,121],{"type":47,"value":115},"97.4% of ",{"type":41,"tag":50,"props":117,"children":119},{"className":118},[],[120],{"type":47,"value":103},{"type":47,"value":122}," strings parse identically in v0.3\nand v0.4 — no migration needed.",{"type":41,"tag":111,"props":124,"children":125},{},[126],{"type":47,"value":127},"2.1% have at least one site where the v0.4 reading differs from v0.3.",{"type":41,"tag":67,"props":129,"children":130},{},[131,133,139],{"type":47,"value":132},"Almost every divergent site falls into one of two mechanical\ncategories, and the analyzer resolves both ",{"type":41,"tag":134,"props":135,"children":136},"strong",{},[137],{"type":47,"value":138},"without asking the\ndeveloper anything",{"type":47,"value":140},":",{"type":41,"tag":107,"props":142,"children":143},{},[144,241],{"type":41,"tag":111,"props":145,"children":146},{},[147,173,175,181,183,189,191,197,199,204,206,211,212,217,219,225,227,232,234,239],{"type":41,"tag":134,"props":148,"children":149},{},[150,152,158,160,166,167],{"type":47,"value":151},"Bare ",{"type":41,"tag":50,"props":153,"children":155},{"className":154},[],[156],{"type":47,"value":157},"null",{"type":47,"value":159}," \u002F ",{"type":41,"tag":50,"props":161,"children":163},{"className":162},[],[164],{"type":47,"value":165},"true",{"type":47,"value":159},{"type":41,"tag":50,"props":168,"children":170},{"className":169},[],[171],{"type":47,"value":172},"false",{"type":47,"value":174}," in value position (",{"type":41,"tag":50,"props":176,"children":178},{"className":177},[],[179],{"type":47,"value":180},"status: null",{"type":47,"value":182},",\n",{"type":41,"tag":50,"props":184,"children":186},{"className":185},[],[187],{"type":47,"value":188},"success: true",{"type":47,"value":190},"). In v0.3 the parser looked up a ",{"type":41,"tag":192,"props":193,"children":194},"em",{},[195],{"type":47,"value":196},"field",{"type":47,"value":198}," named\n",{"type":41,"tag":50,"props":200,"children":202},{"className":201},[],[203],{"type":47,"value":157},{"type":47,"value":205},"\u002F",{"type":41,"tag":50,"props":207,"children":209},{"className":208},[],[210],{"type":47,"value":165},{"type":47,"value":205},{"type":41,"tag":50,"props":213,"children":215},{"className":214},[],[216],{"type":47,"value":172},{"type":47,"value":218},", didn't find it, and returned ",{"type":41,"tag":50,"props":220,"children":222},{"className":221},[],[223],{"type":47,"value":224},"None",{"type":47,"value":226},", which\nresponse normalization surfaced as ",{"type":41,"tag":50,"props":228,"children":230},{"className":229},[],[231],{"type":47,"value":157},{"type":47,"value":233}," — accidentally matching the\nliteral the developer meant. v0.4 reads it as a literal directly. The\nobservable output is the same, so this needs ",{"type":41,"tag":134,"props":235,"children":236},{},[237],{"type":47,"value":238},"no edit",{"type":47,"value":240},".",{"type":41,"tag":111,"props":242,"children":243},{},[244,249,251,257,259,265,266,272,274,279,281,286,287,293,295,301,303,309,311,316,317,323,324,330,331,337],{"type":41,"tag":134,"props":245,"children":246},{},[247],{"type":47,"value":248},"Quoted tokens",{"type":47,"value":250}," (",{"type":41,"tag":50,"props":252,"children":254},{"className":253},[],[255],{"type":47,"value":256},"\"@odata.nextLink\"",{"type":47,"value":258},", ",{"type":41,"tag":50,"props":260,"children":262},{"className":261},[],[263],{"type":47,"value":264},"\"USD\"",{"type":47,"value":258},{"type":41,"tag":50,"props":267,"children":269},{"className":268},[],[270],{"type":47,"value":271},"\"0.00\"",{"type":47,"value":273},") and ",{"type":41,"tag":134,"props":275,"children":276},{},[277],{"type":47,"value":278},"bare\nidentifiers",{"type":47,"value":280},". In v0.3 these were ",{"type":41,"tag":192,"props":282,"children":283},{},[284],{"type":47,"value":285},"field accesses",{"type":47,"value":250},{"type":41,"tag":50,"props":288,"children":290},{"className":289},[],[291],{"type":47,"value":292},"Key::Quoted",{"type":47,"value":294}," \u002F\n",{"type":41,"tag":50,"props":296,"children":298},{"className":297},[],[299],{"type":47,"value":300},"Key::Field",{"type":47,"value":302},"); v0.4 silently rereads them as string literals. The\nbehavior-preserving fix is a deterministic ",{"type":41,"tag":50,"props":304,"children":306},{"className":305},[],[307],{"type":47,"value":308},"$.",{"type":47,"value":310}," fortification:\n",{"type":41,"tag":50,"props":312,"children":314},{"className":313},[],[315],{"type":47,"value":264},{"type":47,"value":57},{"type":41,"tag":50,"props":318,"children":320},{"className":319},[],[321],{"type":47,"value":322},"$.\"USD\"",{"type":47,"value":258},{"type":41,"tag":50,"props":325,"children":327},{"className":326},[],[328],{"type":47,"value":329},"soldTo",{"type":47,"value":57},{"type":41,"tag":50,"props":332,"children":334},{"className":333},[],[335],{"type":47,"value":336},"$.soldTo",{"type":47,"value":338},". The analyzer applies these\nmechanically.",{"type":41,"tag":67,"props":340,"children":341},{},[342,344,349],{"type":47,"value":343},"This is the central design rule: ",{"type":41,"tag":134,"props":345,"children":346},{},[347],{"type":47,"value":348},"a quoted token had no string-literal\nmeaning in v0.3, so treating it as a literal would silently change\nbehavior — which a migration must never do on the developer's behalf.",{"type":47,"value":350},"\nWe always preserve the v0.3 reading. If the developer genuinely wants a\nliteral string, that is a deliberate edit they make later, not something\nthe migration guesses at.",{"type":41,"tag":67,"props":352,"children":353},{},[354,356,361],{"type":47,"value":355},"What remains after those two categories is a small residue of genuinely\nambiguous, structural divergences the analyzer cannot resolve. Those —\nand only those — become ",{"type":41,"tag":134,"props":357,"children":358},{},[359],{"type":47,"value":360},"questions for the developer",{"type":47,"value":362},". The whole point\nof this skill is to distill the work down to those questions: apply\neverything mechanical, then interview the developer about the few real\nforks, rather than making them ratify hundreds of non-decisions.",{"type":41,"tag":364,"props":365,"children":367},"h2",{"id":366},"how-this-skill-works",[368],{"type":47,"value":369},"How this skill works",{"type":41,"tag":67,"props":371,"children":372},{},[373],{"type":47,"value":374},"This is a single agent-driven session, not a file you hand a human to\nedit:",{"type":41,"tag":376,"props":377,"children":378},"ol",{},[379,425,435,445],{"type":41,"tag":111,"props":380,"children":381},{},[382,387,389,395,397,402,404,409,411,416,418,423],{"type":41,"tag":134,"props":383,"children":384},{},[385],{"type":47,"value":386},"Analyze",{"type":47,"value":388}," the project. ",{"type":41,"tag":50,"props":390,"children":392},{"className":391},[],[393],{"type":47,"value":394},"connect-migrate analyze",{"type":47,"value":396}," writes a\n",{"type":41,"tag":134,"props":398,"children":399},{},[400],{"type":47,"value":401},"manifest",{"type":47,"value":403}," sorting every divergent site into three buckets:\n",{"type":41,"tag":192,"props":405,"children":406},{},[407],{"type":47,"value":408},"Rewrites to apply",{"type":47,"value":410}," (deterministic), ",{"type":41,"tag":192,"props":412,"children":413},{},[414],{"type":47,"value":415},"No action needed",{"type":47,"value":417}," (no-op), and\n",{"type":41,"tag":192,"props":419,"children":420},{},[421],{"type":47,"value":422},"Questions for the developer",{"type":47,"value":424}," (genuine ambiguity).",{"type":41,"tag":111,"props":426,"children":427},{},[428,433],{"type":41,"tag":134,"props":429,"children":430},{},[431],{"type":47,"value":432},"Apply",{"type":47,"value":434}," the deterministic rewrites yourself, using the precise\nlocators in each machine block.",{"type":41,"tag":111,"props":436,"children":437},{},[438,443],{"type":41,"tag":134,"props":439,"children":440},{},[441],{"type":47,"value":442},"Interview",{"type":47,"value":444}," the developer over the questions — only the genuine\nforks, distilled — and apply their answers.",{"type":41,"tag":111,"props":446,"children":447},{},[448,453,455,461],{"type":41,"tag":134,"props":449,"children":450},{},[451],{"type":47,"value":452},"Verify",{"type":47,"value":454}," by re-running ",{"type":41,"tag":50,"props":456,"children":458},{"className":457},[],[459],{"type":47,"value":460},"analyze",{"type":47,"value":462}," and confirming the divergence is\nresolved.",{"type":41,"tag":67,"props":464,"children":465},{},[466,471,473,479,481,486],{"type":41,"tag":50,"props":467,"children":469},{"className":468},[],[470],{"type":47,"value":4},{"type":47,"value":472}," has no ",{"type":41,"tag":50,"props":474,"children":476},{"className":475},[],[477],{"type":47,"value":478},"apply",{"type":47,"value":480}," subcommand by design: the manifest ",{"type":41,"tag":192,"props":482,"children":483},{},[484],{"type":47,"value":485},"is",{"type":47,"value":487},"\nthe interface, and you are the executor. The edits happen inside your\nsession, alongside the conversation, so you never break the developer's\nflow by bouncing them through a separate tool.",{"type":41,"tag":67,"props":489,"children":490},{},[491,496],{"type":41,"tag":134,"props":492,"children":493},{},[494],{"type":47,"value":495},"Two entry points.",{"type":47,"value":497}," Either:",{"type":41,"tag":107,"props":499,"children":500},{},[501,511],{"type":41,"tag":111,"props":502,"children":503},{},[504,509],{"type":41,"tag":134,"props":505,"children":506},{},[507],{"type":47,"value":508},"Fresh",{"type":47,"value":510}," — no manifest yet: start at Step 1 (analyze).",{"type":41,"tag":111,"props":512,"children":513},{},[514,519,521,527,529,534,536,542],{"type":41,"tag":134,"props":515,"children":516},{},[517],{"type":47,"value":518},"Resume",{"type":47,"value":520}," — you've been handed an existing manifest (often one the\ndeveloper curated — see ",{"type":41,"tag":88,"props":522,"children":524},{"href":523},"#step-3-apply-the-rewrites",[525],{"type":47,"value":526},"Step 3",{"type":47,"value":528},"): skip\nanalysis and start at Step 3, applying exactly what the manifest now\nlists. Re-run ",{"type":41,"tag":50,"props":530,"children":532},{"className":531},[],[533],{"type":47,"value":460},{"type":47,"value":535}," only if you suspect the source changed since\nthe manifest was written (Step 3's ",{"type":41,"tag":50,"props":537,"children":539},{"className":538},[],[540],{"type":47,"value":541},"id",{"type":47,"value":543}," check catches that).",{"type":41,"tag":545,"props":546,"children":547},"hr",{},[],{"type":41,"tag":364,"props":549,"children":551},{"id":550},"prerequisite-install-connect-migrate",[552,554],{"type":47,"value":553},"Prerequisite: install ",{"type":41,"tag":50,"props":555,"children":557},{"className":556},[],[558],{"type":47,"value":4},{"type":41,"tag":67,"props":560,"children":561},{},[562,564,569],{"type":47,"value":563},"The CLI lives at ",{"type":41,"tag":88,"props":565,"children":567},{"href":25,"rel":566},[92],[568],{"type":47,"value":25},{"type":47,"value":240},{"type":41,"tag":67,"props":571,"children":572},{},[573,575,581,583,589,591,596,598,604],{"type":47,"value":574},"Install it with the one-line script (drops the binary in ",{"type":41,"tag":50,"props":576,"children":578},{"className":577},[],[579],{"type":47,"value":580},"~\u002F.local\u002Fbin",{"type":47,"value":582},"\nby default — no ",{"type":41,"tag":50,"props":584,"children":586},{"className":585},[],[587],{"type":47,"value":588},"sudo",{"type":47,"value":590},"; ensure ",{"type":41,"tag":50,"props":592,"children":594},{"className":593},[],[595],{"type":47,"value":580},{"type":47,"value":597}," is on your ",{"type":41,"tag":50,"props":599,"children":601},{"className":600},[],[602],{"type":47,"value":603},"PATH",{"type":47,"value":605},"):",{"type":41,"tag":607,"props":608,"children":613},"pre",{"className":609,"code":610,"language":611,"meta":612,"style":612},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002Fapollographql\u002Fconnect-migrate\u002Fmain\u002Finstall.sh | sh\n","sh","",[614],{"type":41,"tag":50,"props":615,"children":616},{"__ignoreMap":612},[617],{"type":41,"tag":618,"props":619,"children":621},"span",{"class":620,"line":24},"line",[622,628,634,639,645],{"type":41,"tag":618,"props":623,"children":625},{"style":624},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[626],{"type":47,"value":627},"curl",{"type":41,"tag":618,"props":629,"children":631},{"style":630},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[632],{"type":47,"value":633}," -fsSL",{"type":41,"tag":618,"props":635,"children":636},{"style":630},[637],{"type":47,"value":638}," https:\u002F\u002Fraw.githubusercontent.com\u002Fapollographql\u002Fconnect-migrate\u002Fmain\u002Finstall.sh",{"type":41,"tag":618,"props":640,"children":642},{"style":641},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[643],{"type":47,"value":644}," |",{"type":41,"tag":618,"props":646,"children":647},{"style":624},[648],{"type":47,"value":649}," sh\n",{"type":41,"tag":67,"props":651,"children":652},{},[653,655,661],{"type":47,"value":654},"Or download a release asset directly with the GitHub CLI (",{"type":41,"tag":50,"props":656,"children":658},{"className":657},[],[659],{"type":47,"value":660},"gh",{"type":47,"value":605},{"type":41,"tag":607,"props":663,"children":665},{"className":609,"code":664,"language":611,"meta":612,"style":612},"gh release download -R apollographql\u002Fconnect-migrate \\\n  -p \"*$(uname -s | tr 'A-Z' 'a-z')-$(uname -m | sed 's\u002Fx86_64\u002Fx64\u002F;s\u002Faarch64\u002Farm64\u002F')*\" \\\n  --dir \u002Ftmp\u002Fcm && mkdir -p ~\u002F.local\u002Fbin && chmod +x \u002Ftmp\u002Fcm\u002Fconnect-migrate-* \\\n  && mv \u002Ftmp\u002Fcm\u002Fconnect-migrate-* ~\u002F.local\u002Fbin\u002Fconnect-migrate\n",[666],{"type":41,"tag":50,"props":667,"children":668},{"__ignoreMap":612},[669,702,828,889],{"type":41,"tag":618,"props":670,"children":671},{"class":620,"line":24},[672,676,681,686,691,696],{"type":41,"tag":618,"props":673,"children":674},{"style":624},[675],{"type":47,"value":660},{"type":41,"tag":618,"props":677,"children":678},{"style":630},[679],{"type":47,"value":680}," release",{"type":41,"tag":618,"props":682,"children":683},{"style":630},[684],{"type":47,"value":685}," download",{"type":41,"tag":618,"props":687,"children":688},{"style":630},[689],{"type":47,"value":690}," -R",{"type":41,"tag":618,"props":692,"children":693},{"style":630},[694],{"type":47,"value":695}," apollographql\u002Fconnect-migrate",{"type":41,"tag":618,"props":697,"children":699},{"style":698},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[700],{"type":47,"value":701}," \\\n",{"type":41,"tag":618,"props":703,"children":705},{"class":620,"line":704},2,[706,711,716,721,726,731,736,741,746,751,756,761,765,770,775,780,784,788,793,797,802,806,811,815,819,824],{"type":41,"tag":618,"props":707,"children":708},{"style":630},[709],{"type":47,"value":710},"  -p",{"type":41,"tag":618,"props":712,"children":713},{"style":641},[714],{"type":47,"value":715}," \"",{"type":41,"tag":618,"props":717,"children":718},{"style":630},[719],{"type":47,"value":720},"*",{"type":41,"tag":618,"props":722,"children":723},{"style":641},[724],{"type":47,"value":725},"$(",{"type":41,"tag":618,"props":727,"children":728},{"style":624},[729],{"type":47,"value":730},"uname",{"type":41,"tag":618,"props":732,"children":733},{"style":630},[734],{"type":47,"value":735}," -s ",{"type":41,"tag":618,"props":737,"children":738},{"style":641},[739],{"type":47,"value":740},"|",{"type":41,"tag":618,"props":742,"children":743},{"style":624},[744],{"type":47,"value":745}," tr",{"type":41,"tag":618,"props":747,"children":748},{"style":641},[749],{"type":47,"value":750}," '",{"type":41,"tag":618,"props":752,"children":753},{"style":630},[754],{"type":47,"value":755},"A-Z",{"type":41,"tag":618,"props":757,"children":758},{"style":641},[759],{"type":47,"value":760},"'",{"type":41,"tag":618,"props":762,"children":763},{"style":641},[764],{"type":47,"value":750},{"type":41,"tag":618,"props":766,"children":767},{"style":630},[768],{"type":47,"value":769},"a-z",{"type":41,"tag":618,"props":771,"children":772},{"style":641},[773],{"type":47,"value":774},"')",{"type":41,"tag":618,"props":776,"children":777},{"style":630},[778],{"type":47,"value":779},"-",{"type":41,"tag":618,"props":781,"children":782},{"style":641},[783],{"type":47,"value":725},{"type":41,"tag":618,"props":785,"children":786},{"style":624},[787],{"type":47,"value":730},{"type":41,"tag":618,"props":789,"children":790},{"style":630},[791],{"type":47,"value":792}," -m ",{"type":41,"tag":618,"props":794,"children":795},{"style":641},[796],{"type":47,"value":740},{"type":41,"tag":618,"props":798,"children":799},{"style":624},[800],{"type":47,"value":801}," sed",{"type":41,"tag":618,"props":803,"children":804},{"style":641},[805],{"type":47,"value":750},{"type":41,"tag":618,"props":807,"children":808},{"style":630},[809],{"type":47,"value":810},"s\u002Fx86_64\u002Fx64\u002F;s\u002Faarch64\u002Farm64\u002F",{"type":41,"tag":618,"props":812,"children":813},{"style":641},[814],{"type":47,"value":774},{"type":41,"tag":618,"props":816,"children":817},{"style":630},[818],{"type":47,"value":720},{"type":41,"tag":618,"props":820,"children":821},{"style":641},[822],{"type":47,"value":823},"\"",{"type":41,"tag":618,"props":825,"children":826},{"style":698},[827],{"type":47,"value":701},{"type":41,"tag":618,"props":829,"children":831},{"class":620,"line":830},3,[832,837,842,847,852,857,862,866,871,876,881,885],{"type":41,"tag":618,"props":833,"children":834},{"style":630},[835],{"type":47,"value":836},"  --dir",{"type":41,"tag":618,"props":838,"children":839},{"style":630},[840],{"type":47,"value":841}," \u002Ftmp\u002Fcm",{"type":41,"tag":618,"props":843,"children":844},{"style":641},[845],{"type":47,"value":846}," &&",{"type":41,"tag":618,"props":848,"children":849},{"style":624},[850],{"type":47,"value":851}," mkdir",{"type":41,"tag":618,"props":853,"children":854},{"style":630},[855],{"type":47,"value":856}," -p",{"type":41,"tag":618,"props":858,"children":859},{"style":630},[860],{"type":47,"value":861}," ~\u002F.local\u002Fbin",{"type":41,"tag":618,"props":863,"children":864},{"style":641},[865],{"type":47,"value":846},{"type":41,"tag":618,"props":867,"children":868},{"style":624},[869],{"type":47,"value":870}," chmod",{"type":41,"tag":618,"props":872,"children":873},{"style":630},[874],{"type":47,"value":875}," +x",{"type":41,"tag":618,"props":877,"children":878},{"style":630},[879],{"type":47,"value":880}," \u002Ftmp\u002Fcm\u002Fconnect-migrate-",{"type":41,"tag":618,"props":882,"children":883},{"style":698},[884],{"type":47,"value":720},{"type":41,"tag":618,"props":886,"children":887},{"style":698},[888],{"type":47,"value":701},{"type":41,"tag":618,"props":890,"children":892},{"class":620,"line":891},4,[893,898,903,907,911],{"type":41,"tag":618,"props":894,"children":895},{"style":641},[896],{"type":47,"value":897},"  &&",{"type":41,"tag":618,"props":899,"children":900},{"style":624},[901],{"type":47,"value":902}," mv",{"type":41,"tag":618,"props":904,"children":905},{"style":630},[906],{"type":47,"value":880},{"type":41,"tag":618,"props":908,"children":909},{"style":698},[910],{"type":47,"value":720},{"type":41,"tag":618,"props":912,"children":913},{"style":630},[914],{"type":47,"value":915}," ~\u002F.local\u002Fbin\u002Fconnect-migrate\n",{"type":41,"tag":67,"props":917,"children":918},{},[919,921,927,929,934,936,942],{"type":47,"value":920},"Verify with ",{"type":41,"tag":50,"props":922,"children":924},{"className":923},[],[925],{"type":47,"value":926},"connect-migrate --version",{"type":47,"value":928},". ",{"type":41,"tag":134,"props":930,"children":931},{},[932],{"type":47,"value":933},"If you cannot install or run\nit, stop and tell the developer",{"type":47,"value":935}," — do not hand-migrate (see Step 1).\nWindows users download the ",{"type":41,"tag":50,"props":937,"children":939},{"className":938},[],[940],{"type":47,"value":941},".exe",{"type":47,"value":943}," from the Releases page.",{"type":41,"tag":545,"props":945,"children":946},{},[],{"type":41,"tag":364,"props":948,"children":950},{"id":949},"step-1-confirm-scope-and-run-the-analyzer",[951],{"type":47,"value":952},"Step 1: confirm scope and run the analyzer",{"type":41,"tag":67,"props":954,"children":955},{},[956],{"type":41,"tag":192,"props":957,"children":958},{},[959,961,965],{"type":47,"value":960},"(Resuming from an existing manifest? Skip to ",{"type":41,"tag":88,"props":962,"children":963},{"href":523},[964],{"type":47,"value":526},{"type":47,"value":966},".)",{"type":41,"tag":67,"props":968,"children":969},{},[970,972,978,980,986],{"type":47,"value":971},"Open by asking the developer where to look. The analyzer needs a project\nroot containing ",{"type":41,"tag":50,"props":973,"children":975},{"className":974},[],[976],{"type":47,"value":977},".graphql",{"type":47,"value":979}," schema files — typically the repository root\nor a ",{"type":41,"tag":50,"props":981,"children":983},{"className":982},[],[984],{"type":47,"value":985},"subgraphs\u002F",{"type":47,"value":987}," subdirectory.",{"type":41,"tag":989,"props":990,"children":991},"blockquote",{},[992,997],{"type":41,"tag":67,"props":993,"children":994},{},[995],{"type":47,"value":996},"Suggested prompt:",{"type":41,"tag":989,"props":998,"children":999},{},[1000],{"type":41,"tag":67,"props":1001,"children":1002},{},[1003],{"type":47,"value":1004},"Where should I look for your connector schemas? A directory path\nrelative to the project root is enough; if you're not sure, the\nrepository root is usually the right answer.",{"type":41,"tag":67,"props":1006,"children":1007},{},[1008,1010,1015,1017,1023,1025,1031],{"type":47,"value":1009},"Confirm the CLI is available (",{"type":41,"tag":50,"props":1011,"children":1013},{"className":1012},[],[1014],{"type":47,"value":926},{"type":47,"value":1016},"); if it errors\nwith \"command not found,\" install it per the\n",{"type":41,"tag":88,"props":1018,"children":1020},{"href":1019},"#prerequisite-install-connect-migrate",[1021],{"type":47,"value":1022},"Prerequisite",{"type":47,"value":1024}," section, then\nretry. If ",{"type":41,"tag":50,"props":1026,"children":1028},{"className":1027},[],[1029],{"type":47,"value":1030},"install.sh",{"type":47,"value":1032}," fails (e.g. no binary for the developer's\nplatform), surface its error verbatim and stop — do not build from\nsource unattended.",{"type":41,"tag":67,"props":1034,"children":1035},{},[1036,1038,1043],{"type":47,"value":1037},"Then, from the project root, write a ",{"type":41,"tag":134,"props":1039,"children":1040},{},[1041],{"type":47,"value":1042},"timestamped",{"type":47,"value":1044}," manifest so each\nrun is its own durable artifact and never clobbers a prior one:",{"type":41,"tag":607,"props":1046,"children":1048},{"className":609,"code":1047,"language":611,"meta":612,"style":612},"connect-migrate analyze subgraphs > connect-migrate-manifest-$(date -u +%Y-%m-%dT%H-%M-%SZ).md\n",[1049],{"type":41,"tag":50,"props":1050,"children":1051},{"__ignoreMap":612},[1052],{"type":41,"tag":618,"props":1053,"children":1054},{"class":620,"line":24},[1055,1059,1064,1069,1074,1079,1083,1088,1093,1098,1103],{"type":41,"tag":618,"props":1056,"children":1057},{"style":624},[1058],{"type":47,"value":4},{"type":41,"tag":618,"props":1060,"children":1061},{"style":630},[1062],{"type":47,"value":1063}," analyze",{"type":41,"tag":618,"props":1065,"children":1066},{"style":630},[1067],{"type":47,"value":1068}," subgraphs",{"type":41,"tag":618,"props":1070,"children":1071},{"style":641},[1072],{"type":47,"value":1073}," >",{"type":41,"tag":618,"props":1075,"children":1076},{"style":630},[1077],{"type":47,"value":1078}," connect-migrate-manifest-",{"type":41,"tag":618,"props":1080,"children":1081},{"style":641},[1082],{"type":47,"value":725},{"type":41,"tag":618,"props":1084,"children":1085},{"style":624},[1086],{"type":47,"value":1087},"date",{"type":41,"tag":618,"props":1089,"children":1090},{"style":630},[1091],{"type":47,"value":1092}," -u",{"type":41,"tag":618,"props":1094,"children":1095},{"style":630},[1096],{"type":47,"value":1097}," +%Y-%m-%dT%H-%M-%SZ",{"type":41,"tag":618,"props":1099,"children":1100},{"style":641},[1101],{"type":47,"value":1102},")",{"type":41,"tag":618,"props":1104,"children":1105},{"style":630},[1106],{"type":47,"value":1107},".md\n",{"type":41,"tag":67,"props":1109,"children":1110},{},[1111,1116,1118,1123,1125,1130,1132,1138,1140,1145,1147,1153],{"type":41,"tag":50,"props":1112,"children":1114},{"className":1113},[],[1115],{"type":47,"value":460},{"type":47,"value":1117}," walks ",{"type":41,"tag":50,"props":1119,"children":1121},{"className":1120},[],[1122],{"type":47,"value":977},{"type":47,"value":1124}," files, finds every ",{"type":41,"tag":50,"props":1126,"children":1128},{"className":1127},[],[1129],{"type":47,"value":103},{"type":47,"value":1131},"\ndirective, dual-parses each selection under the v0.3 and v0.4 grammars,\nand writes the differing sites to stdout. Selections that parse\nidentically are not emitted — they need no migration. Use ",{"type":41,"tag":50,"props":1133,"children":1135},{"className":1134},[],[1136],{"type":47,"value":1137},"-o \u003Cpath>",{"type":47,"value":1139},"\nto have ",{"type":41,"tag":50,"props":1141,"children":1143},{"className":1142},[],[1144],{"type":47,"value":460},{"type":47,"value":1146}," write the file itself, or ",{"type":41,"tag":50,"props":1148,"children":1150},{"className":1149},[],[1151],{"type":47,"value":1152},"--format json",{"type":47,"value":1154}," for one\nJSONL record per site if you'd rather consume typed records than parse\nmarkdown.",{"type":41,"tag":67,"props":1156,"children":1157},{},[1158,1163,1165,1170,1172,1177,1179,1184,1186,1191,1193,1197],{"type":41,"tag":134,"props":1159,"children":1160},{},[1161],{"type":47,"value":1162},"The tool's manifest is the source of truth — do not hand-migrate.",{"type":47,"value":1164},"\nEvery edit you make comes from a manifest the tool actually produced.\nIf you cannot install or run ",{"type":41,"tag":50,"props":1166,"children":1168},{"className":1167},[],[1169],{"type":47,"value":4},{"type":47,"value":1171}," (no published binary for\nyour platform, install failed, no repo access), ",{"type":41,"tag":134,"props":1173,"children":1174},{},[1175],{"type":47,"value":1176},"stop and tell the\ndeveloper",{"type":47,"value":1178}," — do ",{"type":41,"tag":134,"props":1180,"children":1181},{},[1182],{"type":47,"value":1183},"not",{"type":47,"value":1185}," reconstruct the migration from memory, the\ndocs, or web searches. A hand-built migration silently drops\nfortifications, and (see Step 5) a post-upgrade ",{"type":41,"tag":50,"props":1187,"children":1189},{"className":1188},[],[1190],{"type":47,"value":460},{"type":47,"value":1192}," will ",{"type":41,"tag":192,"props":1194,"children":1195},{},[1196],{"type":47,"value":1183},{"type":47,"value":1198},"\ncatch the omission — so a from-memory attempt looks clean while quietly\nchanging behavior. The tool is cheap; guessing is not.",{"type":41,"tag":545,"props":1200,"children":1201},{},[],{"type":41,"tag":364,"props":1203,"children":1205},{"id":1204},"step-2-read-the-verdict",[1206],{"type":47,"value":1207},"Step 2: read the verdict",{"type":41,"tag":67,"props":1209,"children":1210},{},[1211,1213,1219,1221,1227],{"type":47,"value":1212},"Read the ",{"type":41,"tag":50,"props":1214,"children":1216},{"className":1215},[],[1217],{"type":47,"value":1218},"\u003C!-- result: … -->",{"type":47,"value":1220}," marker at the top of the manifest and\nswitch on it before reading prose. See ",{"type":41,"tag":88,"props":1222,"children":1224},{"href":1223},"#result-kinds",[1225],{"type":47,"value":1226},"Result kinds",{"type":47,"value":1228},"\nfor the full prescription:",{"type":41,"tag":107,"props":1230,"children":1231},{},[1232,1253,1275,1297,1325],{"type":41,"tag":111,"props":1233,"children":1234},{},[1235,1244,1246,1251],{"type":41,"tag":134,"props":1236,"children":1237},{},[1238],{"type":41,"tag":50,"props":1239,"children":1241},{"className":1240},[],[1242],{"type":47,"value":1243},"empty-scan",{"type":47,"value":1245}," — no ",{"type":41,"tag":50,"props":1247,"children":1249},{"className":1248},[],[1250],{"type":47,"value":977},{"type":47,"value":1252}," files matched. Report the path you\npassed; ask for a different one. Stop.",{"type":41,"tag":111,"props":1254,"children":1255},{},[1256,1265,1267,1273],{"type":41,"tag":134,"props":1257,"children":1258},{},[1259],{"type":41,"tag":50,"props":1260,"children":1262},{"className":1261},[],[1263],{"type":47,"value":1264},"nothing-to-migrate",{"type":47,"value":1266}," — files scanned, no ",{"type":41,"tag":50,"props":1268,"children":1270},{"className":1269},[],[1271],{"type":47,"value":1272},"@connect",{"type":47,"value":1274}," directives.\nReport it; ask whether to look elsewhere. Stop.",{"type":41,"tag":111,"props":1276,"children":1277},{},[1278,1287,1289,1295],{"type":41,"tag":134,"props":1279,"children":1280},{},[1281],{"type":41,"tag":50,"props":1282,"children":1284},{"className":1283},[],[1285],{"type":47,"value":1286},"safe-to-upgrade",{"type":47,"value":1288}," — directives present, zero divergence. State the\nverdict. No fortifications are needed; the only change is the ",{"type":41,"tag":50,"props":1290,"children":1292},{"className":1291},[],[1293],{"type":47,"value":1294},"@link",{"type":47,"value":1296},"\nbump (Step 6) — bump it with the developer's go-ahead.",{"type":41,"tag":111,"props":1298,"children":1299},{},[1300,1309,1311,1316,1318,1323],{"type":41,"tag":134,"props":1301,"children":1302},{},[1303],{"type":41,"tag":50,"props":1304,"children":1306},{"className":1305},[],[1307],{"type":47,"value":1308},"safe-after-rewrites",{"type":47,"value":1310}," — divergence exists but every site is\nmechanical: apply the rewrites (Step 3), verify (Step 5), then bump the\n",{"type":41,"tag":50,"props":1312,"children":1314},{"className":1313},[],[1315],{"type":47,"value":1294},{"type":47,"value":1317}," (Step 6). There are ",{"type":41,"tag":134,"props":1319,"children":1320},{},[1321],{"type":47,"value":1322},"no questions",{"type":47,"value":1324}," — a clean bill of health.",{"type":41,"tag":111,"props":1326,"children":1327},{},[1328,1337],{"type":41,"tag":134,"props":1329,"children":1330},{},[1331],{"type":41,"tag":50,"props":1332,"children":1334},{"className":1333},[],[1335],{"type":47,"value":1336},"needs-decisions",{"type":47,"value":1338}," — apply the rewrites (Step 3), interview the\ndeveloper over the questions (Step 4), verify (Step 5), bump (Step 6).",{"type":41,"tag":67,"props":1340,"children":1341},{},[1342,1344,1349,1351,1356],{"type":47,"value":1343},"Do not edit source until you've read the verdict; whoever performs the\n",{"type":41,"tag":50,"props":1345,"children":1347},{"className":1346},[],[1348],{"type":47,"value":1294},{"type":47,"value":1350}," bump, it is the ",{"type":41,"tag":134,"props":1352,"children":1353},{},[1354],{"type":47,"value":1355},"last",{"type":47,"value":1357}," step and only after Step 5 verifies\n(see Steps 5–6).",{"type":41,"tag":545,"props":1359,"children":1360},{},[],{"type":41,"tag":364,"props":1362,"children":1364},{"id":1363},"step-3-apply-the-rewrites",[1365],{"type":47,"value":1366},"Step 3: apply the rewrites",{"type":41,"tag":67,"props":1368,"children":1369},{},[1370,1372,1381,1383,1388],{"type":47,"value":1371},"The manifest's ",{"type":41,"tag":134,"props":1373,"children":1374},{},[1375],{"type":41,"tag":50,"props":1376,"children":1378},{"className":1377},[],[1379],{"type":47,"value":1380},"## Rewrites to apply",{"type":47,"value":1382}," section lists deterministic\n",{"type":41,"tag":50,"props":1384,"children":1386},{"className":1385},[],[1387],{"type":47,"value":308},{"type":47,"value":1389}," fortifications. Each is behavior-preserving by construction — it\nrestores a v0.3 field access that v0.4 would otherwise read as a literal\n— so you apply them without asking the developer to adjudicate. Show the\ndiff; a single batched go-ahead before writing is good practice.",{"type":41,"tag":67,"props":1391,"children":1392},{},[1393,1405,1407,1412,1414,1420,1422,1428,1430,1435,1437,1442,1444,1449],{"type":41,"tag":134,"props":1394,"children":1395},{},[1396,1398,1403],{"type":47,"value":1397},"The ",{"type":41,"tag":50,"props":1399,"children":1401},{"className":1400},[],[1402],{"type":47,"value":1380},{"type":47,"value":1404}," section is the authoritative work list.",{"type":47,"value":1406},"\nApply ",{"type":41,"tag":192,"props":1408,"children":1409},{},[1410],{"type":47,"value":1411},"exactly",{"type":47,"value":1413}," the ",{"type":41,"tag":50,"props":1415,"children":1417},{"className":1416},[],[1418],{"type":47,"value":1419},"site v2",{"type":47,"value":1421}," blocks present there, using each block's\n",{"type":41,"tag":50,"props":1423,"children":1425},{"className":1424},[],[1426],{"type":47,"value":1427},"rewrite_to",{"type":47,"value":1429}," verbatim — no more, no less. This is what makes the section\neditable: to ",{"type":41,"tag":134,"props":1431,"children":1432},{},[1433],{"type":47,"value":1434},"skip",{"type":47,"value":1436}," a rewrite, delete its block; to ",{"type":41,"tag":134,"props":1438,"children":1439},{},[1440],{"type":47,"value":1441},"change",{"type":47,"value":1443}," a\nreplacement, edit its ",{"type":41,"tag":50,"props":1445,"children":1447},{"className":1446},[],[1448],{"type":47,"value":1427},{"type":47,"value":1450},". Never recompute a fortification the\ndeveloper removed or overrode, and never apply one that isn't listed. If\nthe developer curates the list (in the file or by telling you), honor\nthe curated list as-is.",{"type":41,"tag":67,"props":1452,"children":1453},{},[1454,1459,1461,1466,1468,1474,1475,1480,1482,1487,1489,1494],{"type":41,"tag":134,"props":1455,"children":1456},{},[1457],{"type":47,"value":1458},"Resuming from a handed-in manifest?",{"type":47,"value":1460}," Before applying, confirm it's\nstill current: each block's ",{"type":41,"tag":50,"props":1462,"children":1464},{"className":1463},[],[1465],{"type":47,"value":541},{"type":47,"value":1467}," should still match a directive in the\nsource at its ",{"type":41,"tag":50,"props":1469,"children":1471},{"className":1470},[],[1472],{"type":47,"value":1473},"file",{"type":47,"value":205},{"type":41,"tag":50,"props":1476,"children":1478},{"className":1477},[],[1479],{"type":47,"value":620},{"type":47,"value":1481},". If an ",{"type":41,"tag":50,"props":1483,"children":1485},{"className":1484},[],[1486],{"type":47,"value":541},{"type":47,"value":1488}," no longer resolves, the source\nchanged since the manifest was written — re-run ",{"type":41,"tag":50,"props":1490,"children":1492},{"className":1491},[],[1493],{"type":47,"value":460},{"type":47,"value":1495}," (Step 1) and\nre-collect any decisions rather than applying a stale block.",{"type":41,"tag":67,"props":1497,"children":1498},{},[1499],{"type":47,"value":1500},"Each rewrite carries a machine block:",{"type":41,"tag":607,"props":1502,"children":1506},{"className":1503,"code":1505,"language":47},[1504],"language-text","\u003C!-- connect-migrate site v2\n  id: 7470d4c2\n  file: itemlibraryserv.graphql\n  line: 99\n  col: 205\n  byte_offset: 8746\n  coordinate: Query.merchantItem\n  kind: key_quoted_flipped_to_literal_string\n  text: \"USD\"\n  source_range: 99..104\n  followed_by: nothing\n  recommendation: keep-v0.3\n  rewrite_to: \"$.\\\"USD\\\"\"\n-->\n",[1507],{"type":41,"tag":50,"props":1508,"children":1509},{"__ignoreMap":612},[1510],{"type":47,"value":1505},{"type":41,"tag":67,"props":1512,"children":1513},{},[1514],{"type":47,"value":1515},"To apply one:",{"type":41,"tag":376,"props":1517,"children":1518},{},[1519,1581,1707],{"type":41,"tag":111,"props":1520,"children":1521},{},[1522,1524,1530,1532,1548,1550,1556,1558,1564,1566,1579],{"type":47,"value":1523},"Open ",{"type":41,"tag":50,"props":1525,"children":1527},{"className":1526},[],[1528],{"type":47,"value":1529},"file:",{"type":47,"value":1531}," and navigate to ",{"type":41,"tag":134,"props":1533,"children":1534},{},[1535,1541,1542],{"type":41,"tag":50,"props":1536,"children":1538},{"className":1537},[],[1539],{"type":47,"value":1540},"line:",{"type":47,"value":205},{"type":41,"tag":50,"props":1543,"children":1545},{"className":1544},[],[1546],{"type":47,"value":1547},"col:",{"type":47,"value":1549}," (or ",{"type":41,"tag":50,"props":1551,"children":1553},{"className":1552},[],[1554],{"type":47,"value":1555},"byte_offset:",{"type":47,"value":1557},") —\nthe start of the host ",{"type":41,"tag":50,"props":1559,"children":1561},{"className":1560},[],[1562],{"type":47,"value":1563},"@connect(...)",{"type":47,"value":1565}," directive. ",{"type":41,"tag":134,"props":1567,"children":1568},{},[1569,1571,1577],{"type":47,"value":1570},"Do not use\n",{"type":41,"tag":50,"props":1572,"children":1574},{"className":1573},[],[1575],{"type":47,"value":1576},"coordinate:",{"type":47,"value":1578}," as the locator",{"type":47,"value":1580}," — it isn't unique across files; it's\ninformational only, useful to sanity-check you're at the right\ndirective.",{"type":41,"tag":111,"props":1582,"children":1583},{},[1584,1586,1592,1593,1599,1601,1606,1608,1614,1616,1621,1623],{"type":47,"value":1585},"Inside that directive's ",{"type":41,"tag":50,"props":1587,"children":1589},{"className":1588},[],[1590],{"type":47,"value":1591},"selection: \"…\"",{"type":47,"value":1549},{"type":41,"tag":50,"props":1594,"children":1596},{"className":1595},[],[1597],{"type":47,"value":1598},"\"\"\"…\"\"\"",{"type":47,"value":1600},") argument,\nthe token ",{"type":41,"tag":50,"props":1602,"children":1604},{"className":1603},[],[1605],{"type":47,"value":47},{"type":47,"value":1607}," occupies ",{"type":41,"tag":50,"props":1609,"children":1611},{"className":1610},[],[1612],{"type":47,"value":1613},"source_range",{"type":47,"value":1615}," (byte offsets into the\nselection body). Replace it with ",{"type":41,"tag":50,"props":1617,"children":1619},{"className":1618},[],[1620],{"type":47,"value":1427},{"type":47,"value":1622}," — the exact\nreplacement text.\n",{"type":41,"tag":107,"props":1624,"children":1625},{},[1626],{"type":41,"tag":111,"props":1627,"children":1628},{},[1629,1634,1636,1641,1643,1649,1651,1656,1658,1663,1664,1669,1671,1676,1678,1684,1686,1691,1697,1699,1705],{"type":41,"tag":134,"props":1630,"children":1631},{},[1632],{"type":47,"value":1633},"Mind the escaping.",{"type":47,"value":1635}," ",{"type":41,"tag":50,"props":1637,"children":1639},{"className":1638},[],[1640],{"type":47,"value":1427},{"type":47,"value":1642}," is shown JSON-escaped in the\nmachine block (e.g. ",{"type":41,"tag":50,"props":1644,"children":1646},{"className":1645},[],[1647],{"type":47,"value":1648},"\"$.\\\"USD\\\"\"",{"type":47,"value":1650},"). The value you actually splice\nis ",{"type":41,"tag":50,"props":1652,"children":1654},{"className":1653},[],[1655],{"type":47,"value":322},{"type":47,"value":1657},". In a ",{"type":41,"tag":134,"props":1659,"children":1660},{},[1661],{"type":47,"value":1662},"block-string",{"type":47,"value":1635},{"type":41,"tag":50,"props":1665,"children":1667},{"className":1666},[],[1668],{"type":47,"value":1598},{"type":47,"value":1670}," selection, write it\n",{"type":41,"tag":134,"props":1672,"children":1673},{},[1674],{"type":47,"value":1675},"unescaped",{"type":47,"value":1677}," — ",{"type":41,"tag":50,"props":1679,"children":1681},{"className":1680},[],[1682],{"type":47,"value":1683},"currencyCode: $.\"USD\"",{"type":47,"value":1685},". Only in a ",{"type":41,"tag":134,"props":1687,"children":1688},{},[1689],{"type":47,"value":1690},"single-line",{"type":41,"tag":50,"props":1692,"children":1694},{"className":1693},[],[1695],{"type":47,"value":1696},"\"…\"",{"type":47,"value":1698}," selection do the inner quotes need escaping —\n",{"type":41,"tag":50,"props":1700,"children":1702},{"className":1701},[],[1703],{"type":47,"value":1704},"selection: \"currencyCode: $.\\\"USD\\\"\"",{"type":47,"value":1706},". (Over-escaping inside a\nblock string is the most common splice slip.)",{"type":41,"tag":111,"props":1708,"children":1709},{},[1710,1715,1717,1723,1725,1730],{"type":41,"tag":134,"props":1711,"children":1712},{},[1713],{"type":47,"value":1714},"Preserve quoting and indentation.",{"type":47,"value":1716}," Keep ",{"type":41,"tag":50,"props":1718,"children":1720},{"className":1719},[],[1721],{"type":47,"value":1722},"\"\"\"",{"type":47,"value":1724}," vs ",{"type":41,"tag":50,"props":1726,"children":1728},{"className":1727},[],[1729],{"type":47,"value":823},{"type":47,"value":1731}," as the source\nhas it. GraphQL block strings strip common leading whitespace at\nparse time, so re-indent the spliced text to match the surrounding\nlines for a clean diff; nothing depends on indentation semantically.",{"type":41,"tag":1733,"props":1734,"children":1736},"h3",{"id":1735},"safety-contract",[1737],{"type":47,"value":1738},"Safety contract",{"type":41,"tag":67,"props":1740,"children":1741},{},[1742,1744,1749],{"type":47,"value":1743},"Every edit must leave the source ",{"type":41,"tag":134,"props":1745,"children":1746},{},[1747],{"type":47,"value":1748},"at least as good as it was",{"type":47,"value":140},{"type":41,"tag":107,"props":1751,"children":1752},{},[1753,1777,1794,1804],{"type":41,"tag":111,"props":1754,"children":1755},{},[1756,1761,1763,1768,1770,1775],{"type":41,"tag":134,"props":1757,"children":1758},{},[1759],{"type":47,"value":1760},"Parsing.",{"type":47,"value":1762}," Each touched ",{"type":41,"tag":50,"props":1764,"children":1766},{"className":1765},[],[1767],{"type":47,"value":977},{"type":47,"value":1769}," file must still parse under\n",{"type":41,"tag":50,"props":1771,"children":1773},{"className":1772},[],[1774],{"type":47,"value":63},{"type":47,"value":1776},". The Step 5 re-analyze is the authoritative check.",{"type":41,"tag":111,"props":1778,"children":1779},{},[1780,1785,1787,1792],{"type":41,"tag":134,"props":1781,"children":1782},{},[1783],{"type":47,"value":1784},"Behavior.",{"type":47,"value":1786}," Write only the fortification in ",{"type":41,"tag":50,"props":1788,"children":1790},{"className":1789},[],[1791],{"type":47,"value":1427},{"type":47,"value":1793},". Do not\nexpand the change set.",{"type":41,"tag":111,"props":1795,"children":1796},{},[1797,1802],{"type":41,"tag":134,"props":1798,"children":1799},{},[1800],{"type":47,"value":1801},"Formatting.",{"type":47,"value":1803}," Untouched lines stay byte-identical; only the token's\nbyte range inside the selection changes.",{"type":41,"tag":111,"props":1805,"children":1806},{},[1807,1812],{"type":41,"tag":134,"props":1808,"children":1809},{},[1810],{"type":47,"value":1811},"Recovery.",{"type":47,"value":1813}," If a post-apply check fails for a site, revert that\nedit and report it before declaring success. Partial success is fine\nonly when the developer can see exactly what did and didn't apply.",{"type":41,"tag":67,"props":1815,"children":1816},{},[1817,1819],{"type":47,"value":1818},"If you can't satisfy these for a site, ",{"type":41,"tag":134,"props":1820,"children":1821},{},[1822],{"type":47,"value":1823},"stop and escalate.",{"type":41,"tag":545,"props":1825,"children":1826},{},[],{"type":41,"tag":364,"props":1828,"children":1830},{"id":1829},"step-4-interview-the-developer-over-the-questions",[1831],{"type":47,"value":1832},"Step 4: interview the developer over the questions",{"type":41,"tag":67,"props":1834,"children":1835},{},[1836,1838,1843,1845,1854],{"type":47,"value":1837},"Only ",{"type":41,"tag":50,"props":1839,"children":1841},{"className":1840},[],[1842],{"type":47,"value":1336},{"type":47,"value":1844}," manifests have a non-empty ",{"type":41,"tag":134,"props":1846,"children":1847},{},[1848],{"type":41,"tag":50,"props":1849,"children":1851},{"className":1850},[],[1852],{"type":47,"value":1853},"## Questions for the developer",{"type":47,"value":1855}," section. Each item is a genuine fork the analyzer cannot\nresolve — a structural divergence where the v0.3 and v0.4 readings are\nboth plausible and the right answer depends on the developer's backend.",{"type":41,"tag":67,"props":1857,"children":1858},{},[1859,1861,1867,1868,1874,1876,1881],{"type":47,"value":1860},"Distill, don't interrogate. The manifest already groups sites that share\na single decision (",{"type":41,"tag":50,"props":1862,"children":1864},{"className":1863},[],[1865],{"type":47,"value":1866},"occurrences:",{"type":47,"value":159},{"type":41,"tag":50,"props":1869,"children":1871},{"className":1870},[],[1872],{"type":47,"value":1873},"(×N)",{"type":47,"value":1875},"), so ask ",{"type":41,"tag":134,"props":1877,"children":1878},{},[1879],{"type":47,"value":1880},"one question per\ndistinct fork",{"type":47,"value":1882},", not one per site. For each:",{"type":41,"tag":107,"props":1884,"children":1885},{},[1886,1907,1912],{"type":41,"tag":111,"props":1887,"children":1888},{},[1889,1891,1897,1899,1905],{"type":47,"value":1890},"Show the ",{"type":41,"tag":50,"props":1892,"children":1894},{"className":1893},[],[1895],{"type":47,"value":1896},"coordinate",{"type":47,"value":1898},", the ",{"type":41,"tag":50,"props":1900,"children":1902},{"className":1901},[],[1903],{"type":47,"value":1904},"file:line",{"type":47,"value":1906}," locator, and the windowed\nselection context the manifest provides.",{"type":41,"tag":111,"props":1908,"children":1909},{},[1910],{"type":47,"value":1911},"State the two readings plainly and ask which matches their intent. Do\nnot guess or pre-recommend — their REST API knowledge beats your\npriors.",{"type":41,"tag":111,"props":1913,"children":1914},{},[1915,1917,1922],{"type":47,"value":1916},"Apply their answer with the same Step 3 safety contract. If they\nchoose the v0.3 reading, fortify with ",{"type":41,"tag":50,"props":1918,"children":1920},{"className":1919},[],[1921],{"type":47,"value":308},{"type":47,"value":1923},"; if v0.4, leave it.",{"type":41,"tag":989,"props":1925,"children":1926},{},[1927,1931],{"type":41,"tag":67,"props":1928,"children":1929},{},[1930],{"type":47,"value":996},{"type":41,"tag":989,"props":1932,"children":1933},{},[1934],{"type":41,"tag":67,"props":1935,"children":1936},{},[1937,1943,1945,1950],{"type":41,"tag":50,"props":1938,"children":1940},{"className":1939},[],[1941],{"type":47,"value":1942},"Query.merchantTax",{"type":47,"value":1944}," parses differently under v0.4 here: ",{"type":41,"tag":618,"props":1946,"children":1947},{},[1948],{"type":47,"value":1949},"context",{"type":47,"value":1951},".\nIn v0.3 this read as X; in v0.4 it reads as Y. Which did you intend?\n(This one answer covers all N occurrences.)",{"type":41,"tag":545,"props":1953,"children":1954},{},[],{"type":41,"tag":364,"props":1956,"children":1958},{"id":1957},"step-5-verify-before-you-bump-the-link",[1959,1961,1966,1968],{"type":47,"value":1960},"Step 5: verify — ",{"type":41,"tag":192,"props":1962,"children":1963},{},[1964],{"type":47,"value":1965},"before",{"type":47,"value":1967}," you bump the ",{"type":41,"tag":50,"props":1969,"children":1971},{"className":1970},[],[1972],{"type":47,"value":1294},{"type":41,"tag":67,"props":1974,"children":1975},{},[1976,1978,1983,1984,1997,1998,2002,2004,2009],{"type":47,"value":1977},"After applying the fortifications (and any interview answers), re-run\n",{"type":41,"tag":50,"props":1979,"children":1981},{"className":1980},[],[1982],{"type":47,"value":460},{"type":47,"value":1635},{"type":41,"tag":134,"props":1985,"children":1986},{},[1987,1989,1995],{"type":47,"value":1988},"while the schemas are still on their original\n",{"type":41,"tag":50,"props":1990,"children":1992},{"className":1991},[],[1993],{"type":47,"value":1994},"connect\u002Fv0.n",{"type":47,"value":1996}," link",{"type":47,"value":1178},{"type":41,"tag":134,"props":1999,"children":2000},{},[2001],{"type":47,"value":1183},{"type":47,"value":2003}," bump the ",{"type":41,"tag":50,"props":2005,"children":2007},{"className":2006},[],[2008],{"type":47,"value":1294},{"type":47,"value":2010}," yet. Expected state:",{"type":41,"tag":107,"props":2012,"children":2013},{},[2014,2026,2031],{"type":41,"tag":111,"props":2015,"children":2016},{},[2017,2019,2024],{"type":47,"value":2018},"Sites you fortified no longer appear (their ",{"type":41,"tag":50,"props":2020,"children":2022},{"className":2021},[],[2023],{"type":47,"value":541},{"type":47,"value":2025}," is gone from the new\nmanifest).",{"type":41,"tag":111,"props":2027,"children":2028},{},[2029],{"type":47,"value":2030},"No-op sites may still appear — their source is byte-identical and that\nis correct.",{"type":41,"tag":111,"props":2032,"children":2033},{},[2034,2039],{"type":41,"tag":134,"props":2035,"children":2036},{},[2037],{"type":47,"value":2038},"No new sites",{"type":47,"value":2040}," appear. A new divergence means an edit went wrong —\nrevert it and escalate.",{"type":41,"tag":67,"props":2042,"children":2043},{},[2044,2049,2050,2055,2057,2062,2064,2069,2071,2076,2078,2082,2084,2089,2091,2096,2097,2102,2104,2109],{"type":41,"tag":134,"props":2045,"children":2046},{},[2047],{"type":47,"value":2048},"Why the order matters — this is the load-bearing check.",{"type":47,"value":1635},{"type":41,"tag":50,"props":2051,"children":2053},{"className":2052},[],[2054],{"type":47,"value":460},{"type":47,"value":2056},"\nonly detects ",{"type":41,"tag":192,"props":2058,"children":2059},{},[2060],{"type":47,"value":2061},"pre-upgrade",{"type":47,"value":2063}," divergence: it diffs each schema's linked\n",{"type":41,"tag":50,"props":2065,"children":2067},{"className":2066},[],[2068],{"type":47,"value":1994},{"type":47,"value":2070}," against v0.4. The moment you bump a schema's ",{"type":41,"tag":50,"props":2072,"children":2074},{"className":2073},[],[2075],{"type":47,"value":1294},{"type":47,"value":2077}," to\nv0.4, its \"from\" side ",{"type":41,"tag":192,"props":2079,"children":2080},{},[2081],{"type":47,"value":485},{"type":47,"value":2083}," v0.4, so ",{"type":41,"tag":50,"props":2085,"children":2087},{"className":2086},[],[2088],{"type":47,"value":460},{"type":47,"value":2090}," has nothing left to compare\nand will report ",{"type":41,"tag":50,"props":2092,"children":2094},{"className":2093},[],[2095],{"type":47,"value":1286},{"type":47,"value":1635},{"type":41,"tag":134,"props":2098,"children":2099},{},[2100],{"type":47,"value":2101},"whether or not you applied the\nfortifications",{"type":47,"value":2103},". A post-bump re-analyze therefore proves nothing — it\nwill happily \"pass\" a migration that silently skipped every fortification\n(leaving field accesses as string literals, a behavior change). Verify\n",{"type":41,"tag":192,"props":2105,"children":2106},{},[2107],{"type":47,"value":2108},"here",{"type":47,"value":2110},", on the old version, where a missed fortification still shows up.",{"type":41,"tag":67,"props":2112,"children":2113},{},[2114,2116,2122,2123,2129],{"type":47,"value":2115},"Then run the project's build check (",{"type":41,"tag":50,"props":2117,"children":2119},{"className":2118},[],[2120],{"type":47,"value":2121},"cargo check",{"type":47,"value":258},{"type":41,"tag":50,"props":2124,"children":2126},{"className":2125},[],[2127],{"type":47,"value":2128},"npm run check",{"type":47,"value":2130},",\netc.) and, if a sandbox is available, spot-check a selection or two\nagainst real backend responses.",{"type":41,"tag":545,"props":2132,"children":2133},{},[],{"type":41,"tag":364,"props":2135,"children":2137},{"id":2136},"step-6-bump-the-link-to-connectv04",[2138,2140,2145,2147],{"type":47,"value":2139},"Step 6: bump the ",{"type":41,"tag":50,"props":2141,"children":2143},{"className":2142},[],[2144],{"type":47,"value":1294},{"type":47,"value":2146}," to ",{"type":41,"tag":50,"props":2148,"children":2150},{"className":2149},[],[2151],{"type":47,"value":63},{"type":41,"tag":67,"props":2153,"children":2154},{},[2155,2160,2162,2168,2170,2175,2177,2181],{"type":41,"tag":134,"props":2156,"children":2157},{},[2158],{"type":47,"value":2159},"Only after Step 5 comes back clean.",{"type":47,"value":2161}," In each connector schema, update\nthe existing ",{"type":41,"tag":50,"props":2163,"children":2165},{"className":2164},[],[2166],{"type":47,"value":2167},"@link(url: \"https:\u002F\u002Fspecs.apollo.dev\u002Fconnect\u002Fv0.n\", …)",{"type":47,"value":2169}," to\n",{"type":41,"tag":50,"props":2171,"children":2173},{"className":2172},[],[2174],{"type":47,"value":63},{"type":47,"value":2176}," in place; leave every other link (federation, etc.)\nuntouched. This is the ",{"type":41,"tag":134,"props":2178,"children":2179},{},[2180],{"type":47,"value":1355},{"type":47,"value":2182}," edit — it's what actually moves the\nschema onto v0.4, which is exactly why it follows verification rather\nthan preceding it.",{"type":41,"tag":545,"props":2184,"children":2185},{},[],{"type":41,"tag":364,"props":2187,"children":2189},{"id":2188},"step-7-audit-trail-and-summary",[2190],{"type":47,"value":2191},"Step 7: audit trail and summary",{"type":41,"tag":67,"props":2193,"children":2194},{},[2195],{"type":47,"value":2196},"The timestamped manifest is a durable record of what diverged and what\nwas decided. Suggest committing it alongside the source edits:",{"type":41,"tag":607,"props":2198,"children":2201},{"className":2199,"code":2200,"language":47},[1504],"chore(connectors): v0.3 → v0.4 migration\n\nDriven by `connect-migrate analyze`; manifest preserved as the\nper-decision audit log.\n",[2202],{"type":41,"tag":50,"props":2203,"children":2204},{"__ignoreMap":612},[2205],{"type":47,"value":2200},{"type":41,"tag":67,"props":2207,"children":2208},{},[2209],{"type":47,"value":2210},"Then summarize:",{"type":41,"tag":989,"props":2212,"children":2213},{},[2214,2218],{"type":41,"tag":67,"props":2215,"children":2216},{},[2217],{"type":47,"value":996},{"type":41,"tag":989,"props":2219,"children":2220},{},[2221],{"type":41,"tag":67,"props":2222,"children":2223},{},[2224,2226,2231],{"type":47,"value":2225},"Migration complete. K site(s) fortified, M no-op(s) left as-is, Q\nquestion(s) resolved with you. Post-apply ",{"type":41,"tag":50,"props":2227,"children":2229},{"className":2228},[],[2230],{"type":47,"value":394},{"type":47,"value":2232},"\nreports zero unintended divergence. The manifest is preserved as the\naudit log. Ready to commit?",{"type":41,"tag":67,"props":2234,"children":2235},{},[2236],{"type":47,"value":2237},"Name any appropriate follow-up (run tests, deploy a canary, check\nstaging) explicitly rather than leaving it to the developer to remember.",{"type":41,"tag":545,"props":2239,"children":2240},{},[],{"type":41,"tag":364,"props":2242,"children":2244},{"id":2243},"manifest-format",[2245],{"type":47,"value":2246},"Manifest format",{"type":41,"tag":67,"props":2248,"children":2249},{},[2250,2255,2257,2263],{"type":41,"tag":50,"props":2251,"children":2253},{"className":2252},[],[2254],{"type":47,"value":394},{"type":47,"value":2256}," writes a single markdown file, versioned via\nthe leading ",{"type":41,"tag":50,"props":2258,"children":2260},{"className":2259},[],[2261],{"type":47,"value":2262},"\u003C!-- connect-migrate manifest v2 -->",{"type":47,"value":2264}," comment. Refuse to act\non a file whose version you don't recognize.",{"type":41,"tag":1733,"props":2266,"children":2268},{"id":2267},"header-machine-readable",[2269],{"type":47,"value":2270},"Header (machine-readable)",{"type":41,"tag":67,"props":2272,"children":2273},{},[2274],{"type":47,"value":2275},"Read these comments before parsing prose:",{"type":41,"tag":2277,"props":2278,"children":2279},"table",{},[2280,2299],{"type":41,"tag":2281,"props":2282,"children":2283},"thead",{},[2284],{"type":41,"tag":2285,"props":2286,"children":2287},"tr",{},[2288,2294],{"type":41,"tag":2289,"props":2290,"children":2291},"th",{},[2292],{"type":47,"value":2293},"Comment",{"type":41,"tag":2289,"props":2295,"children":2296},{},[2297],{"type":47,"value":2298},"Meaning",{"type":41,"tag":2300,"props":2301,"children":2302},"tbody",{},[2303,2327,2349,2371,2388,2410,2431,2453,2484],{"type":41,"tag":2285,"props":2304,"children":2305},{},[2306,2316],{"type":41,"tag":2307,"props":2308,"children":2309},"td",{},[2310],{"type":41,"tag":50,"props":2311,"children":2313},{"className":2312},[],[2314],{"type":47,"value":2315},"result:",{"type":41,"tag":2307,"props":2317,"children":2318},{},[2319,2321,2325],{"type":47,"value":2320},"The verdict in one token — see ",{"type":41,"tag":88,"props":2322,"children":2323},{"href":1223},[2324],{"type":47,"value":1226},{"type":47,"value":2326},". Always present.",{"type":41,"tag":2285,"props":2328,"children":2329},{},[2330,2339],{"type":41,"tag":2307,"props":2331,"children":2332},{},[2333],{"type":41,"tag":50,"props":2334,"children":2336},{"className":2335},[],[2337],{"type":47,"value":2338},"files-scanned:",{"type":41,"tag":2307,"props":2340,"children":2341},{},[2342,2347],{"type":41,"tag":50,"props":2343,"children":2345},{"className":2344},[],[2346],{"type":47,"value":977},{"type":47,"value":2348}," files visited.",{"type":41,"tag":2285,"props":2350,"children":2351},{},[2352,2361],{"type":41,"tag":2307,"props":2353,"children":2354},{},[2355],{"type":41,"tag":50,"props":2356,"children":2358},{"className":2357},[],[2359],{"type":47,"value":2360},"directives-analyzed:",{"type":41,"tag":2307,"props":2362,"children":2363},{},[2364,2369],{"type":41,"tag":50,"props":2365,"children":2367},{"className":2366},[],[2368],{"type":47,"value":1272},{"type":47,"value":2370}," directives that parsed cleanly under both grammars.",{"type":41,"tag":2285,"props":2372,"children":2373},{},[2374,2383],{"type":41,"tag":2307,"props":2375,"children":2376},{},[2377],{"type":41,"tag":50,"props":2378,"children":2380},{"className":2379},[],[2381],{"type":47,"value":2382},"divergent-sites:",{"type":41,"tag":2307,"props":2384,"children":2385},{},[2386],{"type":47,"value":2387},"Total divergent tokens reported.",{"type":41,"tag":2285,"props":2389,"children":2390},{},[2391,2400],{"type":41,"tag":2307,"props":2392,"children":2393},{},[2394],{"type":41,"tag":50,"props":2395,"children":2397},{"className":2396},[],[2398],{"type":47,"value":2399},"auto-fixes:",{"type":41,"tag":2307,"props":2401,"children":2402},{},[2403,2405,2409],{"type":47,"value":2404},"Sites in ",{"type":41,"tag":192,"props":2406,"children":2407},{},[2408],{"type":47,"value":408},{"type":47,"value":240},{"type":41,"tag":2285,"props":2411,"children":2412},{},[2413,2422],{"type":41,"tag":2307,"props":2414,"children":2415},{},[2416],{"type":41,"tag":50,"props":2417,"children":2419},{"className":2418},[],[2420],{"type":47,"value":2421},"no-ops:",{"type":41,"tag":2307,"props":2423,"children":2424},{},[2425,2426,2430],{"type":47,"value":2404},{"type":41,"tag":192,"props":2427,"children":2428},{},[2429],{"type":47,"value":415},{"type":47,"value":240},{"type":41,"tag":2285,"props":2432,"children":2433},{},[2434,2443],{"type":41,"tag":2307,"props":2435,"children":2436},{},[2437],{"type":41,"tag":50,"props":2438,"children":2440},{"className":2439},[],[2441],{"type":47,"value":2442},"questions:",{"type":41,"tag":2307,"props":2444,"children":2445},{},[2446,2447,2451],{"type":47,"value":2404},{"type":41,"tag":192,"props":2448,"children":2449},{},[2450],{"type":47,"value":422},{"type":47,"value":2452},". The number that matters: zero means a clean bill of health.",{"type":41,"tag":2285,"props":2454,"children":2455},{},[2456,2465],{"type":41,"tag":2307,"props":2457,"children":2458},{},[2459],{"type":41,"tag":50,"props":2460,"children":2462},{"className":2461},[],[2463],{"type":47,"value":2464},"upgrade:",{"type":41,"tag":2307,"props":2466,"children":2467},{},[2468,2470,2475,2477,2482],{"type":47,"value":2469},"The source ",{"type":41,"tag":50,"props":2471,"children":2473},{"className":2472},[],[2474],{"type":47,"value":1994},{"type":47,"value":2476}," version(s) found across the schemas → the target (",{"type":41,"tag":50,"props":2478,"children":2480},{"className":2479},[],[2481],{"type":47,"value":63},{"type":47,"value":2483},").",{"type":41,"tag":2285,"props":2485,"children":2486},{},[2487,2496],{"type":41,"tag":2307,"props":2488,"children":2489},{},[2490],{"type":41,"tag":50,"props":2491,"children":2493},{"className":2492},[],[2494],{"type":47,"value":2495},"parse-notices:",{"type":41,"tag":2307,"props":2497,"children":2498},{},[2499,2501,2506],{"type":47,"value":2500},"Selections that couldn't be diffed (see ",{"type":41,"tag":192,"props":2502,"children":2503},{},[2504],{"type":47,"value":2505},"Heads up",{"type":47,"value":2507}," below).",{"type":41,"tag":1733,"props":2509,"children":2511},{"id":2510},"title-upgrade-and-scope",[2512],{"type":47,"value":2513},"Title, upgrade, and scope",{"type":41,"tag":67,"props":2515,"children":2516},{},[2517],{"type":47,"value":2518},"Directly under the H1 title:",{"type":41,"tag":107,"props":2520,"children":2521},{},[2522,2564],{"type":41,"tag":111,"props":2523,"children":2524},{},[2525,2527,2532,2534,2539,2541,2547,2549,2555,2557,2562],{"type":47,"value":2526},"An ",{"type":41,"tag":134,"props":2528,"children":2529},{},[2530],{"type":47,"value":2531},"Upgrade",{"type":47,"value":2533}," line names the source ",{"type":41,"tag":50,"props":2535,"children":2537},{"className":2536},[],[2538],{"type":47,"value":1994},{"type":47,"value":2540}," version(s) detected\nacross the schemas and the target — e.g. ",{"type":41,"tag":50,"props":2542,"children":2544},{"className":2543},[],[2545],{"type":47,"value":2546},"connect\u002Fv0.2 (7 schemas) · connect\u002Fv0.3 (5 schemas) → connect\u002Fv0.4",{"type":47,"value":2548},". The \"from\" version is read\nper schema from its ",{"type":41,"tag":50,"props":2550,"children":2552},{"className":2551},[],[2553],{"type":47,"value":2554},"@link(url: \"...\u002Fconnect\u002Fv0.n\")",{"type":47,"value":2556},", and each\nschema's selections are diffed at ",{"type":41,"tag":192,"props":2558,"children":2559},{},[2560],{"type":47,"value":2561},"its own",{"type":47,"value":2563}," version against v0.4 — a\nv0.2 schema is compared as v0.2, not assumed to be v0.3.",{"type":41,"tag":111,"props":2565,"children":2566},{},[2567,2569,2574,2576,2582,2584,2589],{"type":47,"value":2568},"A ",{"type":41,"tag":134,"props":2570,"children":2571},{},[2572],{"type":47,"value":2573},"Scope",{"type":47,"value":2575}," line names the project root, the schema count, and the\ndirective count, followed by a ",{"type":41,"tag":50,"props":2577,"children":2579},{"className":2578},[],[2580],{"type":47,"value":2581},"Schemas considered:",{"type":47,"value":2583}," list of every\n",{"type":41,"tag":50,"props":2585,"children":2587},{"className":2586},[],[2588],{"type":47,"value":977},{"type":47,"value":2590}," file the run walked. Read it first: it's how you confirm\nthe run covered the schemas you expected. If an expected schema isn't\nlisted, the path argument was wrong — re-run before trusting the\nverdict.",{"type":41,"tag":1733,"props":2592,"children":2594},{"id":2593},"body-three-buckets",[2595],{"type":47,"value":2596},"Body: three buckets",{"type":41,"tag":107,"props":2598,"children":2599},{},[2600,2620,2653,2680],{"type":41,"tag":111,"props":2601,"children":2602},{},[2603,2611,2613,2618],{"type":41,"tag":134,"props":2604,"children":2605},{},[2606],{"type":41,"tag":50,"props":2607,"children":2609},{"className":2608},[],[2610],{"type":47,"value":1380},{"type":47,"value":2612}," — one ",{"type":41,"tag":50,"props":2614,"children":2616},{"className":2615},[],[2617],{"type":47,"value":1419},{"type":47,"value":2619}," machine block per\nfortification, followed by a human-readable bullet list. Apply these\n(Step 3).",{"type":41,"tag":111,"props":2621,"children":2622},{},[2623,2632,2634,2639,2640,2645,2646,2651],{"type":41,"tag":134,"props":2624,"children":2625},{},[2626],{"type":41,"tag":50,"props":2627,"children":2629},{"className":2628},[],[2630],{"type":47,"value":2631},"## No action needed",{"type":47,"value":2633}," — a token-frequency rollup of the no-op\n",{"type":41,"tag":50,"props":2635,"children":2637},{"className":2636},[],[2638],{"type":47,"value":157},{"type":47,"value":205},{"type":41,"tag":50,"props":2641,"children":2643},{"className":2642},[],[2644],{"type":47,"value":165},{"type":47,"value":205},{"type":41,"tag":50,"props":2647,"children":2649},{"className":2648},[],[2650],{"type":47,"value":172},{"type":47,"value":2652}," sites. Informational; make no edits.",{"type":41,"tag":111,"props":2654,"children":2655},{},[2656,2664,2666,2672,2674,2679],{"type":41,"tag":134,"props":2657,"children":2658},{},[2659],{"type":41,"tag":50,"props":2660,"children":2662},{"className":2661},[],[2663],{"type":47,"value":1853},{"type":47,"value":2665}," — one entry per genuine\nambiguity, each with a machine block, a one-line statement of the\nfork, and a windowed selection context. Empty unless ",{"type":41,"tag":50,"props":2667,"children":2669},{"className":2668},[],[2670],{"type":47,"value":2671},"result",{"type":47,"value":2673}," is\n",{"type":41,"tag":50,"props":2675,"children":2677},{"className":2676},[],[2678],{"type":47,"value":1336},{"type":47,"value":240},{"type":41,"tag":111,"props":2681,"children":2682},{},[2683,2692,2694,2700,2702,2707,2708,2712,2714,2719,2721,2726],{"type":41,"tag":134,"props":2684,"children":2685},{},[2686],{"type":41,"tag":50,"props":2687,"children":2689},{"className":2688},[],[2690],{"type":47,"value":2691},"## After applying — switch to connect\u002Fv0.4",{"type":47,"value":2693}," — the closing\nsection, with the ready-to-paste ",{"type":41,"tag":50,"props":2695,"children":2697},{"className":2696},[],[2698],{"type":47,"value":2699},"@link(...\u002Fconnect\u002Fv0.4)",{"type":47,"value":2701}," line each\nmigrated schema should adopt. Bump the ",{"type":41,"tag":50,"props":2703,"children":2705},{"className":2704},[],[2706],{"type":47,"value":1294},{"type":47,"value":1635},{"type":41,"tag":134,"props":2709,"children":2710},{},[2711],{"type":47,"value":1355},{"type":47,"value":2713}," — only after\nthe Step 5 verify passes on the ",{"type":41,"tag":192,"props":2715,"children":2716},{},[2717],{"type":47,"value":2718},"old",{"type":47,"value":2720}," version, since once a schema is\non v0.4 ",{"type":41,"tag":50,"props":2722,"children":2724},{"className":2723},[],[2725],{"type":47,"value":460},{"type":47,"value":2727}," can no longer detect a missed fortification.",{"type":41,"tag":1733,"props":2729,"children":2731},{"id":2730},"heads-up-selections-not-analyzed",[2732],{"type":47,"value":2733},"Heads up — selections not analyzed",{"type":41,"tag":67,"props":2735,"children":2736},{},[2737,2738,2744,2746,2751,2753,2758],{"type":47,"value":2568},{"type":41,"tag":50,"props":2739,"children":2741},{"className":2740},[],[2742],{"type":47,"value":2743},"## Heads up — selections not analyzed (N)",{"type":47,"value":2745}," section appears only when\nsome ",{"type":41,"tag":50,"props":2747,"children":2749},{"className":2748},[],[2750],{"type":47,"value":1272},{"type":47,"value":2752}," selection failed to parse, so it couldn't be diffed.\nThis is ",{"type":41,"tag":134,"props":2754,"children":2755},{},[2756],{"type":47,"value":2757},"non-fatal",{"type":47,"value":2759}," — the rest of the manifest stands — but each entry\nneeds a look:",{"type":41,"tag":107,"props":2761,"children":2762},{},[2763,2778,2795],{"type":41,"tag":111,"props":2764,"children":2765},{},[2766,2776],{"type":41,"tag":192,"props":2767,"children":2768},{},[2769,2771],{"type":47,"value":2770},"parses under the linked spec but not under ",{"type":41,"tag":50,"props":2772,"children":2774},{"className":2773},[],[2775],{"type":47,"value":63},{"type":47,"value":2777}," — the\nselection would break on upgrade; it must be fixed before migrating.",{"type":41,"tag":111,"props":2779,"children":2780},{},[2781,2793],{"type":41,"tag":192,"props":2782,"children":2783},{},[2784,2786,2791],{"type":47,"value":2785},"parses under ",{"type":41,"tag":50,"props":2787,"children":2789},{"className":2788},[],[2790],{"type":47,"value":63},{"type":47,"value":2792}," but not under the linked spec",{"type":47,"value":2794}," — it uses\nsyntax newer than the schema declares (a latent inconsistency).",{"type":41,"tag":111,"props":2796,"children":2797},{},[2798,2803],{"type":41,"tag":192,"props":2799,"children":2800},{},[2801],{"type":47,"value":2802},"parses under neither",{"type":47,"value":2804}," — a pre-existing syntax error, out of scope.",{"type":41,"tag":67,"props":2806,"children":2807},{},[2808],{"type":47,"value":2809},"Surface these to the developer; don't try to auto-fix them.",{"type":41,"tag":1733,"props":2811,"children":2813},{"id":2812},"site-v2-machine-block",[2814,2819],{"type":41,"tag":50,"props":2815,"children":2817},{"className":2816},[],[2818],{"type":47,"value":1419},{"type":47,"value":2820}," machine block",{"type":41,"tag":67,"props":2822,"children":2823},{},[2824,2826,2832],{"type":47,"value":2825},"One block per site (grouped sites carry ",{"type":41,"tag":50,"props":2827,"children":2829},{"className":2828},[],[2830],{"type":47,"value":2831},"occurrences: N",{"type":47,"value":605},{"type":41,"tag":2277,"props":2834,"children":2835},{},[2836,2852],{"type":41,"tag":2281,"props":2837,"children":2838},{},[2839],{"type":41,"tag":2285,"props":2840,"children":2841},{},[2842,2847],{"type":41,"tag":2289,"props":2843,"children":2844},{},[2845],{"type":47,"value":2846},"Field",{"type":41,"tag":2289,"props":2848,"children":2849},{},[2850],{"type":47,"value":2851},"Notes",{"type":41,"tag":2300,"props":2853,"children":2854},{},[2855,2871,2894,2928,2945,2973,2996,3040,3056,3086,3136,3175],{"type":41,"tag":2285,"props":2856,"children":2857},{},[2858,2866],{"type":41,"tag":2307,"props":2859,"children":2860},{},[2861],{"type":41,"tag":50,"props":2862,"children":2864},{"className":2863},[],[2865],{"type":47,"value":541},{"type":41,"tag":2307,"props":2867,"children":2868},{},[2869],{"type":47,"value":2870},"Stable 8-hex content hash; survives line shifts.",{"type":41,"tag":2285,"props":2872,"children":2873},{},[2874,2882],{"type":41,"tag":2307,"props":2875,"children":2876},{},[2877],{"type":41,"tag":50,"props":2878,"children":2880},{"className":2879},[],[2881],{"type":47,"value":1473},{"type":41,"tag":2307,"props":2883,"children":2884},{},[2885,2887,2893],{"type":47,"value":2886},"Path relative to ",{"type":41,"tag":50,"props":2888,"children":2890},{"className":2889},[],[2891],{"type":47,"value":2892},"project-root",{"type":47,"value":240},{"type":41,"tag":2285,"props":2895,"children":2896},{},[2897,2912],{"type":41,"tag":2307,"props":2898,"children":2899},{},[2900,2905,2906],{"type":41,"tag":50,"props":2901,"children":2903},{"className":2902},[],[2904],{"type":47,"value":620},{"type":47,"value":258},{"type":41,"tag":50,"props":2907,"children":2909},{"className":2908},[],[2910],{"type":47,"value":2911},"col",{"type":41,"tag":2307,"props":2913,"children":2914},{},[2915,2917,2922,2923],{"type":47,"value":2916},"1-indexed start of the host ",{"type":41,"tag":50,"props":2918,"children":2920},{"className":2919},[],[2921],{"type":47,"value":1272},{"type":47,"value":1565},{"type":41,"tag":134,"props":2924,"children":2925},{},[2926],{"type":47,"value":2927},"Authoritative locator.",{"type":41,"tag":2285,"props":2929,"children":2930},{},[2931,2940],{"type":41,"tag":2307,"props":2932,"children":2933},{},[2934],{"type":41,"tag":50,"props":2935,"children":2937},{"className":2936},[],[2938],{"type":47,"value":2939},"byte_offset",{"type":41,"tag":2307,"props":2941,"children":2942},{},[2943],{"type":47,"value":2944},"Same location as a byte index.",{"type":41,"tag":2285,"props":2946,"children":2947},{},[2948,2956],{"type":41,"tag":2307,"props":2949,"children":2950},{},[2951],{"type":41,"tag":50,"props":2952,"children":2954},{"className":2953},[],[2955],{"type":47,"value":1896},{"type":41,"tag":2307,"props":2957,"children":2958},{},[2959,2965,2966,2971],{"type":41,"tag":50,"props":2960,"children":2962},{"className":2961},[],[2963],{"type":47,"value":2964},"Type.field",{"type":47,"value":928},{"type":41,"tag":134,"props":2967,"children":2968},{},[2969],{"type":47,"value":2970},"Informational only",{"type":47,"value":2972}," — not unique across files; never use as a locator.",{"type":41,"tag":2285,"props":2974,"children":2975},{},[2976,2985],{"type":41,"tag":2307,"props":2977,"children":2978},{},[2979],{"type":41,"tag":50,"props":2980,"children":2982},{"className":2981},[],[2983],{"type":47,"value":2984},"from",{"type":41,"tag":2307,"props":2986,"children":2987},{},[2988,2989,2994],{"type":47,"value":1397},{"type":41,"tag":50,"props":2990,"children":2992},{"className":2991},[],[2993],{"type":47,"value":1994},{"type":47,"value":2995}," spec the host schema links — the \"from\" side of this site's upgrade.",{"type":41,"tag":2285,"props":2997,"children":2998},{},[2999,3008],{"type":41,"tag":2307,"props":3000,"children":3001},{},[3002],{"type":41,"tag":50,"props":3003,"children":3005},{"className":3004},[],[3006],{"type":47,"value":3007},"kind",{"type":41,"tag":2307,"props":3009,"children":3010},{},[3011,3017,3018,3024,3025,3031,3032,3038],{"type":41,"tag":50,"props":3012,"children":3014},{"className":3013},[],[3015],{"type":47,"value":3016},"key_quoted_flipped_to_literal_string",{"type":47,"value":258},{"type":41,"tag":50,"props":3019,"children":3021},{"className":3020},[],[3022],{"type":47,"value":3023},"key_flipped_to_literal_null",{"type":47,"value":258},{"type":41,"tag":50,"props":3026,"children":3028},{"className":3027},[],[3029],{"type":47,"value":3030},"key_flipped_to_literal_bool",{"type":47,"value":258},{"type":41,"tag":50,"props":3033,"children":3035},{"className":3034},[],[3036],{"type":47,"value":3037},"key_field_flipped_to_literal_string",{"type":47,"value":3039},", or a structural kind.",{"type":41,"tag":2285,"props":3041,"children":3042},{},[3043,3051],{"type":41,"tag":2307,"props":3044,"children":3045},{},[3046],{"type":41,"tag":50,"props":3047,"children":3049},{"className":3048},[],[3050],{"type":47,"value":47},{"type":41,"tag":2307,"props":3052,"children":3053},{},[3054],{"type":47,"value":3055},"The token's source text (JSON-escaped).",{"type":41,"tag":2285,"props":3057,"children":3058},{},[3059,3067],{"type":41,"tag":2307,"props":3060,"children":3061},{},[3062],{"type":41,"tag":50,"props":3063,"children":3065},{"className":3064},[],[3066],{"type":47,"value":1613},{"type":41,"tag":2307,"props":3068,"children":3069},{},[3070,3072,3078,3080,3085],{"type":47,"value":3071},"Byte range ",{"type":41,"tag":50,"props":3073,"children":3075},{"className":3074},[],[3076],{"type":47,"value":3077},"start..end",{"type":47,"value":3079}," of the token ",{"type":41,"tag":192,"props":3081,"children":3082},{},[3083],{"type":47,"value":3084},"within the selection body",{"type":47,"value":240},{"type":41,"tag":2285,"props":3087,"children":3088},{},[3089,3098],{"type":41,"tag":2307,"props":3090,"children":3091},{},[3092],{"type":41,"tag":50,"props":3093,"children":3095},{"className":3094},[],[3096],{"type":47,"value":3097},"followed_by",{"type":41,"tag":2307,"props":3099,"children":3100},{},[3101,3107,3108,3114,3115,3121,3122,3128,3129,3135],{"type":41,"tag":50,"props":3102,"children":3104},{"className":3103},[],[3105],{"type":47,"value":3106},"nothing",{"type":47,"value":258},{"type":41,"tag":50,"props":3109,"children":3111},{"className":3110},[],[3112],{"type":47,"value":3113},"sub_selection",{"type":47,"value":258},{"type":41,"tag":50,"props":3116,"children":3118},{"className":3117},[],[3119],{"type":47,"value":3120},"key_access",{"type":47,"value":258},{"type":41,"tag":50,"props":3123,"children":3125},{"className":3124},[],[3126],{"type":47,"value":3127},"method",{"type":47,"value":258},{"type":41,"tag":50,"props":3130,"children":3132},{"className":3131},[],[3133],{"type":47,"value":3134},"question",{"type":47,"value":240},{"type":41,"tag":2285,"props":3137,"children":3138},{},[3139,3148],{"type":41,"tag":2307,"props":3140,"children":3141},{},[3142],{"type":41,"tag":50,"props":3143,"children":3145},{"className":3144},[],[3146],{"type":47,"value":3147},"recommendation",{"type":41,"tag":2307,"props":3149,"children":3150},{},[3151,3157,3159,3165,3167,3173],{"type":41,"tag":50,"props":3152,"children":3154},{"className":3153},[],[3155],{"type":47,"value":3156},"keep-v0.3",{"type":47,"value":3158}," (fortify), ",{"type":41,"tag":50,"props":3160,"children":3162},{"className":3161},[],[3163],{"type":47,"value":3164},"embrace-v0.4",{"type":47,"value":3166}," (no-op), or ",{"type":41,"tag":50,"props":3168,"children":3170},{"className":3169},[],[3171],{"type":47,"value":3172},"???",{"type":47,"value":3174}," (a question).",{"type":41,"tag":2285,"props":3176,"children":3177},{},[3178,3186],{"type":41,"tag":2307,"props":3179,"children":3180},{},[3181],{"type":41,"tag":50,"props":3182,"children":3184},{"className":3183},[],[3185],{"type":47,"value":1427},{"type":41,"tag":2307,"props":3187,"children":3188},{},[3189,3191,3196],{"type":47,"value":3190},"Present on auto-fixes: the exact replacement text for ",{"type":41,"tag":50,"props":3192,"children":3194},{"className":3193},[],[3195],{"type":47,"value":47},{"type":47,"value":3197}," (JSON-escaped).",{"type":41,"tag":545,"props":3199,"children":3200},{},[],{"type":41,"tag":364,"props":3202,"children":3204},{"id":3203},"result-kinds",[3205],{"type":47,"value":1226},{"type":41,"tag":107,"props":3207,"children":3208},{},[3209,3237,3285,3313,3334],{"type":41,"tag":111,"props":3210,"children":3211},{},[3212,3220,3222,3227,3229,3235],{"type":41,"tag":134,"props":3213,"children":3214},{},[3215],{"type":41,"tag":50,"props":3216,"children":3218},{"className":3217},[],[3219],{"type":47,"value":1243},{"type":47,"value":3221}," — zero ",{"type":41,"tag":50,"props":3223,"children":3225},{"className":3224},[],[3226],{"type":47,"value":977},{"type":47,"value":3228}," files visited (",{"type":41,"tag":50,"props":3230,"children":3232},{"className":3231},[],[3233],{"type":47,"value":3234},"files-scanned: 0",{"type":47,"value":3236},").\nAlmost always a path mistake. Report the path; ask for another. Stop.",{"type":41,"tag":111,"props":3238,"children":3239},{},[3240,3248,3249,3254,3256,3262,3264,3270,3272,3277,3278,3283],{"type":41,"tag":134,"props":3241,"children":3242},{},[3243],{"type":41,"tag":50,"props":3244,"children":3246},{"className":3245},[],[3247],{"type":47,"value":1264},{"type":47,"value":1266},{"type":41,"tag":50,"props":3250,"children":3252},{"className":3251},[],[3253],{"type":47,"value":1272},{"type":47,"value":3255}," directives\nparsed cleanly (",{"type":41,"tag":50,"props":3257,"children":3259},{"className":3258},[],[3260],{"type":47,"value":3261},"directives-analyzed: 0",{"type":47,"value":3263},"). Usually means no connectors\nhere; but if a ",{"type":41,"tag":50,"props":3265,"children":3267},{"className":3266},[],[3268],{"type":47,"value":3269},"## Heads up",{"type":47,"value":3271}," section is present, there ",{"type":41,"tag":192,"props":3273,"children":3274},{},[3275],{"type":47,"value":3276},"were",{"type":47,"value":1635},{"type":41,"tag":50,"props":3279,"children":3281},{"className":3280},[],[3282],{"type":47,"value":1272},{"type":47,"value":3284},"\ndirectives that failed to parse — read those before concluding. Stop.",{"type":41,"tag":111,"props":3286,"children":3287},{},[3288,3296,3298,3304,3306,3311],{"type":41,"tag":134,"props":3289,"children":3290},{},[3291],{"type":41,"tag":50,"props":3292,"children":3294},{"className":3293},[],[3295],{"type":47,"value":1286},{"type":47,"value":3297}," — directives present, zero divergence\n(",{"type":41,"tag":50,"props":3299,"children":3301},{"className":3300},[],[3302],{"type":47,"value":3303},"divergent-sites: 0",{"type":47,"value":3305},"). A trustworthy positive verdict, not the\nabsence of one. State it; the only change is the ",{"type":41,"tag":50,"props":3307,"children":3309},{"className":3308},[],[3310],{"type":47,"value":1294},{"type":47,"value":3312}," bump\n(Step 6), done with the developer's go-ahead.",{"type":41,"tag":111,"props":3314,"children":3315},{},[3316,3324,3326,3332],{"type":41,"tag":134,"props":3317,"children":3318},{},[3319],{"type":41,"tag":50,"props":3320,"children":3322},{"className":3321},[],[3323],{"type":47,"value":1308},{"type":47,"value":3325}," — divergence exists, but every site is a\ndeterministic fortification or a no-op (",{"type":41,"tag":50,"props":3327,"children":3329},{"className":3328},[],[3330],{"type":47,"value":3331},"questions: 0",{"type":47,"value":3333},"). Apply the\nrewrites; no developer decisions are required.",{"type":41,"tag":111,"props":3335,"children":3336},{},[3337,3345,3347,3353],{"type":41,"tag":134,"props":3338,"children":3339},{},[3340],{"type":41,"tag":50,"props":3341,"children":3343},{"className":3342},[],[3344],{"type":47,"value":1336},{"type":47,"value":3346}," — at least one genuinely ambiguous site\n(",{"type":41,"tag":50,"props":3348,"children":3350},{"className":3349},[],[3351],{"type":47,"value":3352},"questions: > 0",{"type":47,"value":3354},"). Apply the rewrites, then interview the developer\nover the questions.",{"type":41,"tag":67,"props":3356,"children":3357},{},[3358,3360,3365,3367,3372,3374,3379,3381,3386],{"type":47,"value":3359},"Each schema is diffed at its ",{"type":41,"tag":134,"props":3361,"children":3362},{},[3363],{"type":47,"value":3364},"own",{"type":47,"value":3366}," linked ",{"type":41,"tag":50,"props":3368,"children":3370},{"className":3369},[],[3371],{"type":47,"value":1994},{"type":47,"value":3373}," version\nagainst the v0.4 target (see the ",{"type":41,"tag":50,"props":3375,"children":3377},{"className":3376},[],[3378],{"type":47,"value":2531},{"type":47,"value":3380}," line), so a mixed-version\nproject is handled correctly. A schema already on ",{"type":41,"tag":50,"props":3382,"children":3384},{"className":3383},[],[3385],{"type":47,"value":63},{"type":47,"value":3387}," shows\nzero divergence — it's at the target.",{"type":41,"tag":545,"props":3389,"children":3390},{},[],{"type":41,"tag":364,"props":3392,"children":3394},{"id":3393},"classification-doctrine",[3395],{"type":47,"value":3396},"Classification doctrine",{"type":41,"tag":67,"props":3398,"children":3399},{},[3400],{"type":47,"value":3401},"Why the analyzer sorts sites the way it does:",{"type":41,"tag":107,"props":3403,"children":3404},{},[3405,3457,3494,3539,3580],{"type":41,"tag":111,"props":3406,"children":3407},{},[3408,3419,3421,3426,3428,3433,3435,3441,3443,3448,3450,3455],{"type":41,"tag":134,"props":3409,"children":3410},{},[3411,3413,3418],{"type":47,"value":3412},"Quoted token → fortify (",{"type":41,"tag":50,"props":3414,"children":3416},{"className":3415},[],[3417],{"type":47,"value":3156},{"type":47,"value":2483},{"type":47,"value":3420}," A quoted token was a\n",{"type":41,"tag":50,"props":3422,"children":3424},{"className":3423},[],[3425],{"type":47,"value":292},{"type":47,"value":3427}," field access in v0.3 with ",{"type":41,"tag":192,"props":3429,"children":3430},{},[3431],{"type":47,"value":3432},"no",{"type":47,"value":3434}," literal-string meaning\navailable. Treating it as a v0.4 literal would silently change\nbehavior. Always ",{"type":41,"tag":50,"props":3436,"children":3438},{"className":3437},[],[3439],{"type":47,"value":3440},"$.\"…\"",{"type":47,"value":3442},". The character content is irrelevant —\n",{"type":41,"tag":50,"props":3444,"children":3446},{"className":3445},[],[3447],{"type":47,"value":264},{"type":47,"value":3449}," and ",{"type":41,"tag":50,"props":3451,"children":3453},{"className":3452},[],[3454],{"type":47,"value":256},{"type":47,"value":3456}," are handled identically.",{"type":41,"tag":111,"props":3458,"children":3459},{},[3460,3471,3473,3478,3480,3486,3487,3493],{"type":41,"tag":134,"props":3461,"children":3462},{},[3463,3465,3470],{"type":47,"value":3464},"Bare identifier → fortify (",{"type":41,"tag":50,"props":3466,"children":3468},{"className":3467},[],[3469],{"type":47,"value":3156},{"type":47,"value":2483},{"type":47,"value":3472}," Was a ",{"type":41,"tag":50,"props":3474,"children":3476},{"className":3475},[],[3477],{"type":47,"value":300},{"type":47,"value":3479},"\nreference in v0.3; same logic. ",{"type":41,"tag":50,"props":3481,"children":3483},{"className":3482},[],[3484],{"type":47,"value":3485},"foo",{"type":47,"value":57},{"type":41,"tag":50,"props":3488,"children":3490},{"className":3489},[],[3491],{"type":47,"value":3492},"$.foo",{"type":47,"value":240},{"type":41,"tag":111,"props":3495,"children":3496},{},[3497,3514,3516,3522,3523,3529,3531,3537],{"type":41,"tag":134,"props":3498,"children":3499},{},[3500,3505,3507,3512],{"type":41,"tag":50,"props":3501,"children":3503},{"className":3502},[],[3504],{"type":47,"value":3097},{"type":47,"value":3506}," not ",{"type":41,"tag":50,"props":3508,"children":3510},{"className":3509},[],[3511],{"type":47,"value":3106},{"type":47,"value":3513}," → fortify.",{"type":47,"value":3515}," A literal can't have a\nfield, method, or sub-selection, so a trailing ",{"type":41,"tag":50,"props":3517,"children":3519},{"className":3518},[],[3520],{"type":47,"value":3521},".x",{"type":47,"value":258},{"type":41,"tag":50,"props":3524,"children":3526},{"className":3525},[],[3527],{"type":47,"value":3528},"->m()",{"type":47,"value":3530},", or\n",{"type":41,"tag":50,"props":3532,"children":3534},{"className":3533},[],[3535],{"type":47,"value":3536},"{ … }",{"type":47,"value":3538}," proves the v0.3 field-reference reading was intended.",{"type":41,"tag":111,"props":3540,"children":3541},{},[3542,3571,3573,3578],{"type":41,"tag":134,"props":3543,"children":3544},{},[3545,3546,3551,3552,3557,3558,3563,3565,3570],{"type":47,"value":151},{"type":41,"tag":50,"props":3547,"children":3549},{"className":3548},[],[3550],{"type":47,"value":157},{"type":47,"value":205},{"type":41,"tag":50,"props":3553,"children":3555},{"className":3554},[],[3556],{"type":47,"value":165},{"type":47,"value":205},{"type":41,"tag":50,"props":3559,"children":3561},{"className":3560},[],[3562],{"type":47,"value":172},{"type":47,"value":3564}," → no-op (",{"type":41,"tag":50,"props":3566,"children":3568},{"className":3567},[],[3569],{"type":47,"value":3164},{"type":47,"value":2483},{"type":47,"value":3572}," A field\nliterally named ",{"type":41,"tag":50,"props":3574,"children":3576},{"className":3575},[],[3577],{"type":47,"value":157},{"type":47,"value":3579}," is implausible; v0.3 resolved it to null via\nresponse normalization, so the output coincides with the v0.4 literal.\nLeave it.",{"type":41,"tag":111,"props":3581,"children":3582},{},[3583,3594],{"type":41,"tag":134,"props":3584,"children":3585},{},[3586,3588,3593],{"type":47,"value":3587},"Structural divergence → question (",{"type":41,"tag":50,"props":3589,"children":3591},{"className":3590},[],[3592],{"type":47,"value":3172},{"type":47,"value":2483},{"type":47,"value":3595}," Anything the analyzer\ncan't place in the above (a sub-selection\u002Fobject-literal shape flip it\ncan't prove equivalent). These are the only sites that reach the\ndeveloper.",{"type":41,"tag":545,"props":3597,"children":3598},{},[],{"type":41,"tag":364,"props":3600,"children":3602},{"id":3601},"boundary-conditions",[3603],{"type":47,"value":3604},"Boundary conditions",{"type":41,"tag":107,"props":3606,"children":3607},{},[3608,3629,3651,3672,3689],{"type":41,"tag":111,"props":3609,"children":3610},{},[3611,3620,3622,3627],{"type":41,"tag":134,"props":3612,"children":3613},{},[3614],{"type":41,"tag":50,"props":3615,"children":3617},{"className":3616},[],[3618],{"type":47,"value":3619},"legacy_object_to_lit_object",{"type":47,"value":3621}," — a cosmetic AST shape difference\nfrom the unification; same evaluation semantics. ",{"type":41,"tag":50,"props":3623,"children":3625},{"className":3624},[],[3626],{"type":47,"value":460},{"type":47,"value":3628}," does not\nemit these as sites.",{"type":41,"tag":111,"props":3630,"children":3631},{},[3632,3641,3643,3649],{"type":41,"tag":134,"props":3633,"children":3634},{},[3635],{"type":41,"tag":50,"props":3636,"children":3638},{"className":3637},[],[3639],{"type":47,"value":3640},"v04_only_accepts",{"type":47,"value":3642}," — the selection uses v0.4-only syntax (e.g. the\n",{"type":41,"tag":50,"props":3644,"children":3646},{"className":3645},[],[3647],{"type":47,"value":3648},"…",{"type":47,"value":3650}," spread). Already committed to v0.4; nothing to migrate. Not\nemitted; if you meet one in source, leave it.",{"type":41,"tag":111,"props":3652,"children":3653},{},[3654,3663,3665,3670],{"type":41,"tag":134,"props":3655,"children":3656},{},[3657],{"type":41,"tag":50,"props":3658,"children":3660},{"className":3659},[],[3661],{"type":47,"value":3662},"v03_only_accepts",{"type":47,"value":3664}," — should never appear after the parser fix.\nTreat as a ",{"type":41,"tag":50,"props":3666,"children":3668},{"className":3667},[],[3669],{"type":47,"value":4},{"type":47,"value":3671}," bug and file an issue.",{"type":41,"tag":111,"props":3673,"children":3674},{},[3675,3680,3682,3687],{"type":41,"tag":134,"props":3676,"children":3677},{},[3678],{"type":47,"value":3679},"Pre-existing syntax errors",{"type":47,"value":3681}," — selections that parse under neither\ngrammar. They appear in the ",{"type":41,"tag":50,"props":3683,"children":3685},{"className":3684},[],[3686],{"type":47,"value":3269},{"type":47,"value":3688}," section; surface them for\nawareness. They predate the migration and are out of scope. Do not\nrepair them.",{"type":41,"tag":111,"props":3690,"children":3691},{},[3692,3697,3699,3705,3710,3712,3717,3718,3724,3726,3730],{"type":41,"tag":134,"props":3693,"children":3694},{},[3695],{"type":47,"value":3696},"Non-selection spec changes",{"type":47,"value":3698}," — this tool diffs ",{"type":41,"tag":50,"props":3700,"children":3702},{"className":3701},[],[3703],{"type":47,"value":3704},"@connect(selection:)",{"type":41,"tag":192,"props":3706,"children":3707},{},[3708],{"type":47,"value":3709},"mapping",{"type":47,"value":3711}," only. Other version-to-version changes (e.g. the v0.2→v0.3\narrow-method shape \u002F URI-validation change, or ",{"type":41,"tag":50,"props":3713,"children":3715},{"className":3714},[],[3716],{"type":47,"value":1272},{"type":47,"value":205},{"type":41,"tag":50,"props":3719,"children":3721},{"className":3720},[],[3722],{"type":47,"value":3723},"@source",{"type":47,"value":3725},"\nargument changes) are ",{"type":41,"tag":134,"props":3727,"children":3728},{},[3729],{"type":47,"value":1183},{"type":47,"value":3731}," analyzed. For a multi-version jump\n(v0.2 → v0.4), consult the connect spec changelog for anything beyond\nselection mapping.",{"type":41,"tag":545,"props":3733,"children":3734},{},[],{"type":41,"tag":364,"props":3736,"children":3738},{"id":3737},"failure-modes",[3739],{"type":47,"value":3740},"Failure modes",{"type":41,"tag":107,"props":3742,"children":3743},{},[3744,3766],{"type":41,"tag":111,"props":3745,"children":3746},{},[3747,3757,3759,3764],{"type":41,"tag":134,"props":3748,"children":3749},{},[3750,3755],{"type":41,"tag":50,"props":3751,"children":3753},{"className":3752},[],[3754],{"type":47,"value":4},{"type":47,"value":3756}," not installed.",{"type":47,"value":3758}," Run the install command, retry.\nOn an unsupported-platform error from ",{"type":41,"tag":50,"props":3760,"children":3762},{"className":3761},[],[3763],{"type":47,"value":1030},{"type":47,"value":3765},", surface it\nverbatim and stop; do not build from source unattended.",{"type":41,"tag":111,"props":3767,"children":3768},{},[3769,3774,3776,3781,3783,3788,3790,3795],{"type":41,"tag":134,"props":3770,"children":3771},{},[3772],{"type":47,"value":3773},"Source changed between analyze and apply.",{"type":47,"value":3775}," Before editing, confirm\neach site's ",{"type":41,"tag":50,"props":3777,"children":3779},{"className":3778},[],[3780],{"type":47,"value":541},{"type":47,"value":3782}," still appears in a fresh ",{"type":41,"tag":50,"props":3784,"children":3786},{"className":3785},[],[3787],{"type":47,"value":460},{"type":47,"value":3789}," run. If an ",{"type":41,"tag":50,"props":3791,"children":3793},{"className":3792},[],[3794],{"type":47,"value":541},{"type":47,"value":3796}," is\ngone, the source moved under you — regenerate the manifest and restart\nrather than guess.",{"type":41,"tag":545,"props":3798,"children":3799},{},[],{"type":41,"tag":364,"props":3801,"children":3803},{"id":3802},"tone",[3804],{"type":47,"value":3805},"Tone",{"type":41,"tag":107,"props":3807,"children":3808},{},[3809,3814,3819,3824,3829],{"type":41,"tag":111,"props":3810,"children":3811},{},[3812],{"type":47,"value":3813},"Read the developer's code; don't speculate. Their REST API knowledge\nbeats your priors.",{"type":41,"tag":111,"props":3815,"children":3816},{},[3817],{"type":47,"value":3818},"Lead with the source range and the before\u002Fafter. Don't bury the diff.",{"type":41,"tag":111,"props":3820,"children":3821},{},[3822],{"type":47,"value":3823},"Apply the mechanical rewrites confidently — they're behavior-preserving\nby construction. Reserve the developer's attention for the genuine\nquestions.",{"type":41,"tag":111,"props":3825,"children":3826},{},[3827],{"type":47,"value":3828},"For an ambiguous site, ask. One extra message beats a behavior\nregression.",{"type":41,"tag":111,"props":3830,"children":3831},{},[3832,3834,3839],{"type":47,"value":3833},"Don't claim success unless the post-apply ",{"type":41,"tag":50,"props":3835,"children":3837},{"className":3836},[],[3838],{"type":47,"value":460},{"type":47,"value":3840}," reports zero\nunintended divergence.",{"type":41,"tag":3842,"props":3843,"children":3844},"style",{},[3845],{"type":47,"value":3846},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3848,"total":4020},[3849,3866,3881,3894,3915,3930,3943,3956,3971,3987,3997,4008],{"slug":3850,"name":3850,"fn":3851,"description":3852,"org":3853,"tags":3854,"stars":3863,"repoUrl":3864,"updatedAt":3865},"apollo-client","build React apps with Apollo Client","Guide for building React applications with Apollo Client 4.x. Use this skill when: (1) setting up Apollo Client in a React project, (2) writing GraphQL queries or mutations with hooks, (3) configuring caching or cache policies, (4) managing local state with reactive variables, (5) troubleshooting Apollo Client errors or performance issues.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3855,3856,3859,3860],{"name":9,"slug":8,"type":16},{"name":3857,"slug":3858,"type":16},"Frontend","frontend",{"name":14,"slug":15,"type":16},{"name":3861,"slug":3862,"type":16},"React","react",97,"https:\u002F\u002Fgithub.com\u002Fapollographql\u002Fskills","2026-04-06T18:01:12.268638",{"slug":3867,"name":3867,"fn":3868,"description":3869,"org":3870,"tags":3871,"stars":3863,"repoUrl":3864,"updatedAt":3880},"apollo-connectors","integrate REST APIs with Apollo Connectors","Guide for integrating REST APIs into GraphQL supergraphs using Apollo Connectors with @source and @connect directives. Use this skill when the user: (1) mentions \"connectors\", \"Apollo Connectors\", or \"REST Connector\", (2) wants to integrate a REST API into GraphQL, (3) references @source or @connect directives, (4) works with files containing \"# Note to AI Friends: This is an Apollo Connectors schema\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3872,3875,3876,3877],{"name":3873,"slug":3874,"type":16},"API Development","api-development",{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},{"name":3878,"slug":3879,"type":16},"REST API","rest-api","2026-04-06T18:01:23.758345",{"slug":3882,"name":3882,"fn":3883,"description":3884,"org":3885,"tags":3886,"stars":3863,"repoUrl":3864,"updatedAt":3893},"apollo-federation","author Apollo Federation subgraph schemas","Guide for authoring Apollo Federation subgraph schemas. Use this skill when: (1) creating new subgraph schemas for a federated supergraph, (2) defining or modifying entities with @key, (3) sharing types\u002Ffields across subgraphs with @shareable, (4) working with federation directives (@external, @requires, @provides, @override, @inaccessible), (5) troubleshooting composition errors, (6) any task involving federation schema design patterns.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3887,3888,3889,3892],{"name":3873,"slug":3874,"type":16},{"name":9,"slug":8,"type":16},{"name":3890,"slug":3891,"type":16},"Architecture","architecture",{"name":14,"slug":15,"type":16},"2026-07-30T05:31:51.648593",{"slug":3895,"name":3895,"fn":3896,"description":3897,"org":3898,"tags":3899,"stars":3863,"repoUrl":3864,"updatedAt":3914},"apollo-ios","build Apple apps with Apollo iOS","Guide for building Apple-platform applications with Apollo iOS, the strongly-typed GraphQL client for Swift. Use this skill when: (1) adding Apollo iOS to a Swift Package Manager or Xcode project, (2) configuring `apollo-codegen-config.json` and running code generation, (3) configuring an `ApolloClient` with auth, interceptors, and caching, (4) writing queries, mutations, or subscriptions from SwiftUI views, (5) writing tests against generated operation mocks.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3900,3901,3902,3905,3908,3911],{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},{"name":3903,"slug":3904,"type":16},"iOS","ios",{"name":3906,"slug":3907,"type":16},"Mobile","mobile",{"name":3909,"slug":3910,"type":16},"Swift","swift",{"name":3912,"slug":3913,"type":16},"Xcode","xcode","2026-04-29T05:35:21.329969",{"slug":3916,"name":3916,"fn":3917,"description":3918,"org":3919,"tags":3920,"stars":3863,"repoUrl":3864,"updatedAt":3929},"apollo-kotlin","build Kotlin apps with Apollo GraphQL","Guide for building applications with Apollo Kotlin, the GraphQL client library for Android and Kotlin. Use this skill when: (1) setting up Apollo Kotlin in a Gradle project for Android, Kotlin\u002FJVM, or KMP, (2) configuring schema download and codegen for GraphQL services, (3) configuring an `ApolloClient` with auth, interceptors, and caching, (4) writing queries, mutations, or subscriptions,\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3921,3924,3925,3926],{"name":3922,"slug":3923,"type":16},"Android","android",{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},{"name":3927,"slug":3928,"type":16},"Kotlin","kotlin","2026-04-06T18:01:15.005978",{"slug":3931,"name":3931,"fn":3932,"description":3933,"org":3934,"tags":3935,"stars":3863,"repoUrl":3864,"updatedAt":3942},"apollo-mcp-server","connect MCP agents to GraphQL APIs","Guide for using Apollo MCP Server to connect AI agents with GraphQL APIs. Use this skill when: (1) setting up or configuring Apollo MCP Server, (2) defining MCP tools from GraphQL operations, (3) using introspection tools (introspect, search, validate, execute), (4) troubleshooting MCP server connectivity or tool execution issues.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3936,3937,3938,3939],{"name":3873,"slug":3874,"type":16},{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},{"name":3940,"slug":3941,"type":16},"MCP","mcp","2026-04-06T18:01:20.578589",{"slug":3944,"name":3944,"fn":3945,"description":3946,"org":3947,"tags":3948,"stars":3863,"repoUrl":3864,"updatedAt":3955},"apollo-router","configure Apollo Router for GraphQL federation","Version-aware guide for configuring and running Apollo Router for federated GraphQL supergraphs. Generates correct YAML for both Router v1.x and v2.x. Use this skill when: (1) setting up Apollo Router to run a supergraph, (2) configuring routing, headers, or CORS, (3) implementing custom plugins (Rhai scripts or coprocessors), (4) configuring telemetry (tracing, metrics, logging), (5) troubleshooting Router performance or connectivity issues, (6) securing the graph with JWT, declarative field-level authorization directives, or persisted-query safelisting, (7) managing router.yaml as version-controlled config with CI\u002FCD validation.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3949,3950,3951,3954],{"name":9,"slug":8,"type":16},{"name":3890,"slug":3891,"type":16},{"name":3952,"slug":3953,"type":16},"Deployment","deployment",{"name":14,"slug":15,"type":16},"2026-07-30T05:31:54.665938",{"slug":3957,"name":3957,"fn":3958,"description":3959,"org":3960,"tags":3961,"stars":3863,"repoUrl":3864,"updatedAt":3970},"apollo-router-plugin-creator","write Apollo Router Rust plugins","Guide for writing Apollo Router native Rust plugins. Use this skill when: (1) users want to create a new router plugin, (2) users want to add service hooks (router_service, supergraph_service, execution_service, subgraph_service), (3) users want to modify an existing router plugin, (4) users need to understand router plugin patterns or the request lifecycle. (5) triggers on requests like \"create a new plugin\", \"add a router plugin\", \"modify the X plugin\", or \"add subgraph_service hook\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3962,3963,3964,3967],{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},{"name":3965,"slug":3966,"type":16},"Plugin Development","plugin-development",{"name":3968,"slug":3969,"type":16},"Rust","rust","2026-04-06T18:01:27.176974",{"slug":3972,"name":3972,"fn":3973,"description":3974,"org":3975,"tags":3976,"stars":3863,"repoUrl":3864,"updatedAt":3986},"apollo-server","build GraphQL servers with Apollo Server","Guide for building GraphQL servers with Apollo Server 5.x. Use this skill when: (1) setting up a new Apollo Server project, (2) writing resolvers or defining GraphQL schemas, (3) implementing authentication or authorization, (4) creating plugins or custom data sources, (5) troubleshooting Apollo Server errors or performance issues.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3977,3978,3979,3980,3983],{"name":3873,"slug":3874,"type":16},{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},{"name":3981,"slug":3982,"type":16},"Node.js","nodejs",{"name":3984,"slug":3985,"type":16},"TypeScript","typescript","2026-04-06T18:01:13.653038",{"slug":3988,"name":3988,"fn":3989,"description":3990,"org":3991,"tags":3992,"stars":3863,"repoUrl":3864,"updatedAt":3996},"graphql-operations","write GraphQL queries and mutations","Guide for writing GraphQL operations (queries, mutations, fragments) following best practices. Use this skill when: (1) writing GraphQL queries or mutations, (2) organizing operations with fragments, (3) optimizing data fetching patterns, (4) setting up type generation or linting, (5) reviewing operations for efficiency.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3993,3994,3995],{"name":3873,"slug":3874,"type":16},{"name":9,"slug":8,"type":16},{"name":14,"slug":15,"type":16},"2026-04-06T18:01:25.530906",{"slug":3998,"name":3998,"fn":3999,"description":4000,"org":4001,"tags":4002,"stars":3863,"repoUrl":3864,"updatedAt":4007},"graphql-schema","design GraphQL schemas","Guide for designing GraphQL schemas following industry best practices. Use this skill when: (1) designing a new GraphQL schema or API, (2) reviewing existing schema for improvements, (3) deciding on type structures or nullability, (4) implementing pagination or error patterns, (5) ensuring security in schema design.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[4003,4004,4005,4006],{"name":3873,"slug":3874,"type":16},{"name":9,"slug":8,"type":16},{"name":3890,"slug":3891,"type":16},{"name":14,"slug":15,"type":16},"2026-04-06T18:01:19.140592",{"slug":4009,"name":4009,"fn":4010,"description":4011,"org":4012,"tags":4013,"stars":3863,"repoUrl":3864,"updatedAt":4019},"rover","manage GraphQL schemas with Apollo Rover","Guide for using Apollo Rover CLI to manage GraphQL schemas and federation. Use this skill when: (1) publishing or fetching subgraph\u002Fgraph schemas, (2) composing supergraph schemas locally or via GraphOS, (3) running local supergraph development with rover dev, (4) validating schemas with check and lint commands, (5) configuring Rover authentication and environment, (6) exploring or searching a graph's schema for agent-driven discovery (rover schema describe \u002F rover schema search).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[4014,4015,4018],{"name":9,"slug":8,"type":16},{"name":4016,"slug":4017,"type":16},"CLI","cli",{"name":14,"slug":15,"type":16},"2026-07-30T05:31:52.675547",15,{"items":4022,"total":24},[4023],{"slug":4,"name":4,"fn":5,"description":6,"org":4024,"tags":4025,"stars":24,"repoUrl":25,"updatedAt":26},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[4026,4027,4028,4029],{"name":9,"slug":8,"type":16},{"name":22,"slug":23,"type":16},{"name":14,"slug":15,"type":16},{"name":19,"slug":20,"type":16}]