Skip to content

docs: bump icp-cli docs links to the 1.3 version slug - #354

Merged
raymondk merged 1 commit into
mainfrom
docs/cli-docs-1-3
Aug 27, 2026
Merged

docs: bump icp-cli docs links to the 1.3 version slug#354
raymondk merged 1 commit into
mainfrom
docs/cli-docs-1-3

Conversation

@marc0olo

@marc0olo marc0olo commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

  • Bump all 22 cli.internetcomputer.org links from the 1.1 slug to 1.3 (14 files)
  • icp-cli v1.3.0 shipped on 2026-08-08; the docs-site now serves /1.3/

Verification

All 16 distinct URLs return 200 at /1.3/, and all 7 deep-link anchors were confirmed present in the live 1.3 pages (icp-canister-logs, icp-canister-settings-update, icp-cycles, icp-token-allowance, icp-token-approve, canister-ids, networks). npm run build passes.

Follow-ups

The AGENTS.md linking rule still names 1.1 as the current slug and derives it from the pinned icp-cli release. That rule change, and the .sources/icp-cli pin itself (still v1.1.0), are covered by #355.

Merge order

These three are independent in intent but share one file, so they have a preferred order: #354, then #357, then #358.

PR Why here
1 #354 CLI docs links to the 1.3 slug Content only, no dependencies, and the smallest diff.
2 #357 clear the hand-written validate --all violations Touches scripts/validate.js, data-persistence.mdx, chain-key-tokens.mdx; no overlap with the other two. Merging it here drops validate --all on main from 11 errors to 7.
3 #358 .sources/ reduction Largest diff, and it shares docs/guides/canister-management/lifecycle.mdx with #354 (different lines: a CLI link there, a stale .sources/ pointer here). Last means at most one rebase, on a cleaner validate baseline.

The remaining 7 errors after #357 are all in docs/references/internet-identity-spec.md, which is generated by sync-ii-spec.mjs. They clear when dfinity/internet-identity#4284 merges and the next spec sync runs; that PR is independent of the order here.

@marc0olo
marc0olo requested a review from a team as a code owner August 26, 2026 09:54
All 16 distinct URLs and all 7 anchors verified live at /1.3/.
@raymondk
raymondk merged commit 6b384b0 into main Aug 27, 2026
8 checks passed
@raymondk
raymondk deleted the docs/cli-docs-1-3 branch August 27, 2026 12:36
marc0olo added a commit that referenced this pull request Aug 27, 2026
…358)

Closes #355.

`.sources/` held 16 submodules. Three are opened by the build; the rest
were vendored so an agent could check a fact, or to supply skills
through a symlink chain. Both have cheaper answers.

**Result: 3 submodules, 10 watched entries across 6 upstreams, 7
verified on demand, skills mirrored from the registry.**

## Kept, because the build opens them

| Submodule | Resolved by |
|---|---|
| `motoko` | synced pages, plus 52 `<motokoExamples>` includes |
| `internetidentity` | `sync-ii-spec.mjs`, which generates two reference
pages |
| `examples` | `remark-snippet.mjs`, which extracts `snippet=` code |

Thirteen go: eleven that existed only so an agent could check a fact,
plus `icskills` and `dotskills`, which fed the skills symlink chain. The
eleven appeared only in the `AGENTS.md` source table and
`.agents/submodule-bumping.md`, and CI initializes just `examples` and
`motoko`, so nothing in the build referenced any of them.

## What is worth watching, and what is not

Watching everything meant a weekly check on repos the docs barely draw
on. Measured footprint in `docs/` decided each one:

| Watched | Pages | Hits | Also |
|---|---|---|---|
| `icp-cli` | 51 | 461 | the docs-site link slug |
| `motoko-core` | 63 | 328 | |
| `cdk-rs` | 47 | 336 | pinned versions in prose |
| `icp-js-core` | 15 | 47 | |
| `icp-cli-recipes` | 11 | 26 | every hit is a pinned recipe version in
prose; one entry per recipe |
| `icp-js-canisters` | 4 | 8 | a stated minimum version |

