Skip to content

Rename asset-canister skill to static-site; adopt certified-assets recipe - #256

Open
marc0olo wants to merge 7 commits into
mainfrom
feat/static-site-recipe
Open

Rename asset-canister skill to static-site; adopt certified-assets recipe#256
marc0olo wants to merge 7 commits into
mainfrom
feat/static-site-recipe

Conversation

@marc0olo

Copy link
Copy Markdown
Member

What & why

@dfinity/asset-canister and @dfinity/static-site are two different canisters, not a rename: static-site (the certified-assets canister) adds response certification, clean-URL canonicalization, _headers/_redirects config, 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

  • Rename skills/asset-canister/skills/static-site/ (+ eval file). SKILL.md rewritten led by the static-site recipe: _redirects SPA rule, _headers/CSP, clean URLs, access protection, authorize-based uploads, presync, and 12 static-site pitfalls. Description triggers on both static-site and asset-canister vocabularies.
  • New references: legacy-asset-canister.md (.ic-assets.json5, AssetManager, grant_permission, allow_raw_access) and migrating-from-asset-canister.md (config mapping + reinstall caveat).
  • Cross-refs updated in icp-cli, deploy-to-cloud-engine, custom-domains, internet-identity, autosync-ic-skills, and CLAUDE.md — static-site recommended, asset-canister 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. 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.
  • Port public/.ic-assets.json5public/_headers (faithful CORS/cache/content-type parity; deliberately preserves the current no-CSP behavior). Add public/_redirects with 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 a workflow_dispatch mode input 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):

  1. Merge this PR (the auto-deploy on push attempts an upgrade and will fail — do not rely on it for the cutover).
  2. Run Deploy to Internet Computer via workflow_dispatch with mode: reinstall.
  3. Subsequent pushes deploy as normal upgrades.

Note for skill consumers

npx skills is name-based and additive — a rename won't auto-clean the old install. Consumers with asset-canister should skills remove asset-canister then skills add …@static-site. New discovery (index.json, llms.txt) is already clean, and the _redirects cover 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_aliasing on the new canister and misses the reinstall gotcha (even fabricating commit details).

Output evals (WITH vs WITHOUT skill) + triggers
Eval WITH WITHOUT
1. Static-site recipe setup 4/4 3/4 (baseline used legacy @dfinity/asset-canister)
2. SPA routing via _redirects 4/4 1/4 (baseline used .ic-assets.json5 + enable_aliasing)
3. AssetManager doesn't work on static-site 4/4 3/4
4. _headers / CSP (no defaults) 4/4 0/4 (baseline used .ic-assets.json5 + security_policy)
5. Migration requires reinstall 4/4 0/4 (baseline missed stable-memory layout; fabricated commit details)
6. Legacy asset-canister SPA still works 3/3 3/3 (regression guard)

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; old asset-canister outputs gone, static-site present, no stale .ic-assets.json5, custom domain preserved.

🤖 Generated with Claude Code

…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).
@marc0olo
marc0olo requested review from a team and JoshDFN as code owners July 30, 2026 10:36
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

Skill Validation Report

Project Checks


WARNINGS (1):
  ⚠ autosync-ic-skills/SKILL.md: missing evaluations/autosync-ic-skills.json — see CONTRIBUTING.md for evaluation guidance

✓ Project checks passed for 7 skills (1 warnings)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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-site skill (with evals) plus legacy/migration reference docs; removed the old asset-canister skill 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-assets docs.
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.

Comment thread .github/workflows/deploy-ic.yml
Comment thread scripts/sync-upstream-check.sh
Comment thread skills/icp-cli/SKILL.md
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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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-cli docs in this repo consistently show --mode as a flag to icp deploy (e.g. icp deploy --mode reinstall ...). Here the workflow puts --mode after 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 _headers file sets Cache-Control only for /_astro/*, /*.html, /styles.css, and /matomo.js. Other root assets in public/ (e.g. favicon.*, og-image.*, apple-touch-icon.png, icp-logo.svg) will be served without any Cache-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 explicit Cache-Control blocks 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.
@marc0olo

Copy link
Copy Markdown
Member Author

Addressed both suppressed (low-confidence) comments from the latest review in 71e9133:

  • deploy-ic.yml --mode ordering — moved the flag before the positional: icp deploy -e ic --mode "$MODE" frontend, matching the documented syntax used in the static-site skill/migration guide. Removes any ambiguity for the one-time reinstall cutover.
  • _headers Cache-Control gaps — added public, max-age=0, must-revalidate for every non-hashed asset family that was left header-less (images, *.css, *.xml, *.zip, robots.txt, AGENTS.md, /api/*.json), restoring parity with the old .ic-assets.json5 **/* default. Patterns are kept narrow and non-overlapping because static-site combines same-name headers rather than overriding — dist/_astro is JS-only, so the extension rules can't collide with the immutable block. No CORS added to /api (matches the original, which scoped CORS to .well-known + llms*.txt).

The push-to-main deploy-fails-in-upgrade-mode point from the earlier review is intentional (reinstall cutover via workflow_dispatch), as noted in that thread.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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-canister skill name that weren’t updated as part of this rename:
  • README.md’s index.json example still uses { "name": "asset-canister", "url": ".../asset-canister/SKILL.md" } (README.md:82-84).
  • .github/CODEOWNERS still 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 fileBuffer but never defines it, so the snippet won’t run as-is. Defining the buffer with readFileSync(...) 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)
@marc0olo

Copy link
Copy Markdown
Member Author

