fix: clear hand-written validate --all violations, correct the mo:base check - #357
Merged
Conversation
…e check Replaces two em-dashes in prose, and fixes the mo:base rule so it matches imports (mo:base/) inside code fences instead of legitimate prose mentions of the legacy library in base-to-core migration tables.
This was referenced Aug 26, 2026
raymondk
approved these changes
Aug 27, 2026
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
npm run validate(--all) reported 11 errors onmain. This clears the 4 that are in hand-written pages. The remaining 7 are in a synced file and are tracked in #356.data-persistence.mdx,chain-key-tokens.mdx)mo:basecheck corrected inscripts/validate.jsWhy the
mo:basecheck changedcheckForbiddenPatternsskips fenced code, so the old/mo:base/pattern could only ever match prose. That is backwards for this rule:import Buffer "mo:base/Buffer";lives inside a fence and was never flagged;mainwere the base-to-core migration table indata-persistence.mdx("Use these in preference to the legacymo:baseequivalents", and theReplaces (mo:base)column header), which is the same mapping AGENTS.md itself spells out.The rule now matches the import path
mo:base/and opts into fence checking via a per-ruleincludeFencesflag, so the other two patterns keep their prose-only behaviour.Verified both directions:
with the fixture containing an import inside a fence plus a prose mention; only the import is reported. A scan of
docs/confirms there are nomo:base/imports being missed today.Verification
npm run validategoes from 11 errors to 7 (all 7 indocs/references/internet-identity-spec.md, synced fromdfinity/internet-identity, see #356).npm run buildpasses.Merge order
These three are independent in intent but share one file, so they have a preferred order: #354, then #357, then #358.
validate --allviolationsscripts/validate.js,data-persistence.mdx,chain-key-tokens.mdx; no overlap with the other two. Merging it here dropsvalidate --allonmainfrom 11 errors to 7..sources/reductiondocs/guides/canister-management/lifecycle.mdxwith #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 bysync-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.