The other six became a `reference` group: `icp-cli-templates` (linked,
not restated), `candid` (a rarely moving spec), `response-verification`
(two pages, both linking to docs.rs), `chain-fusion-signer` (7
mentions), `papi` (1 page), `ic-pub-key` (3 pages). They keep their
entry and verify path so an agent knows where to look, but carry no pin
and produce no issue: verify against the **latest** release on demand,
since the surface is small enough that the next edit to those pages
catches drift. Each entry records the footprint that decided it, so
promotion back to `watched` is a judgment someone can re-make.

## Every upstream is now checked exactly once

| Upstream | Checked by | Produces |
|---|---|---|
| `motoko`, `internetidentity` | their existing sync workflows,
unchanged | a bump PR |
| `examples` | `upstream-releases.yml`, `vendored` | an issue when the
gitlink falls behind `master` |
| 10 watched entries | `upstream-releases.yml`, `watched` | an issue
when a newer release exists |
| 7 reference repos | nothing | nothing; verified against the latest
release on demand |
| 29 IC skills | `.claude/sync-ic-skills.sh` on session start | nothing;
not committed |

`examples` previously had **no** drift notification of any kind: it
tracks a branch, and the only two workflows that could have noticed
belong to the other two submodules. Issues carry `upstream-sync` plus a
per-upstream label, and a run whose title already matches an open issue
does nothing, so a repo that stays behind produces no weekly noise.

## Watching

`.sources/upstream.json` records per repo: the ref the docs are verified
against, where its releases appear, which file's diff is the review
payload, and what to re-check. `scripts/check-upstream-releases.mjs`
writes an issue body per repo that moved (no token needed). `npm run
check:upstream` runs it locally.

Agents verify against the **pinned** ref, so a review is reproducible
and a page cannot document a flag from a release whose links have not
been adapted:

```bash
curl -sL https://raw.githubusercontent.com/dfinity/icp-cli/<pinned>/docs/reference/cli.md
```

`raw.githubusercontent.com` rather than `gh api .../contents/...`: the
API returns base64 truncated at ~2.3 KB against 71 KB for the full file,
and a truncated flag reference looks complete.

### Where releases appear differs per repo, and getting it wrong fails
silently

| `track` | Latest from | Repos |
|---|---|---|
| `release` | git tags matching an anchored pattern | `icp-cli`,
`motoko-core`, `icp-js-core`, and one entry per `icp-cli-recipes` recipe
|
| `crate` | crates.io | `cdk-rs` |
| `npm` | npm registry | `icp-js-canisters` |

Seven defects found and fixed: three while building this, one from
Copilot, three from review:

1. **Lexicographic sorting reported downgrades.** These repos tag in
four shapes; as strings `0.9.4` beats `0.20.1`. Sorting is numeric-run
aware.
2. **`cdk-rs` was pinned to `0.20.1`, which is not a tag** but the
published crate version, with the newest tag two minors behind. Against
tags that pin could never be overtaken, so the repo governing every Rust
code block would have reported "current" for ever. A `release` pin that
is not among the matched tags is now a **loud failure**; every pin was
audited.
3. **`icp-js-sdk-docs` was the wrong repo entirely.** It is the
js.icp.build delivery site, so a commit meant the docs site rebuilt.
Replaced by `icp-js-core` (tags match npm) and `icp-js-canisters` (tags
lag npm by two majors, so npm is the signal). Sibling repos, different
correct answers.
4. **`icp-cli-recipes` monitoring was a no-op for four of five
recipes.** Refs compare by numeric run, so `rust-` against
`static-site-` compares textually and only the alphabetically-last
prefix could ever be reported, while the entry read as watched. Each
recipe now has its own entry, pin, issue and label, which immediately
surfaces four releases the docs are behind.
5. **The pin guard covered only the `release` track.** A `crate` or
`npm` pin in the wrong shape (`v0.20.1` for a crate that publishes
`0.20.1`) compared as older for ever. Registry pins are now checked
against the published version list, and crates.io reads
`max_stable_version` so a prerelease is not reported as the latest
release.
6. **`summarizeChange` counted by set membership**, so an added line
that already appeared elsewhere in the file read as unchanged. It now
counts occurrences: `cli.md` v1.1.0 to v1.3.0 reports 14 added rather
than 11.
7. **The `commit` track ordered short SHAs** with the version
comparator, so an update whose SHA happened to sort lower read as
"current". Demonstrated with pin `ef03698` against head `9f1e819`. SHAs
have no order, so it is now a prefix match, with the full SHA in links.