Addressed both suppressed (low-confidence) comments from the latest review in 4f917eb — both were valid, thanks Copilot:

  • Stray asset-canister references after the rename:
    • .github/CODEOWNERS/skills/asset-canister//skills/static-site/ so reviewers are still auto-requested for the renamed path.
    • README.md — the index.json hash example now uses the static-site skill name/URL.
  • Broken snippet in references/legacy-asset-canister.md: the AssetManager.store(fileBuffer, …) example used an undefined fileBuffer; now defined with const fileBuffer = readFileSync("./photo.jpg") so it's copy-paste runnable. (This bug was carried over verbatim from the original asset-canister skill.)

Remaining asset-canister mentions are intentional: the _redirects source paths, the deploy-ic.yml cutover description, and prose in the static-site skill describing the legacy canister. npm run validate still green.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 for workflow_dispatch/reusable workflows; when this workflow runs on push, expression evaluation can fail or be empty unexpectedly. Use github.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.

@marc0olo

Copy link
Copy Markdown
Member Author

Re: the suppressed comment on deploy-ic.yml (${{ inputs.mode }} on a push-triggered workflow) — this is a false positive; no change made.

Per GitHub's contexts docs: the inputs context is populated for workflow_dispatch/workflow_call, and "if you attempt to dereference a nonexistent property, it will evaluate to an empty string" — it does not error or "break expression evaluation." So on a push event, MODE="${{ inputs.mode }}"MODE="" → the else branch runs the normal icp deploy -e ic frontend, which is the intended behavior.

The suggested github.event.inputs.mode resolves to empty on push by the exact same nonexistent-property rule, so it's equivalent — not more correct. inputs.mode is GitHub's modern recommended form for workflow_dispatch inputs, so leaving it as-is.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 as npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm, implying it’s a separate binary/package. Rewording avoids readers skipping the ic-wasm install and then failing when using the metadata field.
- `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)”, but scripts/sync-upstream-check.sh only 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 list filter returns no semver tags, TAG becomes null and 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).
@marc0olo

Copy link
Copy Markdown
Member Author

All three suppressed comments this round were valid — fixed in 84c2686:

  1. static-site/SKILL.mdic-wasm "ships with icp-cli": correct catch. It contradicted the icp-cli skill (and deploy-ic.yml), which install @icp-sdk/ic-wasm as a separate package — a reader could've skipped it and failed on the metadata field. Reworded to point at the icp-cli install (npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm). (The "ships with" phrasing came from upstream certified-assets docs, but our icp-cli skill is authoritative here.)
  2. .claude/upstream.mddocs/ "(all files)": valid — sync-upstream-check.sh uses the GitHub Contents API, which is non-recursive, so it diffs only top-level files in docs/. Clarified that a future nested docs/<subdir>/ would need to be added to the check (all docs are top-level today).
  3. sync-upstream.yml — null TAG guard: valid robustness gap. If no ^v[0-9] release existed, TAG would be empty and the later git/ref/tags/<tag> call would 404 confusingly (and could open a bad issue). Added // empty + a -z guard that fails fast with a clear message.

Verified: YAML parses, npm run validate green.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 /*.zip block 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 forces max-age=0. If a consumer fetches SKILL.zip from 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/*.md endpoints.
/*.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).
@marc0olo

Copy link
Copy Markdown
Member Author

Both suppressed comments were valid, fixed in b7d488f — and reviewing the access-protection guidance surfaced a real runtime bug too:

  1. Access-protection ordering (SKILL.md): valid. The static-site recipe's icp deploy installs the canister and syncs assets in one step, so a plain deploy-then-enable_protection has a brief public window. Rewrote the note and Pitfall 12 to the actual zero-window path: deploy a dir containing only login.html, enable_protection, then deploy the full site (the login page is gate-exempt).

  2. SKILL.zip headers (_headers): valid. /.well-known/skills/<name>/SKILL.zip now gets the same CORS + max-age=300 treatment as its .json/.md discovery siblings (was falling under a /*.zip block with no CORS and max-age=0).

  3. Bonus — issue_token argument form (correctness bug): while fixing (1) I checked the canister's candid. issue_token takes a record (IssueTokenArgs { label; ttl_secs; value }), but the skill used the positional form '("owner", 31536000, opt "...")' — copied from upstream's access-protection.md quick-start, which contradicts both its own .did and the CI-tested example. That call would fail at runtime. Fixed to the record form (+ disable_protection '()'). Verified the rest of the certified-assets calls (authorize/deauthorize/list_authorized/enable_protection/revoke_token) against the .did.

Also did a full self-review pass: version pins all v0.3.1, --mode reinstall ordering consistent, cross-ref skill names all resolve, no positional issue_token remaining. npm run build + npm run validate green.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.mode is only populated for workflow_dispatch runs; on push runs this can evaluate to an undefined context and break expression evaluation. Using github.event.inputs.mode is 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-wasm is only needed when using the optional metadata field, but ic-wasm is 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-cli is 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.
@marc0olo

Copy link
Copy Markdown
Member Author

Addressed all three in 05f64ae:

  1. deploy-ic.yml inputs.mode (repeat): this is the same item I rebutted last round — per GitHub's docs, dereferencing a nonexistent property yields an empty string (not an error), so ${{ inputs.mode }} on a push run is not broken. But since it keeps getting re-flagged and github.event.inputs.mode is exactly equivalent (empty on push by the same rule), I switched to it to end the loop. Not a correctness change.
  2. ic-wasm prerequisite (SKILL.md): valid — reworded so icp-cli and ic-wasm are installed together (official recipes require ic-wasm), instead of implying ic-wasm is optional / only for metadata.
  3. dfx-migration.md assets row: valid — added a caveat that dfx "type": "assets" → static-site is not a drop-in (different canister, _headers/_redirects not .ic-assets.json5, reinstall needed) with a pointer to the migration guide.

Build + validate green; workflow YAML parses and the mode dispatch input is unchanged.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you autosync is this skill actually going to get deleted?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants