Rename asset-canister skill to static-site; adopt certified-assets recipe - #256
Rename asset-canister skill to static-site; adopt certified-assets recipe#256marc0olo wants to merge 7 commits into
Conversation
…ets recipe Reposition frontend hosting around the @dfinity/static-site recipe (certified-assets canister) as the recommended default, and migrate the skills site itself onto it. Skill: - Rename skills/asset-canister -> skills/static-site (eval file too), rewrite SKILL.md led by the static-site recipe (_redirects SPA rule, _headers/CSP, clean URLs, access protection, authorize-based uploads, presync, 12 static-site pitfalls). Description triggers on both static-site and asset-canister vocabularies. - Add references/legacy-asset-canister.md (legacy SDK asset canister: .ic-assets.json5, AssetManager, grant_permission, allow_raw_access) and references/migrating-from-asset-canister.md (config mapping + reinstall-mode caveat). - Fix cross-references in icp-cli, deploy-to-cloud-engine, custom-domains, internet-identity, autosync-ic-skills, and CLAUDE.md; present static-site as recommended, asset-canister as legacy. Upstream tracking: - Track dfinity/certified-assets docs (v0.3.1) in .claude/upstream.md, generalize sync-upstream-check.sh for the docs/ layout, and add a check-certified-assets job to the weekly sync workflow. Site migration (dogfooding): - icp.yaml: @dfinity/asset-canister@v2.2.1 -> @dfinity/static-site@v0.3.1. - Port public/.ic-assets.json5 -> public/_headers (same CORS/cache/ content-type parity; no CSP change); add public/_redirects with 301s for the renamed skill's page, SKILL.md, .well-known, and api paths. - deploy-ic.yml: add a workflow_dispatch mode input for the one-time reinstall cutover (canister type change).
Skill Validation ReportProject Checks |
There was a problem hiding this comment.
🟡 Not ready to approve
There are correctness/operability issues to address (notably in the upstream sync script auth header handling and a small but misleading frontend-cookie reference) before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Repositions the repo’s frontend-hosting guidance around the @dfinity/static-site (certified-assets) recipe, adds migration/legacy references, updates cross-skill documentation, and migrates this repo’s own site deployment/config from the legacy asset canister to static-site (including redirect + header config and upstream tracking automation).
Changes:
- Added the new
static-siteskill (with evals) plus legacy/migration reference docs; removed the oldasset-canisterskill and evals. - Migrated the repo’s site config to static-site (
icp.yaml,_headers,_redirects) and added a one-time reinstall mode input to the deploy workflow. - Extended upstream-sync automation/tracking to cover
dfinity/certified-assetsdocs.
File summaries
| File | Description |
|---|---|
| skills/static-site/SKILL.md | New primary skill content for the recommended static-site (certified-assets) recipe, including pitfalls and config examples. |
| skills/static-site/references/migrating-from-asset-canister.md | Migration guide covering recipe/config mapping and reinstall requirement. |
| skills/static-site/references/legacy-asset-canister.md | Legacy reference for maintaining @dfinity/asset-canister projects and programmatic uploads. |
| skills/internet-identity/SKILL.md | Cross-reference update to point frontend hosting guidance to static-site. |
| skills/icp-cli/SKILL.md | Updates frontend recipe examples and narrative to recommend static-site while keeping legacy asset-canister as supported. |
| skills/icp-cli/references/dfx-migration.md | Updates the dfx "type": "assets" mapping to recommend static-site (with legacy note). |
| skills/deploy-to-cloud-engine/SKILL.md | Updates frontend recipe example and metadata guidance to include static-site. |
| skills/custom-domains/SKILL.md | Updates guidance to account for static-site behavior (.well-known upload) and legacy asset-canister differences. |
| skills/autosync-ic-skills/SKILL.md | Updates examples to reference static-site instead of asset-canister. |
| skills/asset-canister/SKILL.md | Removes the old asset-canister skill content (superseded by static-site + legacy reference). |
| scripts/sync-upstream-check.sh | Generalizes upstream path handling and adds certified-assets mapping for sync checks. |
| public/.ic-assets.json5 | Removes legacy asset-canister config file from the site. |
| public/_redirects | Adds certified 301 redirects for the skill rename and discovery/API endpoints. |
| public/_headers | Adds explicit static-site header rules (CORS, caching, content-type overrides) replacing .ic-assets.json5. |
| icp.yaml | Switches the site deployment recipe from @dfinity/asset-canister to @dfinity/static-site. |
| evaluations/static-site.json | Adds evaluation coverage for static-site recipe/config/migration behaviors. |
| evaluations/asset-canister.json | Removes the old asset-canister evaluation file. |
| .github/workflows/sync-upstream.yml | Adds a weekly check job for dfinity/certified-assets upstream docs changes. |
| .github/workflows/deploy-ic.yml | Adds workflow_dispatch input for one-time --mode reinstall cutover deploy. |
| .claude/upstream.md | Adds upstream tracking metadata for certified-assets/static-site. |
| .claude/CLAUDE.md | Extends upstream-sync guidance to include the new upstream label/repo. |
Review details
- Files reviewed: 21/21 changed files
- Comments generated: 3
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Address Copilot review: several skills said the ic_env cookie / root key is provided 'by the asset canister'. Since static-site is now the recommended frontend recipe (and also serves ic_env), reword to 'the frontend canister (static-site or asset canister)' in icp-cli, internet-identity, canister-security, and the dev-server reference.
There was a problem hiding this comment.
🟡 Not ready to approve
The deploy workflow’s icp deploy invocation orders --mode after the canister name (risking incorrect parsing during the critical reinstall cutover), and _headers currently omits Cache-Control for several non-hashed root assets.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Comments suppressed due to low confidence (2)
.github/workflows/deploy-ic.yml:66
icp-clidocs in this repo consistently show--modeas a flag toicp deploy(e.g.icp deploy --mode reinstall ...). Here the workflow puts--modeafter the canister name (icp deploy -e ic frontend --mode ...), which risks being parsed as an argument (or breaking if the CLI tightens parsing). For the one-time cutover, it would be safer to keep flag ordering consistent with the documented syntax.
run: |
MODE="${{ inputs.mode }}"
if [ -n "$MODE" ]; then
echo "Deploying with --mode $MODE"
icp deploy -e ic frontend --mode "$MODE"
else
public/_headers:26
- This
_headersfile setsCache-Controlonly for/_astro/*,/*.html,/styles.css, and/matomo.js. Other root assets inpublic/(e.g.favicon.*,og-image.*,apple-touch-icon.png,icp-logo.svg) will be served without anyCache-Control, which can change caching behavior for these frequently-requested assets. If the intent is to keep non-hashed root assets revalidating on each deploy, add explicitCache-Controlblocks for them.
# Non-hashed root assets that can change between deploys.
/styles.css
Cache-Control: public, max-age=0, must-revalidate
/matomo.js
Cache-Control: public, max-age=300
- Files reviewed: 23/23 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…ster name Address Copilot review (suppressed/low-confidence): - _headers: add revalidate Cache-Control for all non-hashed asset families (images, css, xml, zip, robots.txt, AGENTS.md, api/*.json) that were left header-less, restoring parity with the old .ic-assets.json5 '**/*' default. Kept patterns narrow and non-overlapping since static-site COMBINES same-name headers (dist/_astro is js-only, so extension rules can't collide with the immutable block). No CORS added to /api, matching the original. - deploy-ic.yml: order '--mode' before the 'frontend' positional (icp deploy -e ic --mode reinstall frontend) to match the documented CLI syntax for the one-time reinstall cutover.
|
Addressed both suppressed (low-confidence) comments from the latest review in 71e9133:
The push-to-main deploy-fails-in-upgrade-mode point from the earlier review is intentional (reinstall cutover via |
There was a problem hiding this comment.
🟡 Not ready to approve
It includes at least one broken copy/pasteable code snippet (fileBuffer is undefined) and leaves some repo references to asset-canister that should be updated to match the rename (e.g., CODEOWNERS/README).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Comments suppressed due to low confidence (2)
public/_redirects:3
- The repo still has a couple of user-facing/collaboration references to the old
asset-canisterskill name that weren’t updated as part of this rename:
README.md’sindex.jsonexample still uses{ "name": "asset-canister", "url": ".../asset-canister/SKILL.md" }(README.md:82-84)..github/CODEOWNERSstill lists/skills/asset-canister/(CODEOWNERS:12), so reviewers won’t be auto-requested for the new/skills/static-site/path.
Updating those keeps documentation/examples consistent with the redirects declared here and ensures CODEOWNERS continues to work after the directory rename.
# Permanent redirects for the asset-canister → static-site skill rename.
# Served as certified 301s by the certified-assets canister.
# from to status
skills/static-site/references/legacy-asset-canister.md:117
- The Node.js example uses
fileBufferbut never defines it, so the snippet won’t run as-is. Defining the buffer withreadFileSync(...)keeps the example copy/pasteable.
// Upload a single file. Files >1.9MB are automatically chunked.
const key = await assetManager.store(fileBuffer, {
fileName: "photo.jpg",
contentType: "image/jpeg",
path: "/uploads",
});
- Files reviewed: 23/23 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…snippet - .github/CODEOWNERS: /skills/asset-canister/ -> /skills/static-site/ so reviewers are still auto-requested after the directory rename - README.md: index.json hash example now uses the static-site skill name/URL - references/legacy-asset-canister.md: define fileBuffer with readFileSync so the AssetManager.store() snippet is copy-paste runnable (was undefined)
|
Addressed both suppressed (low-confidence) comments from the latest review in 4f917eb — both were valid, thanks Copilot:
Remaining |
There was a problem hiding this comment.
🟡 Not ready to approve
The deploy workflow uses ${{ inputs.mode }} in a workflow that also runs on push, which can break expression evaluation outside workflow_dispatch unless guarded or switched to github.event.inputs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Comments suppressed due to low confidence (1)
.github/workflows/deploy-ic.yml:66
${{ inputs.mode }}is only defined forworkflow_dispatch/reusable workflows; when this workflow runs onpush, expression evaluation can fail or be empty unexpectedly. Usegithub.event.inputs.mode(which safely resolves to empty on non-dispatch events) to keep push deployments working while still supporting the dispatch input.
- name: Deploy to IC mainnet
run: |
MODE="${{ inputs.mode }}"
if [ -n "$MODE" ]; then
echo "Deploying with --mode $MODE"
icp deploy -e ic --mode "$MODE" frontend
else
- Files reviewed: 25/25 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
|
Re: the suppressed comment on Per GitHub's contexts docs: the The suggested |
There was a problem hiding this comment.
🟡 Human review recommended
It changes production deployment behavior (static-site canister type cutover + workflow changes) and requires a one-time reinstall runbook that should be validated by a human reviewer before approval.
Review details
Comments suppressed due to low confidence (3)
skills/static-site/SKILL.md:23
- The prerequisites bullet says
ic-wasm“ships with icp-cli”, but elsewhere in the repo (e.g.,skills/icp-cli/SKILL.md) installation is documented asnpm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm, implying it’s a separate binary/package. Rewording avoids readers skipping theic-wasminstall and then failing when using themetadatafield.
- `icp-cli` (the recipe pins the canister + sync-plugin pair; `ic-wasm`, needed only for the `metadata` field, ships with `icp-cli`).
.claude/upstream.md:58
- This bullet says
docs/“(all files)”, butscripts/sync-upstream-check.shonly diffs files returned by the GitHub Contents API at that directory level (type == "file") and does not recurse into subdirectories. Clarify the scope so future nested docs aren’t assumed covered by the automation.
- **Upstream files:** `docs/` (all files) — the certified-assets user documentation is the source of truth for the static-site recipe. Files today: `overview.md`, `routing.md`, `redirects.md`, `headers.md`, `site-files.md`, `access-protection.md`, `how-it-works.md`, `verifying-contents.md`.
.github/workflows/sync-upstream.yml:214
- If the
gh release listfilter returns no semver tags,TAGbecomesnulland later API calls will fail with a confusing 404. Adding an explicit guard makes the workflow fail fast with a clear error (and avoids opening an incorrect upstream issue).
run: |
# Only semver v* releases are the static-site recipe; ignore migration-*/snapshot-* tags.
TAG=$(gh release list --repo dfinity/certified-assets --limit 100 --json tagName --jq '[.[] | select(.tagName | test("^v[0-9]"))] | first | .tagName')
echo "tag=$TAG" >> $GITHUB_OUTPUT
- Files reviewed: 25/25 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…G guard - static-site SKILL.md: drop the 'ic-wasm ships with icp-cli' claim (contradicted the icp-cli skill and risked readers skipping the install and failing on the metadata field). Align with icp-cli's documented install: npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm. - .claude/upstream.md: clarify the certified-assets sync check diffs only the top-level files in docs/ (GitHub Contents API is non-recursive), so a future nested docs subdir would need to be added to the check explicitly. - sync-upstream.yml: guard the certified-assets 'latest release' step — if no semver v* release is found, TAG would be empty and later tag-ref API calls would 404 confusingly (and could open a bad issue). Now fails fast with a clear message (// empty + -z check).
|
All three suppressed comments this round were valid — fixed in 84c2686:
Verified: YAML parses, |
There was a problem hiding this comment.
🟡 Not ready to approve
There are a couple of concrete docs/config inconsistencies (notably access-protection ordering guidance and missing discovery-zip CORS/cache headers) that should be corrected before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Comments suppressed due to low confidence (2)
skills/static-site/SKILL.md:186
- This step sequence deploys (syncs) the site before enabling protection, but the surrounding guidance says to enable protection before the first sync for a brand-new private app. As written, it implies a brief window where assets may be world-readable. Please clarify the ordering by showing an initial “login-only” deploy (or otherwise noting the exposure) and a subsequent deploy after protection is enabled.
# 1. Add a self-contained /login.html to your dir and deploy.
icp deploy
# 2. Turn the gate on, naming your login page.
icp canister call frontend enable_protection '("/login.html")'
public/_headers:78
- The
/*.zipblock is the only header rule that will apply to/.well-known/skills/<name>/SKILL.zip, but unlike the other “agent discovery endpoints” blocks it doesn’t set any CORS headers and forcesmax-age=0. If a consumer fetchesSKILL.zipfrom another origin (or expects discovery endpoints to be consistently cacheable), this can break or add unnecessary load. Consider giving zip downloads the same CORS + short-cache treatment as the.well-known/*.json/.well-known/*.mdendpoints.
/*.zip
Cache-Control: public, max-age=0, must-revalidate
- Files reviewed: 25/25 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
… ordering; give SKILL.zip discovery headers
Review round + a correctness bug the review question surfaced:
- issue_token takes a record (IssueTokenArgs { label; ttl_secs; value }), not
positional args — the positional form (copied from upstream docs' quick-start,
which contradicts its own .did and the CI-tested example) would fail at runtime.
Fixed the code block and table; also disable_protection '()'.
- Access-protection ordering: the static-site recipe's 'icp deploy' installs AND
syncs in one step, so 'enable_protection before the first deploy' is impossible
and a plain deploy-then-enable has a brief public window. Rewrote the note and
Pitfall 12 to the real zero-window path (deploy a login-only dir, enable, then
deploy the full site).
- _headers: give .well-known SKILL.zip the same CORS + short-cache as the .json/.md
discovery endpoints (was /*.zip max-age=0, no CORS).
|
Both suppressed comments were valid, fixed in b7d488f — and reviewing the access-protection guidance surfaced a real runtime bug too:
Also did a full self-review pass: version pins all |
There was a problem hiding this comment.
🟡 Not ready to approve
There are a few correctness/documentation issues to resolve (notably the deploy workflow inputs context on push runs, and a couple of docs that currently imply an overly drop-in migration or optional ic-wasm requirement).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Comments suppressed due to low confidence (3)
.github/workflows/deploy-ic.yml:62
inputs.modeis only populated forworkflow_dispatchruns; onpushruns this can evaluate to an undefined context and break expression evaluation. Usinggithub.event.inputs.modeis safe (empty on push) while preserving the dispatch input behavior.
- name: Deploy to IC mainnet
run: |
MODE="${{ inputs.mode }}"
skills/static-site/SKILL.md:23
- The static-site recipe is an official icp-cli recipe; this bullet implies
ic-wasmis only needed when using the optionalmetadatafield, butic-wasmis required for official recipes in general (including@dfinity/static-site) per the icp-cli skill. Reword to avoid suggesting that installing only@icp-sdk/icp-cliis sufficient.
- `icp-cli` (the recipe pins the canister + sync-plugin pair). The optional `metadata` field additionally needs `ic-wasm`, installed alongside the CLI (`npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`) — see the `icp-cli` skill.
skills/icp-cli/references/dfx-migration.md:96
- This config-mapping row suggests
dfx.json"type": "assets"maps directly to@dfinity/static-site, but dfx "assets" projects are the legacy SDK asset canister (.ic-assets.json5) and switching an existing deployed canister to static-site can require a reinstall + config migration. Adding a short caveat/pointer here would prevent readers from assuming a drop-in recipe swap.
| `"type": "assets"` | `recipe.type: "@dfinity/static-site@v0.3.1"` (recommended; legacy: `@dfinity/asset-canister@v2.2.1`) |
- Files reviewed: 25/25 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…at, quiet recurring inputs.mode flag
- static-site SKILL.md: reword prerequisites so icp-cli AND ic-wasm are installed
together (official recipes require ic-wasm) rather than implying ic-wasm is
optional/only-for-metadata.
- dfx-migration.md: note the dfx 'assets' -> static-site row is NOT a drop-in
(different canister, _headers/_redirects not .ic-assets.json5, reinstall needed)
and point to the migration guide.
- deploy-ic.yml: switch ${{ inputs.mode }} -> ${{ github.event.inputs.mode }}.
Equivalent (both empty on push per GitHub's nonexistent-property rule; the
original was not broken), but Copilot keeps re-flagging inputs.mode each round —
this ends the loop.
|
Addressed all three in 05f64ae:
Build + validate green; workflow YAML parses and the |
There was a problem hiding this comment.
🟢 Ready to approve
Changes are consistent across skills, site config, evals, and automation, and no concrete correctness issues were found in the reviewed diffs.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
- Files reviewed: 25/25 changed files
- Comments generated: 0 new
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| ## Step 4 — Verify and report | ||
|
|
||
| - Confirm `.claude/skills/` now contains skill directories (e.g. `motoko`, | ||
| `asset-canister`, `internet-identity`, …) each with a `SKILL.md`. |
There was a problem hiding this comment.
if you autosync is this skill actually going to get deleted?
There was a problem hiding this comment.
yes, that would be the case. but there would be the new skill which also covers the asset-canister in the references.
I wasn't entirely sure if we should keep the asset-canister skill as a standalone. static-site and asset-canister would then most likely be competing in certain prompts.
There was a problem hiding this comment.
the update behavior was also explored with npx skills update which is why I am updating the AGENTS.md to cover the pinned case with the skills lockfile. npx skills update by default does not detect new skills and also doesn't delete old/outdated ones.
this is also why this PRs was created:
here the updated proposed AGENTS.md:
What & why
@dfinity/asset-canisterand@dfinity/static-siteare two different canisters, not a rename: static-site (the certified-assets canister) adds response certification, clean-URL canonicalization,_headers/_redirectsconfig, and access protection. Per dfinity/icp-cli#682, static-site is the direction for frontends. This PR repositions the skill around it as the recommended default (keeping the legacy asset canister as a reference + migration guide), and dogfoods it by moving the skills site itself onto the recipe.Skill changes
skills/asset-canister/→skills/static-site/(+ eval file). SKILL.md rewritten led by the static-site recipe:_redirectsSPA rule,_headers/CSP, clean URLs, access protection,authorize-based uploads,presync, and 12 static-site pitfalls. Description triggers on bothstatic-siteandasset-canistervocabularies.legacy-asset-canister.md(.ic-assets.json5,AssetManager,grant_permission,allow_raw_access) andmigrating-from-asset-canister.md(config mapping + reinstall caveat).icp-cli,deploy-to-cloud-engine,custom-domains,internet-identity,autosync-ic-skills, andCLAUDE.md— static-site recommended, asset-canister legacy.Upstream tracking
dfinity/certified-assetsdocs (v0.3.1) in.claude/upstream.md, generalizesync-upstream-check.shfor thedocs/layout, and add acheck-certified-assetsjob to the weekly sync workflow. One-time repo setup:gh label create upstream-certified-assets(done).Site migration (this repo → static-site)
icp.yaml:@dfinity/asset-canister@v2.2.1→@dfinity/static-site@v0.3.1.public/.ic-assets.json5→public/_headers(faithful CORS/cache/content-type parity; deliberately preserves the current no-CSP behavior). Addpublic/_redirectswith certified 301s for every renamed path (page,SKILL.md,.well-known/…/SKILL.md+.zip,api/skills/asset-canister.json). All redirect targets verified present in the build.deploy-ic.yml: add aworkflow_dispatchmodeinput for the one-time reinstall cutover.Deploy runbook (one-time)
This is a canister type change, so the cutover must be a reinstall (the site is stateless — assets are re-synced from the build; same canister ID and custom domain):
workflow_dispatchwithmode: reinstall.Note for skill consumers
npx skillsis name-based and additive — a rename won't auto-clean the old install. Consumers withasset-canistershouldskills remove asset-canisterthenskills add …@static-site. New discovery (index.json,llms.txt) is already clean, and the_redirectscover URL re-fetchers.Evals
All new/changed cases run with baseline. The skill fixes real hallucinations — baseline reaches for
.ic-assets.json5/security_policy/enable_aliasingon the new canister and misses the reinstall gotcha (even fabricating commit details).Output evals (WITH vs WITHOUT skill) + triggers
@dfinity/asset-canister)_redirects.ic-assets.json5+enable_aliasing)_headers/ CSP (no defaults).ic-assets.json5+security_policy)Trigger evals: should-trigger 8/8 | should-not-trigger 8/8 (fires on both static-site and asset-canister queries; does not over-trigger on custom-domain DNS).
npm run validate: 26 skills, all passed (warnings only).npm run build: clean; oldasset-canisteroutputs gone,static-sitepresent, no stale.ic-assets.json5, custom domain preserved.🤖 Generated with Claude Code