Skip to content

chore(sync): track caffeinelabs/skills (commit-based) instead of retired caffeinelabs/motoko - #346

Open
marc0olo wants to merge 5 commits into
mainfrom
chore/sync-upstream-caffeinelabs-skills
Open

chore(sync): track caffeinelabs/skills (commit-based) instead of retired caffeinelabs/motoko#346
marc0olo wants to merge 5 commits into
mainfrom
chore/sync-upstream-caffeinelabs-skills

Conversation

@marc0olo

Copy link
Copy Markdown
Member

Wires the upstream sync automation to the new Motoko upstream (caffeinelabs/skills), which #345 switched us to. That repo publishes no releases, so this reworks the detection + docs from release-based to commit-based for it. mops-cli (caffeinelabs/mops) and static-site (dfinity/certified-assets) stay release-based and are untouched.

Completes the second half of #327 (the skill migration was #345).

Changes

  • .github/workflows/sync-upstream.yml — replace the check-motoko job (which polled gh release view on the retired caffeinelabs/motoko) with check-caffeinelabs-skills: it reads the pinned commit from .claude/upstream.md (the writing-motoko section — all three skills share one pin), compares it to the upstream default-branch HEAD, and on drift opens an issue labelled upstream-skills (label already created). No tag/release resolution step needed.
  • scripts/sync-upstream-check.sh — swap the caffeinelabs/motoko case for caffeinelabs/skills (upstream base path skills/, three skill mappings: writing-motoko, migrating-motoko-actors, troubleshooting-motoko-migrations). Clarify that the label args are display-only (a release tag, or a short SHA for commit-tracked repos).
  • .claude/CLAUDE.md — document the two tracking models (release-tracked vs commit-tracked), update the sync checklist, rewrite the "What icskills changes vs upstream" summary table (frontmatter transform, mops-cli cross-ref, references/ paths; drop the obsolete motoko-pitfalls / caffeinelabs/motoko#6156/6157 row), and generalise the automated-detection section.

Tested locally

awk pin           → 02e531673bad… (writing-motoko section)
same commit       → exit 0  "no content changes"
pinned → HEAD     → exit 1, correct issue body (branch suggestion chore/sync-upstream-skills-<sha>, per-skill diffs)

No skill content changes; npm run validate green.

Note

The local test showed upstream has already advanced (writing-motoko 0.1.3 → 0.1.5, plus more). So once this merges, the weekly job (or a manual workflow_dispatch) will open an upstream-skills sync issue — that content sync is a separate follow-up, not this PR.

🤖 Generated with Claude Code

…red caffeinelabs/motoko

The Motoko skills now sync from caffeinelabs/skills (writing-motoko,
migrating-motoko-actors, troubleshooting-motoko-migrations), which publishes
no releases. Rework the sync automation and docs from release-based to
commit-based for this upstream; mops-cli and static-site stay release-based.

- .github/workflows/sync-upstream.yml: replace the check-motoko job (gh release
  view on caffeinelabs/motoko) with check-caffeinelabs-skills — reads the pinned
  commit from .claude/upstream.md (writing-motoko section), compares it to the
  default-branch HEAD, and opens an `upstream-skills`-labelled issue on drift.
- scripts/sync-upstream-check.sh: swap the caffeinelabs/motoko case for
  caffeinelabs/skills (base path skills/, three new skill mappings); clarify the
  label args are display-only (tag or short SHA).
- .claude/CLAUDE.md: document both tracking models (release-tracked vs
  commit-tracked), update the checklist, the owned-section summary table
  (frontmatter transform, mops-cli cross-ref, references/ paths; drop the
  retired motoko-pitfalls row), and the automated-detection section.

Tested locally: awk pins 02e5316; same-commit → exit 0; pinned→HEAD → exit 1
with a correct issue body. (Upstream has already advanced to writing-motoko
0.1.5, so a content sync will be due after this lands — separate follow-up.)

Part of #327.
@marc0olo
marc0olo requested review from a team and JoshDFN as code owners August 11, 2026 18:49
@marc0olo
marc0olo requested a lite review from Copilot August 11, 2026 18:53

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.

Pull request overview

Updates the upstream sync automation to track the new Motoko upstream (caffeinelabs/skills) by pinned commit SHA (since it has no releases), while keeping the existing release-based tracking for caffeinelabs/mops and dfinity/certified-assets.

Changes:

  • Replaces the retired caffeinelabs/motoko release check with a commit-based caffeinelabs/skills check in the sync workflow.
  • Updates the upstream-diff script’s repo mapping from caffeinelabs/motokocaffeinelabs/skills.
  • Documents the two upstream tracking models (release-tracked vs commit-tracked) and updates the automated detection docs accordingly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
scripts/sync-upstream-check.sh Switches Motoko upstream mapping to caffeinelabs/skills and updates commit-based framing.
.github/workflows/sync-upstream.yml Adds a new commit-based job for caffeinelabs/skills and removes the old caffeinelabs/motoko release-based logic.
.claude/CLAUDE.md Updates upstream-sync documentation to describe commit-tracked vs release-tracked models and the new automation behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/sync-upstream-check.sh
Comment thread .claude/CLAUDE.md
PR review (Copilot): the check enumerated only top-level files via the GitHub
Contents API (non-recursive), so nested files like writing-motoko/references/*
were never diffed — the automation would miss upstream changes to them.

Replace the per-skill Contents API listing with a recursive Git Trees API walk
(?recursive=1) filtered to the skill path (paths returned relative to it, so the
raw-content fetch and diff headers now include e.g. references/control-flow.md).
This also closes the previously-documented "future nested docs/<subdir>/ not
covered" caveat for static-site. Warns on stderr if the tree is truncated.

Updated the manual list-files snippet in CLAUDE.md and the static-site note in
.claude/upstream.md to match (recursive, not top-level-only).

Verified locally: 02e5316 -> HEAD now reports a change in
writing-motoko/references/control-flow.md that the old top-level-only listing
missed.

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (1)

scripts/sync-upstream-check.sh:82

  • list_skill_files currently treats GitHub API failures as “no files” (JSON parse errors sys.exit(0) and the trailing || true) and only warns on truncated trees. That can produce false “no changes” results and prevent the workflow from opening a sync issue when the file listing is incomplete/untrusted. Prefer failing fast when the tree can’t be reliably enumerated (API error, parse error, or truncated: true).
list_skill_files() {
  curl -sf "https://api.github.com/repos/${REPO}/git/trees/$1?recursive=1" \
    -H "Authorization: Bearer $GH_TOKEN" | \
  python3 -c "
import sys, json
prefix = sys.argv[1].rstrip('/') + '/'
try:
    d = json.load(sys.stdin)
except Exception:
    sys.exit(0)
if d.get('truncated'):
    sys.stderr.write('WARNING: git tree truncated; files under %s may be missed\n' % sys.argv[1])
for e in d.get('tree', []):
    if e.get('type') == 'blob' and e['path'].startswith(prefix):
        print(e['path'][len(prefix):])
" "$2" || true
}

PR review (Copilot, suppressed): list_skill_files treated API/parse errors as
'no files' (sys.exit(0) + trailing || true) and only warned on truncated trees,
so a transient fetch failure or a truncated tree could yield an empty listing →
'no changes' → no sync issue opened (a silent false-negative).

Now the helper returns non-zero on a fetch error, unparseable JSON, or
truncated: true, and the caller aborts the run with exit 3. The workflow's diff
step re-raises any non-0/1 code, so the job fails loudly instead of skipping a
real upstream change. Verified: happy path still exit 1 (nested references/
included); a bogus SHA now exits 3 with a clear error instead of exit 0.
@marc0olo

Copy link
Copy Markdown
Member Author

Re the suppressed comment on list_skill_files (b33e763): valid — applied it. The helper now fails loudly instead of treating an untrusted listing as "no changes": a fetch error, unparseable JSON, or truncated: true returns non-zero, and the caller aborts with exit 3 (the workflow's diff step re-raises any non-0/1 code, so the job fails visibly rather than silently skipping a real upstream change). Verified: happy path still exits 1 with the nested references/* diffs; a bogus SHA now exits 3 with a clear error instead of exit 0.

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (1)

scripts/sync-upstream-check.sh:74

  • list_skill_files downloads the full repo tree JSON every time it’s called. Because this function is invoked once per skill (and twice per skill for OLD/NEW), the workflow ends up re-fetching the same large tree multiple times in a single run. Caching the tree per SHA within the run reduces API load and lowers the risk of rate-limit/timeout flakiness.
list_skill_files() {
  local tree
  # Fail (non-zero) on a fetch error, unparseable response, or a truncated tree — the caller
  # aborts rather than treating an incomplete/untrusted listing as "no changes" (a silent miss).
  tree=$(curl -sf "https://api.github.com/repos/${REPO}/git/trees/$1?recursive=1" \

…r skill

PR review (Copilot, suppressed): list_skill_files downloaded the full recursive
repo tree on every call — once per skill x OLD/NEW = 6 fetches for caffeinelabs/
skills, but only 2 unique trees. Split out fetch_tree() which caches each tree to
/tmp/upstream-tree-<sha>.json (git trees are immutable per SHA, so caching is
safe) and reads from it. Now 2 fetches per run instead of 6 — less API load and
fewer transient-failure points (which now abort under the fail-fast change).

Verified: happy path exit 1 (nested references/ diffed) with exactly 2 cache
files; bogus SHA exit 3; same commit exit 0.
@marc0olo

Copy link
Copy Markdown
Member Author

Re the suppressed comment on list_skill_files re-fetching the tree (a2d680b): applied — split out fetch_tree() which caches each recursive tree to /tmp/upstream-tree-<sha>.json (git trees are immutable per SHA, so caching is safe) and reads from it. For caffeinelabs/skills that's now 2 fetches per run instead of 6 (3 skills × OLD/NEW). Bonus: fewer fetch points also means fewer transient-failure aborts under the fail-fast change. Verified: happy path exit 1 with exactly 2 cache files created and nested references/* still diffed; bogus SHA exit 3; same-commit exit 0.

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (2)

scripts/sync-upstream-check.sh:78

  • fetch_tree caches the Git Trees API response at a predictable, repo-agnostic path (/tmp/upstream-tree-${sha}.json). This can collide across different upstream repos that happen to share the same commit SHA, and it also risks leaving a partially-written cache file behind (which then won’t be refetched because it is non-empty). Use a per-run/per-repo cache directory (e.g. $RUNNER_TEMP) and write atomically via a temp file + mv.
  local cache="/tmp/upstream-tree-${sha}.json"
  if [ ! -s "$cache" ]; then
    curl -sf "https://api.github.com/repos/${REPO}/git/trees/${sha}?recursive=1" \
      -H "Authorization: Bearer $GH_TOKEN" > "$cache" || {
      echo "ERROR: could not fetch git tree for ${REPO}@${sha}" >&2

.claude/CLAUDE.md:152

  • The example says it “lists all files … in the upstream skill folder”, but the git/trees/<SHA>?recursive=1 request actually returns the full repo tree for that commit and the Python filters by prefix locally. Clarifying this avoids surprising readers about the API response size and potential rate limiting.
# List all files (recursively) in the upstream skill folder at a given commit.
# Recursive matters: some skills nest files (e.g. writing-motoko/references/*).
curl -s "https://api.github.com/repos/<org>/<repo>/git/trees/<SHA>?recursive=1" | \
  python3 -c "import sys,json; p='<upstream-skill-path>/'; [print(e['path'][len(p):]) for e in json.load(sys.stdin).get('tree',[]) if e.get('type')=='blob' and e['path'].startswith(p)]"

… docs

PR review (Copilot, suppressed x2):
- fetch_tree cached at a repo-agnostic path and wrote in place, so a partial/
  interrupted write could leave a non-empty-but-corrupt cache that [ -s ] then
  trusts, and different repos sharing a SHA could collide. Now cache under
  ${RUNNER_TEMP:-/tmp}, key by repo+SHA, and write to a temp file + mv (atomic:
  the cache is only ever complete or absent).
- CLAUDE.md manual snippet: clarify the Git Trees API returns the WHOLE repo
  tree and the Python filters to the skill path locally (response-size/rate-limit
  awareness).

Verified: happy path exit 1 with repo-scoped cache and no leftover .tmp; bogus
SHA exit 3 leaving no partial cache.
@marc0olo

Copy link
Copy Markdown
Member Author

Both suppressed comments applied (f3e1204 review):

  1. fetch_tree cache — now repo-scoped and atomic: cache path is ${RUNNER_TEMP:-/tmp}/upstream-tree-<repo>-<sha>.json (keyed by repo + SHA), and the response is written to a .tmp.$$ file then mv'd into place — so the cache is only ever complete or absent (no partial/corrupt file that [ -s ] would wrongly trust). Verified: bogus SHA exits 3 leaving no partial cache.
  2. CLAUDE.md manual snippet — clarified that git/trees/<SHA>?recursive=1 returns the whole repo tree and the Python filters to the skill path locally.

Both are our own content, so patched directly.

@marc0olo
marc0olo requested a lite review from Copilot August 11, 2026 19:35
@github-actions

Copy link
Copy Markdown

Skill Validation Report

No skill files were changed in this PR — validation skipped.

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

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.

2 participants