From b878c86e9c54eef027fc706a13929fb35bba515d Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 19:12:42 +0200 Subject: [PATCH 1/7] docs: sync content against the ten upstream releases the watcher surfaced Closes the first batch of upstream-sync issues. Two of the ten needed real content changes; the rest were version strings or pin bumps. motoko-core v2.4.0 -> v2.6.1 deprecated every `Module.fromX` conversion that has a `toX` counterpart, and our Motoko examples used five of them across 20 call sites. Each replacement is the one named in the upstream @deprecated marker: Nat64.fromNat -> Nat.toNat64 Blob.fromArray -> Array.toBlob Nat8.fromNat -> Nat.toNat8 Float.fromInt -> Int.toFloat Text.fromChar -> Char.toText Imports were added where a replacement needs a module the block did not import. No import was removed: an unused one is harmless, a missing one does not compile, and the pre-existing imports are not this change's business. icp-cli v1.1.0 -> v1.3.0 removed nothing from docs/reference/cli.md (0 lines removed, 14 added for `icp canister link`), so no documented flag disappeared. Verifying every command against the reference did surface two that never existed, both against the installed v1.3.0: icp canister id -> icp canister status -i (-i prints ids only) icp identity whoami -> icp identity principal The four recipe releases are one additive change each, an optional `visibility` field on metadata entries, so the icp.yaml examples stay valid and only the version strings move. icp-js-core's major bump touches Agent.readState, Delegation permissions, effective subnet ID and URL paths, none of which our examples use: they import agent, agent/canister-env, principal, identity and candid only. cdk-rs 0.20.2 only makes entry points lenient about empty input. icp-js-canisters 3.6.0 adds NNS converter fields and snapshot visibility, and the stated minimum of 3.5.0 still holds. examples is bumped to 42c474d with all six snippet regions verified to resolve. --- .sources/examples | 2 +- .sources/upstream.json | 20 +++++++++---------- docs/getting-started/project-structure.mdx | 6 +++--- docs/guides/canister-calls/candid.mdx | 4 ++-- .../canister-calls/inter-canister-calls.mdx | 2 +- docs/guides/canister-management/large-wasm.md | 2 +- docs/guides/canister-management/logs.md | 2 +- .../canister-management/optimization.md | 2 +- .../reproducible-builds.md | 2 +- docs/guides/chain-fusion/bitcoin.mdx | 5 +++-- .../chain-fusion/chain-fusion-signer.md | 2 +- docs/guides/chain-fusion/ethereum.mdx | 5 +++-- docs/guides/chain-fusion/exchange-rates.mdx | 6 +++--- .../digital-assets/chain-key-tokens.mdx | 6 +++--- docs/guides/digital-assets/ledgers.mdx | 13 ++++++------ docs/guides/frontends/asset-canister.md | 6 +++--- docs/guides/frontends/frameworks.md | 12 +++++------ docs/guides/governance/testing.md | 6 +++--- .../data-integrity-and-authenticity.md | 11 +++++----- docs/references/application-canisters.md | 2 +- 20 files changed, 60 insertions(+), 56 deletions(-) diff --git a/.sources/examples b/.sources/examples index b4fe175c..42c474dd 160000 --- a/.sources/examples +++ b/.sources/examples @@ -1 +1 @@ -Subproject commit b4fe175c14b3755698d56ece8c4dd353824538bb +Subproject commit 42c474ddf77dbe54f8a5dbe80827559a9c1da691 diff --git a/.sources/upstream.json b/.sources/upstream.json index 0e8de111..7997968c 100644 --- a/.sources/upstream.json +++ b/.sources/upstream.json @@ -46,16 +46,16 @@ "watched": [ { "repo": "dfinity/icp-cli", - "pinned": "v1.1.0", + "pinned": "v1.3.0", "track": "release", "tagPattern": "^v\\d+\\.\\d+\\.\\d+$", "verify": "docs/reference/cli.md", - "affects": "Every `icp` command and flag in docs/. A new minor also moves the cli.internetcomputer.org link slug (AGENTS.md 'Linking rules'). NOTE: the doc links were moved to the 1.3 slug in #354, but the commands themselves were last verified against v1.1.0, so the first bump of this pin carries that verification pass.", + "affects": "Every `icp` command and flag in docs/. A new minor also moves the cli.internetcomputer.org link slug (AGENTS.md 'Linking rules'), so check the slug procedure in .agents/upstream-tracking.md when the minor changes.", "reference": "https://cli.internetcomputer.org/" }, { "repo": "dfinity/motoko-core", - "pinned": "v2.4.0", + "pinned": "v2.6.1", "track": "release", "tagPattern": "^v\\d+\\.\\d+\\.\\d+$", "affects": "Every `mo:core` import and API signature in Motoko code blocks.", @@ -64,7 +64,7 @@ }, { "repo": "dfinity/cdk-rs", - "pinned": "0.20.1", + "pinned": "0.20.2", "track": "crate", "crate": "ic-cdk", "affects": "`ic-cdk` APIs in Rust code blocks. The repo stopped tagging releases (its newest bare-semver tag is two minors behind the published crate), so the crate version on crates.io is the release identity. Read the sections newer than the pin in https://github.com/dfinity/cdk-rs/blob/master/ic-cdk/CHANGELOG.md, then grep docs/ for the symbols they name. `ic-cdk-timers` and `ic-cdk-executor` version separately; check whether they moved too.", @@ -72,7 +72,7 @@ }, { "repo": "dfinity/icp-js-core", - "pinned": "v5.4.0", + "pinned": "v6.1.0", "track": "release", "tagPattern": "^v\\d+\\.\\d+\\.\\d+$", "verify": "CHANGELOG.md", @@ -81,7 +81,7 @@ }, { "repo": "dfinity/icp-js-canisters", - "pinned": "3.5.0", + "pinned": "3.6.0", "track": "npm", "package": "@icp-sdk/canisters", "affects": "`@icp-sdk/canisters` APIs, notably `AssetManager` in references/application-canisters.md and guides/frontends/asset-canister.md, which states a minimum of 3.5.0. This repo's semver tags lag its published versions, so npm is the release signal. Changelog: https://github.com/dfinity/icp-js-canisters/blob/main/CHANGELOG.md", @@ -90,7 +90,7 @@ { "repo": "dfinity/icp-cli-recipes", "name": "asset-canister", - "pinned": "asset-canister-v2.2.1", + "pinned": "asset-canister-v2.3.0", "track": "release", "tagPattern": "^asset-canister-v\\d+\\.\\d+\\.\\d+$", "affects": "Cited in 10 places across docs/, including references/application-canisters.md and guides/frontends/asset-canister.md. The legacy frontend recipe; static-site supersedes it. Each recipe in this repo tags and releases on its own prefix, so it gets its own entry: a single pattern across prefixes would compare `rust-` against `static-site-` textually and only ever surface one of them.", @@ -108,7 +108,7 @@ { "repo": "dfinity/icp-cli-recipes", "name": "rust", - "pinned": "rust-v3.3.0", + "pinned": "rust-v3.4.0", "track": "release", "tagPattern": "^rust-v\\d+\\.\\d+\\.\\d+$", "affects": "Cited in 6 places as the Rust canister recipe in icp.yaml examples. Each recipe in this repo tags and releases on its own prefix, so it gets its own entry: a single pattern across prefixes would compare `rust-` against `static-site-` textually and only ever surface one of them.", @@ -117,7 +117,7 @@ { "repo": "dfinity/icp-cli-recipes", "name": "motoko", - "pinned": "motoko-v5.0.0", + "pinned": "motoko-v5.1.0", "track": "release", "tagPattern": "^motoko-v\\d+\\.\\d+\\.\\d+$", "affects": "Cited in 3 places as the Motoko canister recipe in icp.yaml examples. Each recipe in this repo tags and releases on its own prefix, so it gets its own entry: a single pattern across prefixes would compare `rust-` against `static-site-` textually and only ever surface one of them.", @@ -126,7 +126,7 @@ { "repo": "dfinity/icp-cli-recipes", "name": "prebuilt", - "pinned": "prebuilt-v2.0.0", + "pinned": "prebuilt-v2.1.0", "track": "release", "tagPattern": "^prebuilt-v\\d+\\.\\d+\\.\\d+$", "affects": "Cited once, in guides/canister-management/reproducible-builds.md. Each recipe in this repo tags and releases on its own prefix, so it gets its own entry: a single pattern across prefixes would compare `rust-` against `static-site-` textually and only ever surface one of them.", diff --git a/docs/getting-started/project-structure.mdx b/docs/getting-started/project-structure.mdx index 894f9d58..61e3b455 100644 --- a/docs/getting-started/project-structure.mdx +++ b/docs/getting-started/project-structure.mdx @@ -87,7 +87,7 @@ The Motoko backend uses the `@dfinity/motoko` recipe, which compiles via `mops b ```yaml name: backend recipe: - type: "@dfinity/motoko@v5.0.0" + type: "@dfinity/motoko@v5.1.0" ``` The recipe takes no source or Candid configuration. Instead, the source file (and optionally the Candid file and compiler flags) are declared in a `mops.toml` at the project root. The `[canisters]` key must match the canister `name`: @@ -106,7 +106,7 @@ The Rust backend uses the `@dfinity/rust` recipe: ```yaml name: backend recipe: - type: "@dfinity/rust@v3.3.0" + type: "@dfinity/rust@v3.4.0" configuration: shrink: true candid: backend.did @@ -123,7 +123,7 @@ The frontend uses the `@dfinity/asset-canister` recipe, which builds the fronten ```yaml name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: build: - npm install diff --git a/docs/guides/canister-calls/candid.mdx b/docs/guides/canister-calls/candid.mdx index cf38a8a0..b32de164 100644 --- a/docs/guides/canister-calls/candid.mdx +++ b/docs/guides/canister-calls/candid.mdx @@ -110,7 +110,7 @@ You can also provide a hand-written `.did` file by setting the `candid` field fo canisters: - name: backend recipe: - type: "@dfinity/motoko@v5.0.0" + type: "@dfinity/motoko@v5.1.0" ``` Declare the source and Candid file under the canister's entry in `mops.toml`: @@ -166,7 +166,7 @@ Reference the generated `.did` file in your `icp.yaml`: canisters: - name: my_canister recipe: - type: "@dfinity/rust@v3.3.0" + type: "@dfinity/rust@v3.4.0" configuration: candid: src/my_canister/my_canister.did ``` diff --git a/docs/guides/canister-calls/inter-canister-calls.mdx b/docs/guides/canister-calls/inter-canister-calls.mdx index 0d1cdc5e..8b9a5c95 100644 --- a/docs/guides/canister-calls/inter-canister-calls.mdx +++ b/docs/guides/canister-calls/inter-canister-calls.mdx @@ -201,7 +201,7 @@ Deployment order does not matter: `icp deploy` creates all canisters first, then **Init arguments.** Accept the target `Principal` as an `#[init]` argument and store it. This avoids the environment variable lookup at call time but requires passing the ID at every deploy and upgrade: ```bash -TARGET_ID=$(icp canister id counter) +TARGET_ID=$(icp canister status counter -i) icp deploy my_canister --argument "(principal \"$TARGET_ID\")" ``` diff --git a/docs/guides/canister-management/large-wasm.md b/docs/guides/canister-management/large-wasm.md index eeb9bdeb..9ed9c6fd 100644 --- a/docs/guides/canister-management/large-wasm.md +++ b/docs/guides/canister-management/large-wasm.md @@ -34,7 +34,7 @@ The Rust and prebuilt recipes expose a `compress` flag that gzip-compresses the canisters: - name: backend recipe: - type: "@dfinity/rust@v3.3.0" + type: "@dfinity/rust@v3.4.0" configuration: shrink: true compress: true diff --git a/docs/guides/canister-management/logs.md b/docs/guides/canister-management/logs.md index 85727496..f0dfd4ae 100644 --- a/docs/guides/canister-management/logs.md +++ b/docs/guides/canister-management/logs.md @@ -155,7 +155,7 @@ You can configure log visibility per canister in `icp.yaml` so it is applied on canisters: - name: backend recipe: - type: "@dfinity/rust@v3.3.0" + type: "@dfinity/rust@v3.4.0" settings: log_visibility: controllers # "controllers" | "public" | allowed_viewers object ``` diff --git a/docs/guides/canister-management/optimization.md b/docs/guides/canister-management/optimization.md index d280082d..90268576 100644 --- a/docs/guides/canister-management/optimization.md +++ b/docs/guides/canister-management/optimization.md @@ -26,7 +26,7 @@ This guide covers the main tools and techniques available: canisters: - name: backend recipe: - type: "@dfinity/rust@v3.3.0" + type: "@dfinity/rust@v3.4.0" configuration: shrink: true ``` diff --git a/docs/guides/canister-management/reproducible-builds.md b/docs/guides/canister-management/reproducible-builds.md index 5f26b566..87872dc5 100644 --- a/docs/guides/canister-management/reproducible-builds.md +++ b/docs/guides/canister-management/reproducible-builds.md @@ -187,7 +187,7 @@ If you have already built a Wasm and computed its hash, you can deploy it using canisters: - name: my-canister recipe: - type: "@dfinity/prebuilt@v2.0.0" + type: "@dfinity/prebuilt@v2.1.0" configuration: path: dist/my-canister.wasm sha256: d7c1aba0de1d7152897aeca49bd5fe89a174b076a0ee1cc3b9e45fcf6bde71a6 diff --git a/docs/guides/chain-fusion/bitcoin.mdx b/docs/guides/chain-fusion/bitcoin.mdx index a4487114..f0f82fcd 100644 --- a/docs/guides/chain-fusion/bitcoin.mdx +++ b/docs/guides/chain-fusion/bitcoin.mdx @@ -47,6 +47,7 @@ import Blob "mo:core/Blob"; import Nat8 "mo:core/Nat8"; import Array "mo:core/Array"; import Runtime "mo:core/Runtime"; +import Nat "mo:core/Nat"; persistent actor Self { @@ -63,11 +64,11 @@ persistent actor Self { let bytes = Blob.toArray(Principal.toBlob(p)); let size = bytes.size(); let sub = Array.tabulate(32, func(i : Nat) : Nat8 { - if (i == 0) { Nat8.fromNat(size) } + if (i == 0) { Nat.toNat8(size) } else if (i <= size) { bytes[i - 1] } else { 0 } }); - Blob.fromArray(sub) + Array.toBlob(sub) }; public shared ({ caller }) func getDepositAddress() : async Text { diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index 631ea161..bf81396a 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -14,7 +14,7 @@ The signer charges callers in cycles for each API call. You pre-approve the sign ## Prerequisites - An ICP identity with cycles in the [Cycles Ledger](../../references/system-canisters.md#cycles-ledger) (`um5iw-rqaaa-aaaaq-qaaba-cai`) -- icp-cli installed and authenticated (`icp identity whoami`) +- icp-cli installed and authenticated (`icp identity principal`) - For offline address derivation: Node.js and `npx` ## Approve payment diff --git a/docs/guides/chain-fusion/ethereum.mdx b/docs/guides/chain-fusion/ethereum.mdx index 637edfe3..4dfbaf9d 100644 --- a/docs/guides/chain-fusion/ethereum.mdx +++ b/docs/guides/chain-fusion/ethereum.mdx @@ -237,6 +237,7 @@ To read an ERC-20 balance, use `eth_call` with the `balanceOf(address)` function import EvmRpc "canister:evm_rpc"; import Runtime "mo:core/Runtime"; import Text "mo:core/Text"; +import Char "mo:core/Char"; persistent actor { @@ -290,7 +291,7 @@ persistent actor { switch (chars.next(), chars.next()) { case (?"0", ?"x") { var rest = ""; - for (c in chars) { rest #= Text.fromChar(c) }; + for (c in chars) { rest #= Char.toText(c) }; rest; }; case _ { hex }; @@ -593,7 +594,7 @@ Add the EVM RPC canister to your `icp.yaml` as a pre-built canister for local de canisters: - name: backend recipe: - type: "@dfinity/motoko@v5.0.0" + type: "@dfinity/motoko@v5.1.0" - name: evm_rpc build: steps: diff --git a/docs/guides/chain-fusion/exchange-rates.mdx b/docs/guides/chain-fusion/exchange-rates.mdx index 4a86040b..445ddc9b 100644 --- a/docs/guides/chain-fusion/exchange-rates.mdx +++ b/docs/guides/chain-fusion/exchange-rates.mdx @@ -93,8 +93,8 @@ persistent actor { switch result { case (#Ok rate) { - let scale = Float.fromInt(Int.pow(10, Nat32.toNat(rate.metadata.decimals))); - ?(Float.fromInt(Nat64.toNat(rate.rate)) / scale) + let scale = Int.toFloat(Int.pow(10, Nat32.toNat(rate.metadata.decimals))); + ?(Int.toFloat(Nat64.toNat(rate.rate)) / scale) }; case (#Err err) { // handle specific errors as needed (see Error handling section below) @@ -198,7 +198,7 @@ For reliability, use the start of the **previous minute** rather than the curren ```motoko -let oneMinuteAgo : Nat64 = (Nat64.fromNat(Int.abs(Time.now())) / 1_000_000_000) - 60; +let oneMinuteAgo : Nat64 = (Nat.toNat64(Int.abs(Time.now())) / 1_000_000_000) - 60; let request : GetExchangeRateRequest = { base_asset = { symbol = "ICP"; class_ = #Cryptocurrency }; diff --git a/docs/guides/digital-assets/chain-key-tokens.mdx b/docs/guides/digital-assets/chain-key-tokens.mdx index d20b4c39..5bc9e9d4 100644 --- a/docs/guides/digital-assets/chain-key-tokens.mdx +++ b/docs/guides/digital-assets/chain-key-tokens.mdx @@ -220,11 +220,11 @@ persistent actor Self { let bytes = Blob.toArray(Principal.toBlob(p)); let size = bytes.size(); let sub = Array.tabulate(32, func(i : Nat) : Nat8 { - if (i == 0) { Nat8.fromNat(size) } + if (i == 0) { Nat.toNat8(size) } else if (i <= size) { bytes[i - 1] } else { 0 } }); - Blob.fromArray(sub) + Array.toBlob(sub) }; // Withdraw ckBTC to a Bitcoin address (minimum 50,000 satoshis) @@ -234,7 +234,7 @@ persistent actor Self { let minterPrincipal = Principal.fromText("mqygn-kiaaa-aaaar-qaadq-cai"); // Set created_at_time for deduplication: two identical approvals within 24h // would both execute without this. Omit if you intentionally allow retries. - let now = ?Nat64.fromNat(Int.abs(Time.now())); + let now = ?Nat.toNat64(Int.abs(Time.now())); // Step 1: approve the minter to spend ckBTC (amount + fee) let approveResult = await ckbtcLedger.icrc2_approve({ diff --git a/docs/guides/digital-assets/ledgers.mdx b/docs/guides/digital-assets/ledgers.mdx index 56384eb7..967cce16 100644 --- a/docs/guides/digital-assets/ledgers.mdx +++ b/docs/guides/digital-assets/ledgers.mdx @@ -73,7 +73,7 @@ persistent actor { /// Transfer tokens from this canister's default account. /// WARNING: Add access control in production. public func sendTokens(to : Principal, amount : Nat) : async Nat { - let now = Nat64.fromNat(Int.abs(Time.now())); + let now = Nat.toNat64(Int.abs(Time.now())); let result = await icpLedger.icrc1_transfer({ from_subaccount = null; to = { owner = to; subaccount = null }; @@ -180,7 +180,7 @@ Without `created_at_time` (set to `null`), every submission is treated as a new Set `created_at_time` to the current nanosecond timestamp to enable deduplication: -- **Motoko**: `Nat64.fromNat(Int.abs(Time.now()))` (as shown in `sendTokens` above) +- **Motoko**: `Nat.toNat64(Int.abs(Time.now()))` (as shown in `sendTokens` above) - **Rust**: `ic_cdk::api::time()` (as shown in `send_tokens` above) Two boundary errors to handle alongside the normal transfer errors: @@ -319,7 +319,7 @@ persistent actor { }; public func approveSpender(spender : Principal, amount : Nat) : async Nat { - let now = Nat64.fromNat(Int.abs(Time.now())); + let now = Nat.toNat64(Int.abs(Time.now())); let result = await icpLedger.icrc2_approve({ from_subaccount = null; spender = { owner = spender; subaccount = null }; @@ -338,7 +338,7 @@ persistent actor { /// WARNING: Add access control in production. public func transferFrom(from : Principal, to : Principal, amount : Nat) : async Nat { - let now = Nat64.fromNat(Int.abs(Time.now())); + let now = Nat.toNat64(Int.abs(Time.now())); let result = await icpLedger.icrc2_transfer_from({ spender_subaccount = null; from = { owner = from; subaccount = null }; @@ -445,6 +445,7 @@ import Principal "mo:core/Principal"; import Blob "mo:core/Blob"; import Array "mo:core/Array"; import Nat8 "mo:core/Nat8"; +import Nat "mo:core/Nat"; type Account = { owner : Principal; subaccount : ?Blob }; @@ -452,11 +453,11 @@ type Account = { owner : Principal; subaccount : ?Blob }; func depositAccount(canister : Principal, user : Principal) : Account { let bytes = Blob.toArray(Principal.toBlob(user)); let subaccount = Array.tabulate(32, func(i) { - if (i == 0) { Nat8.fromNat(bytes.size()) } + if (i == 0) { Nat.toNat8(bytes.size()) } else if (i <= bytes.size()) { bytes[i - 1] } else { 0 } }); - { owner = canister; subaccount = ?Blob.fromArray(subaccount) } + { owner = canister; subaccount = ?Array.toBlob(subaccount) } }; ``` diff --git a/docs/guides/frontends/asset-canister.md b/docs/guides/frontends/asset-canister.md index 0421b902..fa50b32f 100644 --- a/docs/guides/frontends/asset-canister.md +++ b/docs/guides/frontends/asset-canister.md @@ -44,7 +44,7 @@ Define an asset canister using the `@dfinity/asset-canister` recipe in your `icp canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: dir: dist build: @@ -64,7 +64,7 @@ For a fullstack project with a backend canister, list both in the same `icp.yaml canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: dir: dist build: @@ -72,7 +72,7 @@ canisters: - npm run build - name: backend recipe: - type: "@dfinity/rust@v3.3.0" + type: "@dfinity/rust@v3.4.0" ``` For more on project configuration, see [Project structure](../../getting-started/project-structure.md). diff --git a/docs/guides/frontends/frameworks.md b/docs/guides/frontends/frameworks.md index c883ebaf..46b83a6e 100644 --- a/docs/guides/frontends/frameworks.md +++ b/docs/guides/frontends/frameworks.md @@ -36,7 +36,7 @@ The [hello-world template](../../getting-started/project-structure.md) uses Reac canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: build: - npm install @@ -214,7 +214,7 @@ export default { canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: build: - npm install @@ -246,7 +246,7 @@ This outputs static files to the `out/` directory. canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: build: - npm install @@ -271,7 +271,7 @@ Export your game from Unity Editor: **File → Build Settings → WebGL → Buil canisters: - name: unity_webgl_template_assets recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: dir: dist build: @@ -293,7 +293,7 @@ Export your game from Godot Editor: **Project → Export → HTML5 → Export Pr canisters: - name: godot_html5_assets recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: dir: dist build: @@ -330,7 +330,7 @@ For sites with no backend canister (portfolios, landing pages, documentation): canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: build: - npm install diff --git a/docs/guides/governance/testing.md b/docs/guides/governance/testing.md index d36dcd72..432898e3 100644 --- a/docs/guides/governance/testing.md +++ b/docs/guides/governance/testing.md @@ -213,11 +213,11 @@ Add the SNS root canister as an **additional** controller of each app canister. ```bash # Locally: icp canister settings update test \ - --add-controller $(icp canister id sns_root) + --add-controller $(icp canister status sns_root -i) # On mainnet: icp canister settings update test \ - --add-controller $(icp canister id sns_root -e ic) \ + --add-controller $(icp canister status sns_root -e ic -i) \ -e ic ``` @@ -231,7 +231,7 @@ export DEVELOPER_NEURON_ID="" # is where dfx stores PEM files. If you created your identity with icp-cli, the path # may differ: check ~/.config/icp/identity/ or the path shown by `icp identity export`. export PEM_FILE="$HOME/.config/dfx/identity/$(icp identity default)/identity.pem" -export CID="$(icp canister id test -e ic)" +export CID="$(icp canister status test -e ic -i)" ``` Submit the registration proposal: diff --git a/docs/guides/security/data-integrity-and-authenticity.md b/docs/guides/security/data-integrity-and-authenticity.md index eaa5d62c..f4222a77 100644 --- a/docs/guides/security/data-integrity-and-authenticity.md +++ b/docs/guides/security/data-integrity-and-authenticity.md @@ -91,6 +91,7 @@ import Debug "mo:core/Debug"; import Text "mo:core/Text"; import Nat64 "mo:core/Nat64"; import Array "mo:core/Array"; +import Nat "mo:core/Nat"; import CertTree "mo:ic-certification/CertTree"; import CV "mo:cbor/Value"; import CborEncoder "mo:cbor/Encoder"; @@ -157,11 +158,11 @@ actor CertifiedVariable { func encodeUser(user : User) : Blob { let bytes : CV.Value = #majorType5([ (#majorType3("name"), #majorType3(user.name)), - (#majorType3("age"), #majorType0(Nat64.fromNat(Nat8.toNat(user.age)))), + (#majorType3("age"), #majorType0(Nat.toNat64(Nat8.toNat(user.age)))), ]); let #ok(encoded_user) = CborEncoder.encode(bytes); - return Blob.fromArray(encoded_user); + return Array.toBlob(encoded_user); }; func decodeUser(bytes : Blob) : User { @@ -182,7 +183,7 @@ actor CertifiedVariable { let age = switch (age_tag) { case (?age_value) { let #majorType0(age) = age_value.1; - Nat8.fromNat(Nat64.toNat(age)); + Nat.toNat8(Nat64.toNat(age)); }; case (null) { Debug.trap("Decoding failed for age"); @@ -198,9 +199,9 @@ actor CertifiedVariable { func blobOfNat64(n : Nat64) : Blob { let byteMask : Nat64 = 0xff; func byte(x : Nat64) : Nat8 { - Nat8.fromNat(Nat64.toNat(x)); + Nat.toNat8(Nat64.toNat(x)); }; - Blob.fromArray([ + Array.toBlob([ byte(((byteMask << 56) & n) >> 56), byte(((byteMask << 48) & n) >> 48), byte(((byteMask << 40) & n) >> 40), diff --git a/docs/references/application-canisters.md b/docs/references/application-canisters.md index a787cfa9..e7ff05f2 100644 --- a/docs/references/application-canisters.md +++ b/docs/references/application-canisters.md @@ -19,7 +19,7 @@ Asset canisters are deployed per-project. There is no global asset canister ID: canisters: - name: frontend recipe: - type: "@dfinity/asset-canister@v2.2.1" + type: "@dfinity/asset-canister@v2.3.0" configuration: dir: dist build: From 002a39af6abfbdaf13b46c19bac7df4b724e3760 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 19:14:45 +0200 Subject: [PATCH 2/7] infra: stop opening release issues for the examples submodule Running the first batch showed the examples check earning nothing. The only thing a bump can break is a snippet= path or a #region marker, and plugins/remark-snippet.mjs fails the build on either, verified by renaming a region and watching the build fail. A bump that breaks nothing needs no doc change, so an issue per upstream commit on an active repo is pure noise. It moves to the reference group with that reasoning recorded. The vendored group stays in the schema for a submodule that has no check of its own; it is empty today, since motoko and internetidentity have their own sync workflows. --- .agents/upstream-tracking.md | 20 +++++++------- .sources/upstream.json | 42 +++++++++++++---------------- AGENTS.md | 7 ++--- scripts/check-upstream-releases.mjs | 7 ++--- 4 files changed, 37 insertions(+), 39 deletions(-) diff --git a/.agents/upstream-tracking.md b/.agents/upstream-tracking.md index c8bd387a..7922aece 100644 --- a/.agents/upstream-tracking.md +++ b/.agents/upstream-tracking.md @@ -7,18 +7,18 @@ Upstream repos fall into three groups, and the group decides the procedure. | | Vendored (submodule) | Watched | Reference | |---|---|---|---| | Which | `motoko`, `internetidentity`, `examples` | the `watched` array in `.sources/upstream.json` | the `reference` array | -| Why | the build opens their files | a release can silently invalidate a lot of published content | drawn on too lightly for a weekly issue | +| Why | the build opens their files | a release can silently invalidate a lot of published content | drawn on too lightly, or another check already catches the drift | | Pin | the gitlink | `pinned` in `upstream.json` | none; verify against the latest release | -| Notified | yes | yes | no | +| Release issue | no: each has its own check | yes | no | Deciding between the last two is a judgment about blast radius, and the `why` field on each `reference` entry records the footprint that decided it. Promote an entry to `watched` if its footprint grows. `motoko` and `internetidentity` have their own weekly sync workflows that open -the bump PR directly. `examples` and the `watched` repos are covered by the -weekly **Upstream release check**, which opens an issue. `reference` repos are -not checked at all. +the bump PR directly. The `watched` repos are covered by the weekly **Upstream +release check**, which opens an issue. `examples` and the `reference` repos get +no issue. ## Why only three are vendored @@ -167,10 +167,12 @@ When `icp-cli` moves to a new minor: Only the project maintainer bumps submodule refs. -`examples` tracks a branch and is checked by the same **Upstream release check** -workflow, which opens an issue when the gitlink falls behind that branch. Its pin -lives in git, so `upstream.json` records only the branch to compare against and -what a bump affects. +`examples` gets no release issue. The only thing a bump can break is a `snippet=` +path or `#region` marker, and `plugins/remark-snippet.mjs` fails the build on +either, so a bump that breaks nothing needs no doc change. Bump it when a page +needs newer example code and let the build verify it. The `vendored` group in +`upstream.json` still exists for a submodule that has no check of its own; it is +empty today. `motoko` and `internetidentity` are not in `upstream.json`: `sync-motoko.yml` and `sync-ii-spec.yml` already check for a new release, run the sync, and open the diff --git a/.sources/upstream.json b/.sources/upstream.json index 7997968c..09769931 100644 --- a/.sources/upstream.json +++ b/.sources/upstream.json @@ -1,48 +1,36 @@ { "$comment": [ - "Every upstream this repo tracks, in three groups.", - "", - "`vendored` are submodules whose pin has no sync workflow of its own. Their pin", - "is the gitlink in git, not a field here, so it has one source of truth; the entry", - "records which branch to compare against and what a bump affects. `motoko` and", - "`internetidentity` are absent on purpose: sync-motoko.yml and sync-ii-spec.yml", - "already check for a new release and open the bump PR themselves.", + "Every upstream this repo tracks, in two groups.", "", "`watched` are repos where a release can silently invalidate a lot of published", "content, so they get a pinned ref and a weekly issue when they move. Verify", "claims against the pinned ref, not against main, so a review is reproducible.", "", - "`reference` are repos the docs draw on too lightly to be worth a weekly issue:", - "each `why` records the footprint that decided it. They carry no pin. Verify", - "against the latest release on demand; the surface is small enough that the next", - "edit to those pages catches any drift. Promote one to `watched` if its footprint", - "grows.", + "`reference` are repos the docs draw on too lightly, or whose drift another check", + "already catches: each `why` records what decided it. They carry no pin. Verify", + "against the latest release on demand. Promote one to `watched` if that changes.", + "", + "The three submodules are not watched here. `motoko` and `internetidentity` have", + "their own sync workflows that release-check and open the bump PR themselves;", + "`examples` is covered by the build, which fails on a broken snippet region.", "", - "scripts/check-upstream-releases.mjs checks `vendored` and `watched` only, and", + "scripts/check-upstream-releases.mjs checks `watched`, and", ".github/workflows/upstream-releases.yml opens an issue per repo that moved.", "See AGENTS.md 'Source material' and .agents/upstream-tracking.md.", "", "Fields (watched):", " repo owner/name on GitHub", + " name optional, when one repo releases several things independently", " pinned the ref docs are currently verified against", " track where releases appear: 'release' (git tags matching tagPattern),", " 'crate' (crates.io, needs `crate`), 'npm' (npm registry, needs", - " `package`), or 'commit' (no releases; the default branch head).", - " Entries in `vendored` use 'branch' and name the branch instead.", + " `package`), or 'commit' (no releases; the default branch head)", " tagPattern anchored JS regex selecting the tags that count as a release", " verify file whose diff between the two refs is the review payload", " affects what to re-check in docs/ when this moves", " reference authoritative published docs for the topic" ], - "vendored": [ - { - "path": ".sources/examples", - "repo": "dfinity/examples", - "track": "branch", - "branch": "master", - "affects": "Every `snippet=` path and `#region` marker resolved at build time by plugins/remark-snippet.mjs. A renamed region is a build error, so verify each one after bumping." - } - ], + "vendored": [], "watched": [ { "repo": "dfinity/icp-cli", @@ -134,6 +122,12 @@ } ], "reference": [ + { + "repo": "dfinity/examples", + "verify": "the paths and `#region` markers named by `snippet=` in docs/", + "why": "A submodule, but not watched: the only thing a bump can break is a snippet path or region, and `plugins/remark-snippet.mjs` already fails the build on either. A bump that does not break one needs no doc change, so an issue per upstream commit would be pure noise. Bump it when a page needs newer example code, and let the build verify it.", + "reference": "https://github.com/dfinity/examples" + }, { "repo": "dfinity/icp-cli-templates", "verify": "the template's `icp.yaml`", diff --git a/AGENTS.md b/AGENTS.md index f5900d83..1e5e9f0a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -229,9 +229,10 @@ git submodule update --init --depth 1 # do NOT use --recursive | Code examples (`snippet=`, ``) | `.sources/examples/` | Pinned versions: [`.sources/VERSIONS`](.sources/VERSIONS). `motoko` and -`internetidentity` are release-checked and synced by their own workflows; -`examples` tracks a branch and is checked by the weekly **Upstream release -check**. Canister IDs and code patterns are in the skills (see "Skills"). +`internetidentity` are release-checked and synced by their own workflows. +`examples` is bumped when a page needs newer example code; the build fails if a +`snippet=` path or `#region` marker stops resolving, so it needs no separate +check. Canister IDs and code patterns are in the skills (see "Skills"). **Watched** are the repos where a release can silently invalidate a lot of what is published, so [`.sources/upstream.json`](.sources/upstream.json) records the diff --git a/scripts/check-upstream-releases.mjs b/scripts/check-upstream-releases.mjs index 0f3049a6..6921f3d8 100644 --- a/scripts/check-upstream-releases.mjs +++ b/scripts/check-upstream-releases.mjs @@ -3,9 +3,10 @@ * Check every upstream in `.sources/upstream.json` for a newer ref than the one * the docs are pinned to, and write an issue body for each that moved. * - * Covers both groups in that file: `vendored` submodules whose pin has no sync - * workflow of its own (their pin is read from the gitlink, so git stays the - * single source of truth), and `watched` repos that are not vendored at all. + * Covers the `watched` group. The optional `vendored` group compares a + * submodule's gitlink against a branch head, so git stays the single source of + * truth for its pin; it is empty today because every submodule this repo keeps + * has a check of its own (a sync workflow, or the build). * * A watched repo declares where its releases actually appear: `release` (git * tags matching a pattern), `crate` (crates.io) or `npm` (the npm registry) for From f661a759bc9d254498d9372ecf60a339e813951c Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 19:29:39 +0200 Subject: [PATCH 3/7] infra: report an examples bump only when it touches a quoted file Dropping the examples check entirely was the wrong call. The build catches a snippet= path or #region marker that stops resolving, but it cannot see the case that matters more: an example the docs quote gets corrected upstream, still resolves, and the docs keep showing the old code with a green build. The vendored check now compares the pinned gitlink against the branch head, asks the compare API which files moved, and intersects that with every file quoted by a snippet= in docs/. An unrelated commit on an active repo stays silent; a commit touching a quoted file opens an issue naming the files. Verified both ways against real history. Pinned at 01edb5d8, which precedes a fix to the send_http_get and send_http_post examples, it reports and names motoko/send_http_get/backend/main.mo and motoko/send_http_post/backend/main.mo. Pinned at b4fe175, where the 78 files in the range touch none of the four quoted files, it stays silent. The snippet path is /, where the directory comes from the fence language, so the language map from plugins/remark-snippet.mjs is mirrored here; that plugin cannot be imported because it pulls in remark dependencies the workflow does not install. An unmapped language throws rather than silently matching fewer files. If the comparison cannot be trusted, the API failing or truncating at its 300-file cap, the check reports instead of assuming nothing moved. --- .sources/upstream.json | 41 +++++++----- scripts/check-upstream-releases.mjs | 97 +++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 22 deletions(-) diff --git a/.sources/upstream.json b/.sources/upstream.json index 09769931..fd9306b6 100644 --- a/.sources/upstream.json +++ b/.sources/upstream.json @@ -1,20 +1,21 @@ { "$comment": [ - "Every upstream this repo tracks, in two groups.", + "Every upstream this repo tracks, in three groups.", "", "`watched` are repos where a release can silently invalidate a lot of published", "content, so they get a pinned ref and a weekly issue when they move. Verify", "claims against the pinned ref, not against main, so a review is reproducible.", "", - "`reference` are repos the docs draw on too lightly, or whose drift another check", - "already catches: each `why` records what decided it. They carry no pin. Verify", - "against the latest release on demand. Promote one to `watched` if that changes.", + "`vendored` are submodules whose pin has no sync workflow of its own. Their pin", + "is the gitlink, so git stays the single source of truth. `motoko` and", + "`internetidentity` are absent on purpose: sync-motoko.yml and sync-ii-spec.yml", + "already release-check and open the bump PR themselves.", "", - "The three submodules are not watched here. `motoko` and `internetidentity` have", - "their own sync workflows that release-check and open the bump PR themselves;", - "`examples` is covered by the build, which fails on a broken snippet region.", + "`reference` are repos the docs draw on too lightly to be worth a weekly issue:", + "each `why` records the footprint that decided it. They carry no pin. Verify", + "against the latest release on demand. Promote one if that changes.", "", - "scripts/check-upstream-releases.mjs checks `watched`, and", + "scripts/check-upstream-releases.mjs checks `watched` and `vendored`, and", ".github/workflows/upstream-releases.yml opens an issue per repo that moved.", "See AGENTS.md 'Source material' and .agents/upstream-tracking.md.", "", @@ -28,9 +29,23 @@ " tagPattern anchored JS regex selecting the tags that count as a release", " verify file whose diff between the two refs is the review payload", " affects what to re-check in docs/ when this moves", - " reference authoritative published docs for the topic" + " reference authoritative published docs for the topic", + "", + "Fields (vendored): path, repo, track 'branch', branch, affects, and an optional", + "pathFilter. 'snippets' reports only when the range touched a file quoted by a", + "`snippet=` in docs/, so an unrelated commit on an active repo stays silent." + ], + "vendored": [ + { + "path": ".sources/examples", + "repo": "dfinity/examples", + "track": "branch", + "branch": "master", + "pathFilter": "snippets", + "affects": "Bump the submodule and re-read the affected pages: the example moved on, so the code the docs quote is behind. The build already fails if a `snippet=` path or `#region` marker stops resolving, so this reports the case the build cannot see, an example that changed while still resolving.", + "reference": "https://github.com/dfinity/examples" + } ], - "vendored": [], "watched": [ { "repo": "dfinity/icp-cli", @@ -122,12 +137,6 @@ } ], "reference": [ - { - "repo": "dfinity/examples", - "verify": "the paths and `#region` markers named by `snippet=` in docs/", - "why": "A submodule, but not watched: the only thing a bump can break is a snippet path or region, and `plugins/remark-snippet.mjs` already fails the build on either. A bump that does not break one needs no doc change, so an issue per upstream commit would be pure noise. Bump it when a page needs newer example code, and let the build verify it.", - "reference": "https://github.com/dfinity/examples" - }, { "repo": "dfinity/icp-cli-templates", "verify": "the template's `icp.yaml`", diff --git a/scripts/check-upstream-releases.mjs b/scripts/check-upstream-releases.mjs index 6921f3d8..883e0b17 100644 --- a/scripts/check-upstream-releases.mjs +++ b/scripts/check-upstream-releases.mjs @@ -28,8 +28,8 @@ */ import { execFileSync } from 'node:child_process'; -import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; -import { join, resolve } from 'node:path'; +import { readFileSync, writeFileSync, mkdirSync, readdirSync, statSync } from 'node:fs'; +import { join, resolve, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = resolve(fileURLToPath(import.meta.url), '..', '..'); @@ -93,6 +93,71 @@ function defaultBranchHead(repo) { return { branch, sha }; } +/** + * Language-to-directory map, mirroring LANG_TO_DIR in plugins/remark-snippet.mjs. + * It is copied rather than imported because that plugin pulls in remark + * dependencies and this script must run in the workflow, which installs none. + * Keep the two in step; an unknown language here fails the check rather than + * silently reporting fewer files than the docs actually quote. + */ +const LANG_TO_DIR = { + rust: 'rust', + rs: 'rust', + motoko: 'motoko', + mo: 'motoko', + javascript: 'hosting', + js: 'hosting', +}; + +/** + * Every file in the examples repo that a `snippet=` in docs/ quotes, as a + * repo-relative path. The attribute is relative to `.sources/examples//`, + * where the directory comes from the fence's language, so both parts are needed + * to reconstruct the path the compare API reports. + */ +function snippetPaths() { + const out = new Set(); + const walk = (dir) => { + for (const entry of readdirSync(dir)) { + const full = join(dir, entry); + if (statSync(full).isDirectory()) walk(full); + else if (/\.mdx?$/.test(entry)) { + const text = readFileSync(full, 'utf8'); + for (const m of text.matchAll(/^```(\w+)[^\n]*\ssnippet="([^"#]+)/gm)) { + const [, lang, path] = m; + const dir = LANG_TO_DIR[lang]; + if (!dir) { + throw new Error( + `${relative(ROOT, full)}: snippet in an unmapped language "${lang}"; ` + + `add it to LANG_TO_DIR here and in plugins/remark-snippet.mjs` + ); + } + out.add(`${dir}/${path}`); + } + } + } + }; + walk(join(ROOT, 'docs')); + return out; +} + +/** + * Files changed between two refs, via the compare API. Returns null when the + * answer cannot be trusted (request failed, or the response was truncated at + * the API's 300-file cap), so callers report rather than assume nothing moved. + */ +async function changedFiles(repo, base, head) { + const headers = { Accept: 'application/vnd.github+json' }; + const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN; + if (token) headers.Authorization = `Bearer ${token}`; + const res = await fetch(`https://api.github.com/repos/${repo}/compare/${base}...${head}`, { headers }); + if (!res.ok) return null; + const body = await res.json(); + if (!Array.isArray(body.files)) return null; + if (body.files.length >= 300) return null; + return new Set(body.files.map((f) => f.filename)); +} + function gitlinkSha(path) { // The committed submodule pointer, readable without initializing the // submodule, so this works on a bare checkout in CI. @@ -177,8 +242,8 @@ function slugFor(entry) { return entry.name ? `${base}-${entry.name}` : base; } -function checkVendored(entry) { - const { path, repo, branch, affects } = entry; +async function checkVendored(entry) { + const { path, repo, branch, affects, pathFilter } = entry; const pinnedSha = gitlinkSha(path); if (!pinnedSha) throw new Error(`${path}: not a submodule in this commit`); const headSha = branchHead(repo, branch); @@ -187,6 +252,22 @@ function checkVendored(entry) { // Short SHAs are for the table and the issue title; anything a machine // consumes (the compare link, the checkout command) gets the full SHA. + let touched; + if (pathFilter === 'snippets') { + // A commit on an active examples repo is not news by itself. What matters + // is whether it touched a file the docs quote: an improvement upstream + // leaves our copy stale while the build stays green, since the build only + // catches a path or region that stopped resolving. + const wanted = snippetPaths(); + const changed = await changedFiles(repo, pinnedSha, headSha); + if (changed) { + touched = [...wanted].filter((f) => changed.has(f)); + if (touched.length === 0) return null; + } + // changed === null: the comparison could not be trusted, so fall through + // and report rather than assume nothing moved. + } + const pinned = pinnedSha.slice(0, 7); const latest = headSha.slice(0, 7); const name = path.replace(/^\.sources\//, ''); @@ -201,7 +282,11 @@ function checkVendored(entry) { '', '## What to re-check', '', - affects ?? 'No notes recorded for this submodule.', + touched?.length + ? 'Files the docs quote that changed in this range:\n\n' + + touched.map((f) => `- \`${f}\``).join('\n') + + '\n\n' + (affects ?? '') + : affects ?? 'No notes recorded for this submodule.', '', '## How to close this', '', @@ -380,7 +465,7 @@ let failed = false; for (const entry of vendored) { try { - const r = checkVendored(entry); + const r = await checkVendored(entry); if (r) { results.push(r); console.error(`behind: ${entry.path} -> ${entry.repo}@${entry.branch}`); From ffd00bac901b4510661d0c6a1f3ff9ecc95b4ac6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 19:30:18 +0200 Subject: [PATCH 4/7] docs: describe the snippet-aware examples check --- .agents/upstream-tracking.md | 16 +++++++++------- AGENTS.md | 6 +++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.agents/upstream-tracking.md b/.agents/upstream-tracking.md index 7922aece..7b44dfcd 100644 --- a/.agents/upstream-tracking.md +++ b/.agents/upstream-tracking.md @@ -9,7 +9,7 @@ Upstream repos fall into three groups, and the group decides the procedure. | Which | `motoko`, `internetidentity`, `examples` | the `watched` array in `.sources/upstream.json` | the `reference` array | | Why | the build opens their files | a release can silently invalidate a lot of published content | drawn on too lightly, or another check already catches the drift | | Pin | the gitlink | `pinned` in `upstream.json` | none; verify against the latest release | -| Release issue | no: each has its own check | yes | no | +| Release issue | only `examples`, and only when a quoted file moved | yes | no | Deciding between the last two is a judgment about blast radius, and the `why` field on each `reference` entry records the footprint that decided it. Promote an @@ -167,12 +167,14 @@ When `icp-cli` moves to a new minor: Only the project maintainer bumps submodule refs. -`examples` gets no release issue. The only thing a bump can break is a `snippet=` -path or `#region` marker, and `plugins/remark-snippet.mjs` fails the build on -either, so a bump that breaks nothing needs no doc change. Bump it when a page -needs newer example code and let the build verify it. The `vendored` group in -`upstream.json` still exists for a submodule that has no check of its own; it is -empty today. +`examples` is checked, but only for the case the build cannot see. A `snippet=` +path or `#region` marker that stops resolving already fails the build. What the +build cannot notice is an example being corrected upstream while still +resolving, which leaves the docs quoting old code with everything green. So its +entry carries `pathFilter: "snippets"`: the check intersects the files changed +between the pinned gitlink and the branch head with every file a `snippet=` +quotes, and opens an issue only when they overlap, naming the files. Any other +commit on that repo stays silent. `motoko` and `internetidentity` are not in `upstream.json`: `sync-motoko.yml` and `sync-ii-spec.yml` already check for a new release, run the sync, and open the diff --git a/AGENTS.md b/AGENTS.md index 1e5e9f0a..1d9a3b7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -230,9 +230,9 @@ git submodule update --init --depth 1 # do NOT use --recursive Pinned versions: [`.sources/VERSIONS`](.sources/VERSIONS). `motoko` and `internetidentity` are release-checked and synced by their own workflows. -`examples` is bumped when a page needs newer example code; the build fails if a -`snippet=` path or `#region` marker stops resolving, so it needs no separate -check. Canister IDs and code patterns are in the skills (see "Skills"). +`examples` is checked weekly, but reports only when a commit touches a file a +`snippet=` quotes; the build already fails if such a path or `#region` marker +stops resolving. Canister IDs and code patterns are in the skills (see "Skills"). **Watched** are the repos where a release can silently invalidate a lot of what is published, so [`.sources/upstream.json`](.sources/upstream.json) records the From c7d7f18bb6758f4ffa8156b7969f523c63fd6e10 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 19:46:09 +0200 Subject: [PATCH 5/7] fix: complete the mirrored language map, correct a stale header Addresses the Copilot review on #370. LANG_TO_DIR omitted the typescript and ts entries the plugin has, so a docs page using a ts snippet fence would have thrown 'unmapped language' even though the build resolves it. No page uses one today, which is why it was latent, and the comment above it already promised the two would be kept in step. They now match exactly. The header still said the vendored group was empty, written when it was, and not updated when the examples entry came back with pathFilter. --- scripts/check-upstream-releases.mjs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/check-upstream-releases.mjs b/scripts/check-upstream-releases.mjs index 883e0b17..571c4039 100644 --- a/scripts/check-upstream-releases.mjs +++ b/scripts/check-upstream-releases.mjs @@ -3,10 +3,12 @@ * Check every upstream in `.sources/upstream.json` for a newer ref than the one * the docs are pinned to, and write an issue body for each that moved. * - * Covers the `watched` group. The optional `vendored` group compares a - * submodule's gitlink against a branch head, so git stays the single source of - * truth for its pin; it is empty today because every submodule this repo keeps - * has a check of its own (a sync workflow, or the build). + * Covers two groups. `watched` repos are compared against the ref recorded in + * that file. `vendored` submodules are compared gitlink against branch head, so + * git stays the single source of truth for their pin; an entry may narrow that + * to the paths the docs actually depend on via `pathFilter`. `motoko` and + * `internetidentity` are in neither group: their own sync workflows + * release-check them and open the bump PR. * * A watched repo declares where its releases actually appear: `release` (git * tags matching a pattern), `crate` (crates.io) or `npm` (the npm registry) for @@ -107,6 +109,8 @@ const LANG_TO_DIR = { mo: 'motoko', javascript: 'hosting', js: 'hosting', + typescript: 'hosting', + ts: 'hosting', }; /** From abb8f3c22162533d2401ee0c0a2fe234a87cb486 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 20:01:24 +0200 Subject: [PATCH 6/7] fix: address the three suppressed comments from the Copilot review The tracking doc's table said examples gets an issue when a quoted file moved while the paragraph below it said examples gets no issue; the paragraph was written before the path filter and never caught up. Vendored issue bodies dropped the entry's reference URL, so the field looked configured but unused. They now carry the same Published reference row watched bodies do. exchange-rates.mdx has a fragment that now calls Nat.toNat64 while the page's full example imported Nat32 and Nat64 but not Nat, so a reader assembling both into one module would hit an unresolved identifier. The example imports Nat. --- .agents/upstream-tracking.md | 5 +++-- docs/guides/chain-fusion/exchange-rates.mdx | 1 + scripts/check-upstream-releases.mjs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.agents/upstream-tracking.md b/.agents/upstream-tracking.md index 7b44dfcd..8d425112 100644 --- a/.agents/upstream-tracking.md +++ b/.agents/upstream-tracking.md @@ -17,8 +17,9 @@ entry to `watched` if its footprint grows. `motoko` and `internetidentity` have their own weekly sync workflows that open the bump PR directly. The `watched` repos are covered by the weekly **Upstream -release check**, which opens an issue. `examples` and the `reference` repos get -no issue. +release check**, which opens an issue whenever one moves. That same check covers +`examples`, but opens an issue only when the range touched a file a `snippet=` +quotes. `reference` repos are not checked at all. ## Why only three are vendored diff --git a/docs/guides/chain-fusion/exchange-rates.mdx b/docs/guides/chain-fusion/exchange-rates.mdx index 445ddc9b..4ef4f769 100644 --- a/docs/guides/chain-fusion/exchange-rates.mdx +++ b/docs/guides/chain-fusion/exchange-rates.mdx @@ -24,6 +24,7 @@ In Motoko, declare the XRC actor interface inline and use the `(with cycles = am import Cycles "mo:core/Cycles"; import Float "mo:core/Float"; import Int "mo:core/Int"; +import Nat "mo:core/Nat"; import Nat32 "mo:core/Nat32"; import Nat64 "mo:core/Nat64"; diff --git a/scripts/check-upstream-releases.mjs b/scripts/check-upstream-releases.mjs index 571c4039..88c2398a 100644 --- a/scripts/check-upstream-releases.mjs +++ b/scripts/check-upstream-releases.mjs @@ -247,7 +247,7 @@ function slugFor(entry) { } async function checkVendored(entry) { - const { path, repo, branch, affects, pathFilter } = entry; + const { path, repo, branch, affects, pathFilter, reference } = entry; const pinnedSha = gitlinkSha(path); if (!pinnedSha) throw new Error(`${path}: not a submodule in this commit`); const headSha = branchHead(repo, branch); @@ -283,6 +283,7 @@ async function checkVendored(entry) { `| Pinned (gitlink) | \`${pinned}\` |`, `| Branch head | \`${latest}\` |`, `| Compare | https://github.com/${repo}/compare/${pinnedSha}...${headSha} |`, + ...(reference ? [`| Published reference | ${reference} |`] : []), '', '## What to re-check', '', From 06185465ab97b1940b5851c1f17fe261efab3ef2 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 27 Aug 2026 20:28:07 +0200 Subject: [PATCH 7/7] docs: record why the icp-cli reference URL is unversioned The AGENTS.md rule requiring a versioned cli.internetcomputer.org slug governs docs/ pages, which is where the validator enforces it. The reference field is the opposite case: an issue exists because a new release shipped, so a maintainer opening it wants that release's docs, not the pinned version's. A hardcoded slug would point at the version being replaced and would need bumping in every sync PR. Every other reference value already follows that convention, including two docs.rs/latest URLs. The exception is now stated in the schema comment so it is a decision rather than something to re-flag. --- .sources/upstream.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.sources/upstream.json b/.sources/upstream.json index fd9306b6..454d8522 100644 --- a/.sources/upstream.json +++ b/.sources/upstream.json @@ -29,7 +29,13 @@ " tagPattern anchored JS regex selecting the tags that count as a release", " verify file whose diff between the two refs is the review payload", " affects what to re-check in docs/ when this moves", - " reference authoritative published docs for the topic", + " reference authoritative published docs for the topic, deliberately the", + " living version rather than a pinned one: an issue exists because a", + " NEW release shipped, so a maintainer opening it wants the docs for", + " that release, not for the version being replaced. This is why the", + " icp-cli entry uses the bare root while AGENTS.md requires a", + " versioned slug in docs/ pages, and why every other entry here", + " points at docs.rs/latest, mops.one, js.icp.build or a branch.", "", "Fields (vendored): path, repo, track 'branch', branch, affects, and an optional", "pathFilter. 'snippets' reports only when the range touched a file quoted by a",