`commit` is currently used by nothing: it exists for a repo with no
releases at all, and after the reduction below no watched repo is in
that state.

### First run: 9 of 11 have drifted

`icp-cli` v1.1.0→v1.3.0, `motoko-core` v2.4.0→v2.6.1, `cdk-rs`
0.20.1→0.20.2, `icp-js-core` v5.4.0→v6.1.0, `icp-js-canisters`
3.5.0→3.6.0, and four recipes: `asset-canister` v2.2.1→v2.3.0, `rust`
v3.3.0→v3.4.0, `motoko` v5.0.0→v5.1.0, `prebuilt` v2.0.0→v2.1.0. The
`static-site` recipe and `examples` are current.

Three carry real content risk: `motoko-core` (two minors of `mo:core`),
`icp-js-core` (a major bump, `Agent.readState` revamped), `cdk-rs`.
Where a changelog exists it is the review payload, because "check every
API signature" is unbounded while a changelog names the symbols to grep
for.

`icp-cli` is pinned at v1.1.0 deliberately: #354 moved the doc *links*
to the 1.3 slug, but the commands were last verified against v1.1.0, so
the first watch issue carries that pass rather than hiding it.

## Skills

`.claude/sync-ic-skills.sh` on `SessionStart`, byte-identical to the
script `dfinity/examples` runs, replaces two submodules and a double
symlink chain. Coverage rises 16 → 29 and corrects two table entries
pointing at skills that no longer exist upstream: `asset-canister` →
`static-site` (which covers the legacy recipe and the migration) and
`vetkd` → `vetkeys`.

No symlinks, in either direction: Claude finds a skill whether it is a
directory or a link, no other harness auto-discovers any path, and for
the synced 29 a symlink is actively wrong because the sync consumes
anything at a name the registry publishes (verified in a sandbox).
AGENTS.md states the non-Claude path instead: fetch the registry index
by URL, explicitly **not** `.claude/skills/`, which is gitignored and
empty in a fresh clone.

`icp-brand-design`, `icp-brand-voice`, and `technical-documentation`
stay committed. The last is not on the registry and changed once since
our pin, only to relicense `AGPL-3.0-only` → MIT. That license is why it
was a submodule; MIT still requires the notice to travel, so its text
sits in the skill directory and a new root `NOTICE` records the
attribution.

## Instructions updated

`AGENTS.md` (source material, CLI verification, the `1.3` slug no longer
derived from a submodule pin, skills, key directories),
`.agents/upstream-tracking.md` replacing `submodule-bumping.md`, and
`CONTRIBUTING.md`.

## Worth challenging

- **The workflow itself has never run.** The script is exercised
locally; the Actions job (app token, label creation, supersede logic) is
not.
- **`examples` costs 265 MB** for one page today (6 snippets, 2
examples). Keeping it bets on `<CodeExample>` spreading.
- **`ic-cdk-timers` and `ic-cdk-executor` version separately** from
`ic-cdk` and are not tracked individually; the `affects` note tells a
reviewer to check them.
- **`setup.sh` now prunes stale `.sources/` directories.** An existing
clone keeps them after a submodule is removed, and a frozen
`.sources/icp-cli/` looks authoritative while being exactly what the
verify-at-the-pinned-ref rule exists to prevent. Only untracked
directories are touched.
- **The `reference` group trades a prompt for less noise.** If nobody
edits those pages, drift sits there unreported. That is the deliberate
bet: six weekly checks on repos with a 1-to-3 page footprint cost more
attention than they return.
- **The three committed skills are watched by nothing.**
- **The non-Claude skill path is an instruction, not a mechanism.**
Claude gets skills regardless; every other harness only by compliance.

## Found along the way, not fixed here

`ic-cdk` is pinned to four different versions across the docs: `0.19` in
8 places, plus `0.20`, `0.17`, and `0.17.1`. The published crate is
0.20.2. That is a content bug rather than infrastructure, so it belongs
with the `cdk-rs` watch issue rather than in this PR.

## Follow-up

#279 is unblocked: of the 11 repos its "keep as-is" bucket names, only
`examples` is still a submodule, and `affects` now carries the
upstream-to-pages mapping.

