Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
319951b
chore(cli)!: require Node >= 20, drop mocv detection and legacy re-ex…
Kamirus Aug 5, 2026
6dba147
feat(cli)!: stricter flags and safer defaults for v3 (#676)
Kamirus Aug 5, 2026
43117eb
fix(cli): use real semver in the resolver and always report cross-maj…
Kamirus Aug 6, 2026
a25731d
feat(cli)!: drop vessel/dhall support (#675)
Kamirus Aug 6, 2026
2f83fb1
test(cli): extend github dep coverage to sources, add and failures (#…
Kamirus Aug 6, 2026
98e0ac4
feat(cli)!: eliminate hidden shared state (network commands, lock byp…
Kamirus Aug 6, 2026
b45941f
feat(cli)!: adopt the cargo lock model — `--locked`, self-healing ins…
Kamirus Aug 6, 2026
820bb26
Merge remote-tracking branch 'origin/main' into v3
Kamirus Aug 6, 2026
50dec8c
docs: relativise links added on v3
Kamirus Aug 6, 2026
38f9aa4
feat(cli)!: remove dfx from the CLI (#691)
Kamirus Aug 7, 2026
66a58b0
ci: publish 3.x previews to npm and serve 3.x docs at /next (#692)
Kamirus Aug 7, 2026
587f4a0
ci: move the remaining canister deploys off dfx (#693)
Kamirus Aug 10, 2026
aedc790
feat!: drop dfx (#694)
Kamirus Aug 10, 2026
78405ad
feat(cli): confirm major-version jumps in `mops self update` (#697)
Kamirus Aug 10, 2026
d30c81c
docs: pre-GA sweep — strike done v3 items, fix stale CLI guides (#698)
Kamirus Aug 10, 2026
0bdcdbc
docs(cli): post-process the 3.0.0 changelog (#701)
Kamirus Aug 10, 2026
eb5c123
chore: merge main into v3 (decompress removal, self-update guard, lau…
Kamirus Aug 10, 2026
2479364
chore: merge main into v3 (`mops build --check-wasm` / `--check-deplo…
Kamirus Aug 10, 2026
9d086b9
chore: merge main into v3 (2.21.0 release, docs root version) (#707)
Kamirus Aug 10, 2026
78705e7
ci: migrate the docs canister to dfinity's certified-assets canister …
Kamirus Aug 11, 2026
76a940a
fix(cli)!: never write mops.toml or resolve a version during a build
Kamirus Aug 11, 2026
79df3d4
docs: add a guide for driving mops from another build system
Kamirus Aug 11, 2026
61e9acf
docs: correct the network behaviour of --locked and verify
Kamirus Aug 11, 2026
2567b96
fix(cli)!: never write mops.toml or resolve a version during a build …
Kamirus Aug 11, 2026
3c6696c
Merge branch 'v3' into kamil-v3/docs-build-integration
Kamirus Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions .agents/skills/mops-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Opinionated guide for Motoko projects. Covers project config, dependency managem

## Key Principles

1. **No dfx** — always pin `moc` in `[toolchain]`. Use the newest `moc` version. Pin `pocket-ic` too if you have replica tests or benchmarks (otherwise `mops test --mode replica`, `mops bench`, and `mops watch` fall back to the deprecated dfx replica and print a warning).
1. **No dfx** — mops neither invokes nor supports `dfx`. There is no `mops toolchain init`, no `moc-wrapper`, and no `mops watch --generate` / `--deploy`. Always pin `moc` in `[toolchain]`; every command that compiles requires it. Use the newest `moc` version. Pin `pocket-ic` too if you have replica tests or benchmarks — with no pin, `mops test --mode replica`, `mops bench` and `mops watch --test` download and run the mops default (`14.0.0`).
2. **No `mo:base`** — it is deprecated. Always use `mo:core` (`import Array "mo:core/Array"`).
3. **All config in `mops.toml`** — canisters, moc flags, toolchain versions, build settings.
4. **Canister-centric workflow** — define all canisters in `[canisters]`; never pass file paths to `mops check`. Exception: library packages (no `[canisters]`) use file paths directly: `mops check src/**/*.mo`.
Expand All @@ -22,7 +22,7 @@ Opinionated guide for Motoko projects. Covers project config, dependency managem
[toolchain]
moc = "1.7.0"
lintoko = "0.10.0"
pocket-ic = "12.0.0" # only if you have replica tests / benchmarks
pocket-ic = "14.0.0" # optional; defaults to 14.0.0 for replica tests / benchmarks

[dependencies]
core = "2.5.0"
Expand All @@ -43,12 +43,14 @@ path = "deployed/backend.most"
[build]
outputDir = "src/backend/dist"
args = ["--release"]
check-wasm = true # optional: analyze final Wasm complexity
check-deploy = true # optional: verify fresh PocketIC installation after build

# Opt-in Wasm optimization (Binaryen wasm-opt) for build + bench
[optimize]
# level = "O3" # default
# keep-names = true # default
# wasm-opt pin: [toolchain] wasm-opt = "131" (auto-pinned to latest if missing)
# wasm-opt pin: [toolchain] wasm-opt = "131" (required when [optimize] is set)
```

`check-stable` runs ICP's upgrade-time stable-variable compatibility check locally, so incompatible changes fail in `mops check` instead of being rejected when upgrading a live canister. It compares the current code against a `.most` from the deployed version.
Expand Down Expand Up @@ -76,14 +78,25 @@ Flags are applied in this order (later overrides earlier):
### `mops install`

```bash
mops install
mops install --lock update # regenerate a stale/corrupt mops.lock
mops install --lock check # fail if lockfile is missing or stale (CI)
mops install # dev flow: keeps mops.lock in sync, self-heals a broken one
mops install --locked # CI flow: fail if mops.lock is missing or would change
```

Run after cloning or after manual `mops.toml` edits. Updates `mops.lock` by default. Local path dependencies are stored root-relative in the lockfile (portable across machines). A plain install will not rewrite absolute paths left by older CLIs — use `mops install --lock update`, and ensure every environment has a CLI that understands relative lock paths.
Run after cloning or after manual `mops.toml` edits. `mops.lock` is always maintained — there is no flag to opt out, and no `--lock` flag (removed in v3). A missing, unparseable, legacy-format or `mops.toml`-inconsistent lock is regenerated by a plain `mops install`, including locks that still carry absolute local paths from older CLIs.

When the `CI` env var is set and `--lock` is omitted, defaults to `--lock check` (deprecated — pass `--lock check` explicitly; auto-detection will be removed in v3). A stale lock fails with a hint to run `mops install --lock update`.
`--locked` requires an up-to-date lock and never writes it. It is also available on every implicitly-installing command (`mops build`, `mops check`, `mops check-stable`, `mops check-candid`, `mops test`, `mops bench`, `mops generate candid`), so CI can run `mops test --locked` with no prior install. `mops sources` has no `--locked` (a packtool caller parses its stdout mid-build) — put `mops install --locked` earlier in the pipeline instead.

The `CI` env var no longer affects lockfile behavior (removed in v3). Commit `mops.lock` — for applications and libraries alike.

Integrity is verified at download time, so `mops install` no longer re-hashes `.mops/`: editing a dependency in place will not fail the next install. Use `mops verify` for the on-demand on-disk audit.

### `mops verify`

```bash
mops verify # re-hash .mops/ against mops.lock, and mops.lock against the registry
```

Exits 1 with the offending files and a recovery hint. This is the replacement for anyone who relied on `mops install` failing when `.mops/` had been modified.

### `mops add <package>`

Expand All @@ -93,7 +106,7 @@ mops add core@2.5.0 # specific version
mops add --dev test # dev dependency
```

Updates `mops.toml` and `mops.lock` (even when `CI` is set).
Updates `mops.toml` and `mops.lock`.

### `mops check`

Expand All @@ -103,6 +116,7 @@ Primary correctness command — runs moc check, then check-stable (if configured
mops check # all canisters
mops check backend # single canister
mops check --fix # autofix + check + stable + lint
mops check --no-lint # skip the lint step for one run
mops check --verbose # show moc invocations
mops check -- -Werror # treat warnings as errors
```
Expand All @@ -117,12 +131,18 @@ mops check -- -Werror # treat warnings as errors
mops build # all canisters
mops build backend # single canister
mops build --verbose # show compiler commands
mops build --check-wasm # analyze final Wasm complexity without PocketIC
mops build --no-check-wasm # skip configured [build].check-wasm once
mops build --check-deploy # verify fresh installation on PocketIC
mops build --no-check-deploy # skip configured [build].check-deploy once
mops build -- --ai-errors # pass extra moc flags
```

Produces `.wasm`, `.did`, and `.most` files in `[build].outputDir` (default `.mops/.build`).

With `[optimize]` in `mops.toml`, runs `wasm-opt` after candid metadata (default `-O3 -g`). Pin Binaryen with `mops toolchain use wasm-opt 131` (or let auto-pin write latest on first build). Soft-fails to unoptimized Wasm on error. Pass `--no-optimize` (on `build` or `bench`) to skip the pass for a single run without editing `mops.toml`.
With `[optimize]` in `mops.toml`, runs `wasm-opt` after candid metadata (default `-O3 -g`). Requires a Binaryen pin: `mops toolchain use wasm-opt 131`. Build commands never write the pin themselves, and a `wasm-opt` failure fails the build. Pass `--no-optimize` (on `build` or `bench`) to skip the pass for a single run without editing `mops.toml`.

When `--check-wasm` or `[build].check-wasm = true` is enabled, Mops runs fast Walrus analysis on the final Wasm without starting PocketIC. Per-function IC0505 complexity below 750,000 is quiet, 750,000 through 899,999 emits an early warning, and 900,000 or more emits a critical warning. `MOPS-WASM-COMPLEXITY` output includes actionable function metrics, the three largest complexity contributors, and Motoko correction guidance. The estimate never fails the build. Use `--no-check-wasm` to skip configured analysis once.

### `mops deployed`

Expand Down Expand Up @@ -152,7 +172,7 @@ mops generate candid backend -o <path> # single canister, ad-hoc path
mops toolchain use moc 1.7.0 # pin specific version
mops toolchain use moc latest # pin latest version (non-interactive)
mops toolchain use lintoko 0.10.0 # pin specific version
mops toolchain use pocket-ic 12.0.0 # pin for replica tests / benchmarks (pin a specific version; `latest` may resolve to one the vendored `@dfinity/pic` client doesn't support)
mops toolchain use pocket-ic 14.0.0 # pin for replica tests / benchmarks (optional; defaults to 14.0.0)
mops toolchain use wasm-opt 131 # Binaryen for [optimize] (or `latest`)
mops toolchain update moc # update to latest (requires existing [toolchain] entry)
mops toolchain update # update all tools to latest
Expand All @@ -162,7 +182,7 @@ mops toolchain info <tool> --versions --all # full stable history (cache warming
mops toolchain bin moc # print path to binary
```

**`pocket-ic` versions**: `9.0.0` and newer run on the vendored `@dfinity/pic` client. Pins below `9.0.0` still work on the legacy `pic-ic` client but print a deprecation warning and are removed in mops v3 — pin `9.0.0` or newer.
**`pocket-ic` versions**: pin anything from `9.0.0` up, `latest` included — mops keeps no list of blessed versions. Pins below `9.0.0` error with a migration message (they ran on the legacy client that mops 3.0.0 removed). With no pin, mops uses `14.0.0` — a fixed constant, never a network lookup, so a warmed cache keeps runtime offline.

**Agent note**: `toolchain use <tool>` without a version opens an interactive picker — do not use in scripts or agents. Always pass a version or `latest`. `toolchain update` only works when the tool already has a `[toolchain]` entry. `toolchain info <tool> --versions` works without `mops.toml` (first GitHub page by default; pass `--all` for full history).

Expand All @@ -174,6 +194,8 @@ Create migration files directly in the `chain` directory.

After `mops check --fix` (or `mops check <canister>`) confirms the chain compiles, run `mops build` to produce the wasm artifact.

Use `mops build --check-deploy`, or set `[build].check-deploy = true` for every build, to install each built Wasm on a fresh PocketIC canister and catch module validation, initialization, and installation failures. No `pocket-ic` pin is required (the default version is used when unpinned); a version from `9.0.0` up or a local binary path can be pinned in `[toolchain]`. Use `--no-check-deploy` to skip configured validation once. The command uses each canister's `initArg`, or `()` when omitted. Set `wasmMemoryLimit` to a positive integer byte limit on a canister to check deployment under that limit. PocketIC errors are reported as provided by the client, and installation failures are collected across canisters. Before installation, Mops runs `moc --stable-compatible` from a temporary empty-actor `.most` to each generated `.most`. If moc reports incompatibility, Mops emits `MOPS-CHECK-DEPLOY-SKIPPED` with the compiler diagnostic and does not check that canister on fresh PocketIC. Eligible siblings are still checked; validate the skipped upgrade against representative baseline state.

`check-limit` (optional) caps how many recent chain files `mops check` and `mops lint` consider — useful when the chain grows long and re-checking every old migration slows feedback down. `mops build` is unaffected by `check-limit`. When the limit kicks in, mops stages the included files into `.migrations-<canister>/` next to the `chain` directory (auto-`.gitignore`d). `moc` diagnostics may then print paths there — the real file lives in the `chain` directory with the same name.

Override `check-limit` for a single run with `--no-check-limit` (`mops check`, `mops check-stable`, `mops lint`) — e.g. `mops check --fix --no-check-limit` to autofix older, normally-trimmed migrations. On `mops check` and `mops check-stable`, `--no-check-limit` also suppresses the pending-migration warning.
Expand Down Expand Up @@ -218,12 +240,12 @@ Tests live in `test/*.test.mo`:
mops test # run all tests
mops test my-test # filter by name
mops test --mode wasi # use wasmtime (for to_candid/from_candid)
mops test --reporter verbose # show Debug.print output
mops test --reporter files # one line per file (default is verbose)
mops test --watch # re-run on file changes
mops test -- -Werror # pass extra moc flags
```

Replica tests (actor files or `// @testmode replica`) use `pocket-ic` from `[toolchain]`. With no pin they fall back to the deprecated `dfx` replica (warning printed) — pin `pocket-ic` in `[toolchain]` to silence it. Same applies to `mops bench` and `mops watch`.
Replica tests (actor files or `// @testmode replica`) run on PocketIC — the `pocket-ic` version from `[toolchain]`, or `14.0.0` when unpinned. Same for `mops bench` and `mops watch --test`. There is no `--replica` flag and no dfx replica.

### `mops bench`

Expand Down Expand Up @@ -257,6 +279,16 @@ mops format # format all .mo files
mops format --check # check formatting without modifying
```

### `mops watch`

```bash
mops watch # errors + warnings + format (the default set)
mops watch --warning # errors + warnings only
mops watch -t # errors + tests
```

Error checking is always on. Passing any flag selects only the named tasks, so add `-w` / `-f` when you want them alongside `-t`. Those four are the whole set — there is no `--generate` or `--deploy`.

## Common Patterns

### Warning suppression for a canister
Expand Down Expand Up @@ -284,4 +316,4 @@ To update tools later: `mops toolchain update moc` or `mops toolchain update` (a

## Editing the mops docs site

Edits go to `docs/docs/` — the in-development line, served at the root of docs.mops.one. `docs/versioned_docs/version-2.x/` is a frozen snapshot; back-port only correctness fixes for behavior that also exists in 2.x.
Edits go to `docs/docs/` — the in-development 3.x line, served at `docs.mops.one/next` during the 3.x preview. `docs/versioned_docs/version-2.x/` is the released line, served at the root, and is a frozen snapshot; back-port only correctness fixes for behavior that also exists in 2.x. The two swap back at the 3.0.0 GA.
16 changes: 7 additions & 9 deletions .cursor/skills/frontend-testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Run from the repo root:

```bash
npm run lint
npm run build-frontend
MOPS_FRONTEND_NETWORK=local npm run build-frontend
npm run build-cli-releases
```

Expand All @@ -30,7 +30,7 @@ lsof -ti:3000,3001 | xargs kill -9 2>/dev/null; echo "ports cleared"
Start the main frontend dev server (from repo root):

```bash
cd frontend && DFX_NETWORK=ic npx vite --port 3000
cd frontend && MOPS_FRONTEND_NETWORK=ic npx vite --port 3000
```

Start the cli-releases frontend (from repo root, in a separate terminal):
Expand All @@ -51,7 +51,7 @@ Kill the dev servers after checks pass.

## Phase 3: Deploy to Staging (Human)

**This phase must be run by the human in their terminal.** The agent cannot reliably run `dfx deploy` due to a `ColorOutOfRange` TTY panic in dfx v0.29.1 within Cursor's shell, and potential macOS keychain prompts for identity access.
**This phase must be run by the human in their terminal.** It deploys to a mainnet canister with the `mops` identity, and may prompt the macOS keychain for access to it.

Print the following instructions for the human and wait for confirmation before proceeding to Phase 4.

Expand All @@ -62,16 +62,14 @@ Print the following instructions for the human and wait for confirmation before
Deploy the frontend to the staging `assets` canister.

**Prerequisites**:
- `dfx` installed via `dfxvm`
- `dfx identity` with controller access to staging canisters (e.g. `mops`)
- `icp` installed at the version `DEVELOPMENT.md` pins
- An `icp identity` with controller access to the staging canisters (e.g. `mops`)
- Dependencies installed (`npm install` in repo root)

**Important**: `dfxvm` automatically uses the dfx version pinned in `dfx.json`. Do NOT run `dfxvm update`, `dfxvm install`, or `dfxvm default` to "fix" the version — this is correct behavior.

**Run from the repo root:**

```bash
dfx deploy assets --network staging -y
npm run deploy-staging assets
```

After deployment, tell the agent to continue with Phase 4 verification.
Expand Down Expand Up @@ -105,5 +103,5 @@ Key things to check: fonts, button styles, layout, package detail pages, search.

### Troubleshooting

- **Page blank or doesn't load**: Check `dfx canister status assets --network staging` for cycle balance.
- **Page blank or doesn't load**: Check `icp canister status assets -e staging` for cycle balance.
- **`Package not found` errors**: The `ic-mops` npm package may be querying the wrong backend. Ensure the code has `window.MOPS_NETWORK` set to `"ic"` for non-local deployments (see `frontend/components/package/Package.svelte`).
64 changes: 64 additions & 0 deletions .github/actions/deploy-canister/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Deploy asset canister
description: >-
Build and deploy one of the repo's asset canisters to the IC with icp-cli.
Expects icp-cli on PATH and an imported `mops` identity — the calling job
does both once, since it deploys more than one canister.

inputs:
canister:
description: Canister name, as declared in icp.yaml and mapped in .icp/data/mappings/ic.ids.json.
required: true
domain:
description: >-
Custom domain the canister serves. Asserted against its
.well-known/ic-domains after the deploy.
required: true
build-deps-dir:
description: >-
Directory to run `npm ci` in before deploying. The canister's build step
in icp.yaml runs an npm script, which needs that directory's node_modules.
required: true

runs:
using: composite
steps:
- name: Install ${{ inputs.canister }} build dependencies
shell: bash
working-directory: ${{ inputs.build-deps-dir }}
run: npm ci

- name: Deploy ${{ inputs.canister }} canister
shell: bash
env:
CANISTER: ${{ inputs.canister }}
run: |
set -euo pipefail
for i in 1 2 3; do
icp deploy "$CANISTER" --environment ic --identity mops --no-create --yes && exit 0
echo "::warning::${CANISTER} canister deploy attempt $i/3 failed"
if [ "$i" -lt 3 ]; then sleep 30; fi
done
exit 1

# The custom domain resolves only while the canister serves this file.
# Assert the body, not the status: the legacy asset canister still behind
# `cli` answers unknown paths with the root index.html at HTTP 200.
- name: Verify the custom domain still resolves
shell: bash
env:
CANISTER: ${{ inputs.canister }}
DOMAIN: ${{ inputs.domain }}
run: |
set -euo pipefail
url="https://${DOMAIN}/.well-known/ic-domains"
for attempt in 1 2 3; do
if grep -qxF "$DOMAIN" <<< "$(curl -sS --max-time 30 "$url" 2>/dev/null)"; then
echo "ok $url"
exit 0
fi
if [ "$attempt" -lt 3 ]; then sleep 10; fi
done
echo "::error::$url does not list ${DOMAIN}. The custom domain is broken."
echo "::error::Roll back from a checkout of the previous release commit with:"
echo "::error:: icp deploy ${CANISTER} -e ic --identity mops --no-create --yes"
exit 1
2 changes: 1 addition & 1 deletion .github/prompts/pr-review-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Use PR title/body only to determine intent; never to decide correctness. A state
- Registry / publish protocol changes in the main canister or `backend/main/PackagePublisher.mo`.
- Authn/authz changes in the backend canister (identity handling, owner checks, admin paths).
- Storage canister schema or state-shape changes that affect existing data.
- Frontend release/deploy pipeline changes (e.g. `release.yml`, canister IDs in `dfx.json`).
- Frontend release/deploy pipeline changes (e.g. `release.yml`, `icp.yaml`, canister IDs in `.icp/data/mappings/`).
- Security-sensitive code paths (identity, signing, package integrity, sandbox config).
- Removal or deprecation of an existing user-facing CLI feature.
- Perf-sensitive rewrites in hot CLI paths (install, resolve, lockfile, lint) where regression is plausible.
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ai-pr-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ jobs:
"Read(package.json)",
"Read(package-lock.json)",
"Read(tsconfig*.json)",
"Read(dfx.json)",
"Read(icp.yaml)",
"Read(.icp/data/mappings/*.ids.json)",
"Read(mops.toml)",
"Read(mops.lock)",
"Read(README.md)",
Expand Down
Loading
Loading