After merge: trigger the workflow once so the drifts land as issues,
batch the pin-only bumps, then handle `icp-cli` and `motoko-core` as
their own PRs.

## Merge order

These three are independent in intent but share one file, so they have a
preferred order: **#354, then #357, then #358.**

| | PR | Why here |
|---|---|---|
| 1 | #354 CLI docs links to the 1.3 slug | Content only, no
dependencies, and the smallest diff. |
| 2 | #357 clear the hand-written `validate --all` violations | Touches
`scripts/validate.js`, `data-persistence.mdx`, `chain-key-tokens.mdx`;
no overlap with the other two. Merging it here drops `validate --all` on
`main` from 11 errors to 7. |
| 3 | #358 `.sources/` reduction | Largest diff, and it shares
`docs/guides/canister-management/lifecycle.mdx` with #354 (different
lines: a CLI link there, a stale `.sources/` pointer here). Last means
at most one rebase, on a cleaner validate baseline. |

The remaining 7 errors after #357 are all in
`docs/references/internet-identity-spec.md`, which is generated by
`sync-ii-spec.mjs`. They clear when
[dfinity/internet-identity#4284](dfinity/internet-identity#4284)
merges and the next spec sync runs; that PR is independent of the order
here.
marc0olo added a commit that referenced this pull request Aug 27, 2026
…aced (#370)

Closes #360, #361, #362, #363, #364, #365, #366, #367, #368, #369.

The first batch from the upstream watcher added in #358, plus one fix to
the watcher itself that running the batch exposed. **23 files, 18 of
them docs pages.** Two of the ten issues needed real content changes;
the other eight were version strings or pin bumps.

Every claim below was checked against the upstream at the new ref. Where
a replacement is named, it is upstream's own wording, not an inference.

## What a reviewer should scrutinise

1. **The 20 Motoko call-site replacements** are the only changes that
alter code a reader copies. Each one is named in upstream's
`@deprecated` marker, quoted below.
2. **`icp canister status <name> -i`** replacing `icp canister id
<name>`. This is a real command substitution, verified against the
installed CLI, not a rename.
3. **The examples path filter** decides whether a whole class of issue
fires. Its two-way verification is in the table below.

Everything else is a version string or a pin.

## motoko-core v2.4.0 → v2.6.1: 20 deprecated call sites (#362)

v2.6.0 deprecated every `Module.fromX` conversion that has a `toX`
counterpart. Our examples used five of them, so left alone each one
emits a compiler warning for anyone who copies the code. The
replacements are read from `src/*.mo` at v2.6.1:

| Deprecated | Upstream's `@deprecated` text | Sites |
|---|---|---|
| `Nat64.fromNat` | Use `Nat.toNat64` instead. | 7 |
| `Nat8.fromNat` | Use `Nat.toNat8` instead. | 5 |
| `Blob.fromArray` | Use `Array.toBlob` instead. | 5 |
| `Float.fromInt` | Use `Int.toFloat` instead. | 2 |
| `Text.fromChar` | Use `Char.toText` instead. | 1 |

**Import policy: four added, zero removed.** A replacement can need a
module the block did not import (`Nat`, `Char`), so those are added.
Nothing is removed, deliberately: an unused import is harmless, a
missing one does not compile, and pre-existing imports are not this
change's business. A first attempt did prune them and got it wrong three
ways, dropping `Text` where `hex.chars()` still needs it in scope for
contextual dot, plus `Cycles` and `Principal`, which are unrelated. That
pass was reverted.

Two cases handled by hand: one `Nat64.fromNat` appears in prose rather
than a fence (`ledgers.mdx`), and one fence is a fragment with no import
block of its own, so it gets the call change without an import.

## icp-cli v1.1.0 → v1.3.0: nothing lost, two commands that never
existed (#361)

The decisive check is that **`docs/reference/cli.md` lost 0 lines**
between the two refs (14 added, all for the new `icp canister link`). No
documented command or flag disappeared, so the bump itself is safe.

Verifying every `icp` invocation in `docs/` against that reference then
surfaced two commands that do not exist. Both confirmed against the
installed v1.3.0, not just the reference:

| In the docs | Actual | Sites |
|---|---|---|
| `icp canister id <name>` | `icp canister status <name> -i` (`-i` is
documented as "Only print the canister ids") | 4 |
| `icp identity whoami` | `icp identity principal` | 1 |

These predate the bump. They are exactly what the verification pass
deferred in #354 was for.

## The remaining eight

- **Four recipes** (#366, #367, #368, #369) each shipped **one identical
additive change**, an optional `visibility` field on metadata entries,
so the `icp.yaml` examples stay valid and only the 20 version strings
move: `asset-canister` v2.2.1→v2.3.0 (10 places), `rust` v3.3.0→v3.4.0
(6), `motoko` v5.0.0→v5.1.0 (3), `prebuilt` v2.0.0→v2.1.0 (1).
- **icp-js-core v5.4.0 → v6.1.0** (#364) is a major bump, and still
pin-only for us. It changes `Agent.readState`, delegation permissions,
effective subnet ID and URL path handling. Our examples import `agent`,
`agent/canister-env`, `principal`, `identity` and `candid` only, never
construct a `Delegation`, and never call `readState`.
- **cdk-rs 0.20.2** (#363) only makes entry points lenient about an
empty input vector. Pin-only.
- **icp-js-canisters 3.6.0** (#365) adds NNS converter fields and
snapshot visibility; the stated `>= 3.5.0` minimum still holds.
Pin-only.
- **examples** (#360) bumped to `42c474d`, with all six `snippet=`
regions verified to resolve.

## Watcher fix: report an examples bump only when it touches a quoted
file

Running the batch showed #360 was noise, and the first attempt at fixing
that was wrong in an instructive way. I dropped the check entirely,
reasoning that the build already fails when a `snippet=` path or
`#region` marker stops resolving. It does, but that only covers
**breakage**. It cannot see an example being **corrected upstream while
still resolving**, which leaves the docs quoting old code with a green
build. That is the more valuable signal, and dropping the check would
have lost it.

So the check stays and gets precise. The entry carries `pathFilter:
"snippets"`, and the check intersects the files changed between the
pinned gitlink and the branch head with every file a `snippet=` quotes,
opening an issue only when they overlap, naming the files.

Verified both ways against real history:

| Pin | Range | Result |
|---|---|---|
| `01edb5d8`, before a fix to the two `send_http_*` examples | 5
commits, 80 files | **reports**, naming
`motoko/send_http_get/backend/main.mo` and
`motoko/send_http_post/backend/main.mo` |
| `b4fe175`, the pin this PR bumps from | 4 commits, 78 files, none
quoted | **silent** |

The second row is the point: #360 fired for a range touching nothing the
docs quote, and would not fire now.

Two implementation details worth a look. The snippet path is
`<lang-dir>/<attribute>`, where the directory comes from the **fence
language**, so `LANG_TO_DIR` is mirrored from
`plugins/remark-snippet.mjs` rather than imported, because that plugin
pulls in remark dependencies and the workflow installs none. The two
must be kept in step, and an unmapped language throws rather than
silently matching fewer files. Separately, when the comparison cannot be
trusted, the API failing or truncating at its 300-file cap, the check
reports instead of assuming nothing moved.

The per-recipe split from #358 is unchanged. A batch release produces
one issue per recipe, four here for a single upstream change. That is
the cost of tracking them independently, and it is what made four stale
version strings visible.

## Verification

```
npm run check:upstream   # every entry current, exits 0
npm run build            # passes
npm run validate         # 7 errors, unchanged from main
```

The 7 are all in `docs/references/internet-identity-spec.md`, which is
generated. They clear when Internet Identity cuts its next dated release
containing
[dfinity/internet-identity#4284](dfinity/internet-identity#4284);
our pin is already on the newest existing release tag, so there is
nothing to sync until then. Tracked in #356.

## Known limits

- The two CLI command fixes are verified against `icp 1.3.0` locally and
against the published reference, but not executed end to end against a
live project.
- `snippet=` coverage depends on `LANG_TO_DIR` staying in step with the
plugin. The two now match key for key, verified by parsing both maps
rather than reading them, and an unmapped language fails loudly, which
is the safe direction. It is still a duplicated constant: the plugin
cannot be imported here because it pulls in remark dependencies the
workflow does not install.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants