Agent skill

Codap V3 Build

by concord-consortium in concord-consortium/codap

A skill your agent uses when preparing a CODAP v3 release, creating release notes, updating version files, creating release PRs, tagging releases, or deploying to staging/production.

MITAuto-check passedDevelopment

Install Codap V3 Build

skills CLI
$ npx skills add concord-consortium/codap --skill codap-v3-build -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install concord-consortium/codap codap-v3-build --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/concord-consortium/codap.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/codap-v3-build .claude/skills/codap-v3-build && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
codap-v3-build
GitHub stars
106
Token cost
~15k tokens
SKILL.md length
7,108 words
Files
1
Skills in repo
3
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when preparing a CODAP v3 release, creating release notes, updating version files, creating release PRs, tagging releases, or deploying to staging/production.

  • Works in 6 steps: Prepare the Release → Prepare Release Notes → Update Version Files → …
  • Preparing a CODAP v3 release
  • SKILL.md covers Overview, Quick Reference, Getting Started and Approval Gates, plus 10 more sections
  • Calls git, gh and npm; reaches codap3.concord.org and concord-consortium.atlassian.net; needs API_TOKEN and POEDITOR_API_TOKEN

What it does

Codap V3 Build is an agent skill from concord-consortium/codap. Use when preparing a CODAP v3 release, creating release notes, updating version files, creating release PRs, tagging releases, or deploying to staging/production. Invoke with phase name or version number to resume.

Its SKILL.md is about 15k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Development, covering Changelog and release notes. It works with Jira. The repository describes itself as: CODAP (Common Online Data Analysis Platform). The licence is MIT.

When your agent uses it

  • Preparing a CODAP v3 release
  • Creating release notes
  • Updating version files
  • Creating release PRs

Example prompts

  • “/codap-v3-build”

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Prepare the Release
  2. Prepare Release Notes
  3. Update Version Files
  4. Create Release PR
  5. Tag
  6. Deploy

What it can do on your machine

Read from SKILL.md and the folder at commit 4bcb0ff. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • git
    • gh
    • npm
    • curl
    • node
    • python3

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • codap3.concord.org
    • concord-consortium.atlassian.net
    • api.poeditor.com
    • githubstatus.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • API_TOKEN
    • POEDITOR_API_TOKEN

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Codap V3 Build loads about 15k tokens when it runs. Until then it costs about 57 tokens; SKILL.md has 7,108 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~57
When it runs · the whole SKILL.md, loaded when a task matches
~15k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from concord-consortium/codap at commit 4bcb0ff, republished under its MIT licence (© concord-consortium). 7,108 words, ~14,757 tokens.

Download SKILL.mdSave it as .claude/skills/codap-v3-build/SKILL.md (or your agent's skills folder).
name
codap-v3-build
description
Use when preparing a CODAP v3 release, creating release notes, updating version files, creating release PRs, tagging releases, or deploying to staging/production. Invoke with phase name or version number to resume.

CODAP v3 Build & Release

Overview

Interactive workflow for CODAP v3 releases. Guides you through Jira setup, release notes generation, version updates, PR creation, tagging, and deployment.

Quick Reference

PhaseCommandDescription
1/codap-v3-buildPrepare release (Jira version, tag stories)
2/codap-v3-build notesPrepare release notes (interactive)
3/codap-v3-build filesUpdate version files
4/codap-v3-build prCreate release PR
5/codap-v3-build tagTag the release (triggers S3 build)
6/codap-v3-build deploy [version]Deploy to staging/production, publish GitHub release
fix/codap-v3-build fix {old-version}Revise release after staging QA failure

Getting Started

When invoked, introduce the skill:

This skill will walk you through the process of building a release of CODAP v3. The process has 6 phases:

  1. Prepare the Release - Set up Jira version and gather context
  2. Prepare Release Notes - Interactive walkthrough to create CHANGELOG entry
  3. Update Version Files - Update package.json, versions.md, CHANGELOG.md
  4. Create Release PR - Build, capture asset sizes, create PR
  5. Tag - After PR merge, create git tag (triggers the S3 build)
  6. Deploy - Stage, QA, deploy to production, then publish the GitHub release

Are you ready to proceed?

Wait for user confirmation before starting Phase 1.

Approval Gates

Every action below changes shared state that other people see or depend on. For each one:

  1. Show exactly what will happen: the full command, the Jira issues and fields, or the full message text.
  2. Wait for the user's approval.
  3. Do it, then read the result back (the run's outcome, the stored Jira value, the posted message) and report it.

An approval covers only the actions it names. Approving the staging deploy does not approve the Slack post that follows it, and "go ahead with everything" early in the session does not lift later gates.

ActionWhereRead back with
POEditor pushPhase 3, step 2bthe script's output
POEditor translation fixesPhase 3, step 2dthe API response, then the re-pulled file
git push of a release branch, gh pr createPhase 4; Fix, Step 4gh pr view --json url,labels
Tag push or deletionPhase 5; Fix, Step 5git ls-remote --tags origin '{version}^{}'
Workflow dispatch (staging, production, beta)Phase 6; Fix, Step 6Dispatch, watch, verify
gh release createPhase 6gh release view {version} --json url,isDraft,isPrerelease
Jira edits (Fix Versions, version rename)Phase 2, step 9; Fix, Step 6re-fetch the changed fields
Slack posts outside the developer's self-DMPhase 6; Fix, Step 6the posted message's ts (Slack's message ID)

Not gated: reads, builds, local commits, and previews posted to the developer's own Slack self-DM (Slack Posts).

Phase 1: Prepare the Release

Goal: Create Jira release version and gather context.

Steps
  1. Check workspace status:

    bash
    git status
    • If there are modified/staged files, ask user to: Stash / Commit / Discard
    • Untracked files are okay to leave
  2. Ensure on main branch with latest:

    bash
    git checkout main
    git pull
  3. Get current build number:

    bash
    cat v3/build_number.json

    Needed for the version string only in the pre-release phase (see step 6), but always worth showing as context.

  4. Get previous release tag:

    bash
    git tag --sort=-creatordate | head -5
  5. Show context to user:

    • Display last 3 releases (version and date)
    • Show current build number from build_number.json
  6. Determine recommended version string:

    CODAP v3 has two versioning conventions, one per development phase. Identify the current phase from the newest release tag, then apply that phase's rule.

    bash
    git tag --sort=-creatordate | head -3
    PhaseYou are here when…Version formatRule
    Production releaseThe newest release tag is plain semver with no prerelease suffix (3.0.4, 3.1.2)MAJOR.MINOR.PATCHIncrement from the last released version: patch for bug fixes, minor for new features, major for breaking changes. Example: after 3.0.4 → 3.0.5.
    Pre-release developmentThe newest release tag carries a prerelease suffix (-beta, -rc, -pre){major}.{minor}.{patch}-{suffix}.{buildNumber}Use build number + 1 (it auto-increments when the release PR merges), carrying over the previous tag's version prefix and suffix. Example: newest tag 3.0.0-beta.2662 with build 2662 → 3.0.0-beta.2663.

    The build number is part of the version string ONLY in the pre-release phase. In the production phase the build number still exists and still increments, but it is independent of the version — do not derive the version from build_number.json. (At the 3.0.5 release the build number was 2956, heading to 2957 on merge, while the version was 3.0.5. The two are unrelated and will never match again.)

    CODAP v3 entered the production phase at 3.0.0 on 2026-06-04. The pre-release rule is retained because the project may re-enter a pre-release phase for a future major version (e.g. a 4.0.0-beta.N series), at which point it applies again.

    Exception — starting a new prerelease series. Inferring the phase from the newest tag is reliable within a phase but blind to the moment you deliberately switch. When the next major begins its prerelease series, the newest tag is still production semver (e.g. 3.1.4), so the rule above would wrongly say "production, bump the patch" instead of 4.0.0-beta.N. A tag can't signal an intent to change phase. So before applying the rule, ask the user whether this release starts a new prerelease series for a new major version. If yes, switch to the pre-release convention and confirm the intended prefix (4.0.0) and suffix (-beta) with them rather than inferring.

    Which component to bump is a judgment call about the release's contents, not a mechanical rule — propose one and confirm it with the user along with the rest of the Jira release details (step 8). Count only what users can see: work behind a feature flag, logging, and docs don't make a release minor, and by convention neither does a new translation or language.

  7. Get previous release date from Jira (for start date default)

  8. Ask user for Jira release details:

    FieldDefaultOptions
    Version nameThe phase-appropriate next version from step 6Production phase: patch / minor / major bump. Pre-release phase: previous suffix + new build number
    Start datePrevious release dateUser can modify
    Release dateToday's dateToday / Tomorrow / Custom future date
    DescriptionVersion {version}User can modify

    Note: The release date chosen here is used throughout the process:

    • CHANGELOG.md header date
    • versions.md entry date
    • Jira release date
  9. Ensure the Jira release version exists (status: Unreleased).

    The Atlassian MCP tools cannot create a version or edit its release date — they expose no version-management tool. This step is the user's to perform, in the Jira UI (CODAP → Releases). Setting issue Fix Versions (Phase 2, step 9) works fine through MCP; it is only the version object itself that is out of reach.

    • Check whether it already exists first — Jira automation may have created the version (and a Release {version} tracking issue) ahead of time. Ask the user to check Jira → CODAP → Releases; that is the only reliable check. Querying the fixVersions field via JQL is a weak fallback: it can only surface versions already assigned to at least one issue, so a freshly created version with nothing assigned to it — the very case this step is looking for — will not appear. Absence from JQL is not evidence the version is missing.
    • If it exists, confirm its release date matches the date agreed in step 8; if it doesn't, ask the user to correct it.
    • If it doesn't exist, ask the user to create it with the agreed name, dates, and description, and to confirm once done.

    The date only has to be correct before the release is marked Released in Phase 6, so this need not block the rest of the workflow.

  10. Put the release tracking issue in the current sprint. Jira automation creates a Release {version} issue (type Release) along with the version, and it lands in the backlog. Once the user confirms the version exists, have a subagent find it and the active sprint:

    project = CODAP AND issuetype = Release AND fixVersion = "{version}"   -- with customfield_10020
    project = CODAP AND sprint in openSprints()     -- customfield_10020, maxResults 50

    If the tracking issue's customfield_10020 already holds the active sprint, report that and skip the edit (the user may have moved it already). Sprint IDs are not sequential, so read the active sprint's id, name, and endDate from customfield_10020 rather than guessing; scan all results, since an issue can carry several sprints. If more than one sprint is active, or the active sprint ends before the release date, ask which sprint to use.

    Setting the sprint is a gated Jira edit: show the issue key, its summary, and the sprint name and ID, then set customfield_10020 to the sprint's ID (a plain integer, not an object) with editJiraIssue, and re-fetch the field to confirm it. If no tracking issue exists, say so; don't create one.

Phase 2: Prepare Release Notes

Goal: Generate CHANGELOG entry with user-selected titles.

Steps
  1. Get PRs since last release:

    bash
    git log <last-tag>..HEAD --oneline | grep -E '\(#[0-9]+\)|Merge pull request #[0-9]+'

    Note: This finds both regular merge commits AND squash-merged PRs (which include (#123) in the commit message). Using --merges alone misses squash merges.

  2. Get PR details from GitHub:

    bash
    gh pr view <number> --json number,title,headRefName
  3. Match PRs to Jira stories by CODAP-XXX ID:

    • Check branch name first (most reliable, e.g., CODAP-1027-inbounds-url-param)
    • Then PR title (e.g., CODAP-1027: Implement inbounds parameter)
    • Use caution with PR descriptions - they may reference related stories (e.g., "Follow-up to CODAP-XXX") that aren't the primary story for this PR
    • Extract unique CODAP-XXX IDs
  4. For each matched item, fetch:

    • Jira story details (summary, issue type, current status)
    • PR title from GitHub
    • Generate AI-suggested title (concise, user-focused)
  5. Interactive walkthrough for each item:

    IMPORTANT - NO SHORTCUTS:

    • Do NOT ask user to approve the entire list at once
    • Do NOT batch items together (e.g., "approve these 3 items")
    • Do NOT skip showing title options
    • ALWAYS go through items ONE BY ONE, presenting all title options for each

    IMPORTANT - PUT THE TABLE IN THE QUESTION: Text written in the same turn as an AskUserQuestion call is not shown to the user, so a table output just before the question is invisible and the user has nothing to decide from. Put each item's table in the preview of every option of the Section question (previews render as monospace markdown beside the options), and put the candidate titles in the option descriptions of the Title question.

    The preview for each item:

    Item 1/8: CODAP-1027 (Story) — Jira: Done
    
    | Source        | Title       |
    |---------------|-------------|
    | AI suggestion | {ai_title}  |
    | Jira          | {jira_summary} |
    | PR            | {pr_title}  |
    
    PR #NNNN. {one or two lines of context: what the user would see, whether it is
    flag-gated, anything that bears on the section choice}

    Note: Strip Jira IDs from PR titles before presenting (e.g., "CODAP-138: Fix point color" → "Fix point color")

    Jira status notice (if not Done): append the status to the preview's first line with a warning indicator, e.g. Item 1/8: CODAP-1027 (Story) — Jira: In Project Team Review ⚠️. The user can choose to exclude the story via the Section question.

    Ask using AskUserQuestion (Section and Title are TWO SEPARATE CALLS so Title is skipped if Exclude):

    Section question:

    • Question: "Item N/M: CODAP-XXXX ({type}) — which section?"
    • Options: Features / Bug Fixes / Under the Hood / Exclude, with the recommended one first
    • Recommend from what users will see, not from the issue type alone. Read the PR body when the type and the change disagree:
      • Work behind a feature flag → Exclude. But check each PR: a story in a flag-gated epic can still ship an ungated, visible change (in 3.1.1, a Format palette redesign and a legend-behavior fix both came from the flag-gated point-shapes epic).
      • A story that fixes broken behavior → Bug Fixes.
      • A fix to something no released version has shipped → Exclude; users never saw the bug (e.g. corrections to a language first released in the same version).
      • Docs, plans, logging, and CI/deploy infrastructure → Exclude.
      • Otherwise: Features for Stories, Bug Fixes for Bugs.
    • If user types in "Other", interpret as an instruction (e.g., "go back to previous item") and handle accordingly

    If Section is NOT Exclude - ask Title question:

    • Question: "CODAP-XXXX — which title? (or type your own in 'Other')"
    • Options: AI suggestion / Jira / PR, each with its full title as the option description (no "Custom" - user types preferred title in built-in "Other")
    • If the chosen section changes the framing (e.g. a Story moved to Bug Fixes), reword the AI suggestion to match and say so in the question
    • If user types in "Other", use their text as the title
    • Title option order must ALWAYS be: AI suggestion, Jira, PR (both in the preview and in question options)
    • Stories included in release notes will have their Fix Version updated automatically (tracked for step 9)

    If Section IS Exclude - ask Fix Version question:

    • Question: "Should CODAP-XXXX's Fix Version be set to this release?"
    • Options: Yes / No, with the recommended one first
    • Recommend Yes when the story's work is complete in this release, even if it isn't user-facing (flag-gated work, docs, infrastructure) — it should still be tracked in Jira.
    • Recommend No when the story is still In Progress or is an epic with open stories: more work will follow, so it isn't "fixed" in this version.
    • If Yes: Add to Fix Version update list (step 9) even though excluded from release notes
    • If No: Do not update Fix Version (e.g., if the story was fixed in a prior release, or the PR isn't part of this release)

    After selection, confirm:

    ✓ CODAP-1027 → Features: "Selected title here"

  6. For PRs without Jira IDs:

    • Show PR title only
    • Default recommendation: Exclude (Recommended) for docs, dependencies, maintenance
    • Option to include in Under the Hood if relevant
    • No Fix Version to update (no Jira story)
  7. Generate CHANGELOG markdown after all items are processed:

    markdown
    ## Version {version} - Month Day, Year
    
    ### ✨ Features & Improvements:
    - **CODAP-XXX:** Title here
    - **CODAP-YYY:** Another title
    
    ### 🐞 Bug Fixes:
    - **CODAP-AAA:** Fix description
    - **CODAP-BBB:** Another fix
    
    ### 🛠️ Under the Hood:
    - **CODAP-ZZZ:** Internal improvement

    Rules:

    • Order items by numeric Jira ID (223 before 1027)
    • Only include sections that have items
    • Use the release date from Phase 1
    • Date format: Month Day, Year (e.g., February 1, 2026)
  8. Present generated markdown for approval:

    Show the complete CHANGELOG entry as a markdown code block and end the turn with a plain question — do not use AskUserQuestion here. The entry is too long for an option preview, and text in the same turn as a question isn't shown, so the user would be asked to approve notes they can't see. Ask whether to approve, edit an item (section or title), or reorder. The user often reviews the whole entry for consistency at this point (e.g. capitalization, or similar items landing in different sections), so expect edits.

    In the same message, list the issues step 9 will set the Fix Version on, so a single reply can approve both the notes and that gated Jira edit.

    Note: Mention that Asset Sizes will be added in Phase 4 after the build.

  9. Update Jira Fix Versions for all stories where user approved the update (during step 5).

    This is a gated action: list the story IDs and the version first, and wait for approval.

    IMPORTANT - Context Management: Jira MCP responses can be verbose and consume significant context. Delegate this bulk operation to a subagent:

    Use the Task tool to update Fix Versions for all approved stories. Provide the subagent with:

    • The list of CODAP-XXX story IDs to update
    • The version string to set (e.g., 3.0.5)

    The subagent should report back ONLY:

    • Success/failure count (e.g., "Updated 8/10 stories successfully")
    • IDs of any stories that failed (e.g., "Failed: CODAP-123, CODAP-456")
  10. Check the fix version from the Jira side. Steps 1–9 match only from PR to story, so a story that carries the fix version but has no merged PR in the release range goes unnoticed. Have a subagent run this JQL and report key, summary, issue type, status, assignee, and Project Team Approver for each result:

    project = CODAP AND fixVersion = "{version}" ORDER BY key

    Compare the results with the stories matched in step 3 and show the user:

    • Stories on the version with no merged PR in the range. Ask whether each belongs in this release (e.g. a story with no code, or a PR merged before the previous tag), or whether its Fix Version should be removed. Removing it is a gated Jira edit. The Release {version} issue (type Release) that Jira automation creates with the version is expected here and needs no action.
    • Stories that are not Done, grouped by status. These are expected at this point (most stories sit in "In Project Team Review" until after the release), so this is informational. They are checked again before the version is marked released (Phase 6).
    • Stories whose PR is merged but whose status is earlier than In Project Team Review (e.g. still "Ready for Merge"). Point these out. Before moving one to In Project Team Review (a gated Jira edit), check that it has testing instructions the Project Team Approver can follow, and offer to draft them from the PR if not.

    This check and the read-back of step 9 are the same query, so one subagent can do both.

    JQL can query this reliably now that step 9 has assigned the version to issues; the caveat in Phase 1, step 9 applies only to a version with no issues yet.

Phase 3: Update Version Files

Goal: Sync translations, update all version-related files, and create release branch.

IMPORTANT — Working Directory Awareness:

  • Scripts in v3/scripts/ use cd v3 internally, which changes the shell's working directory for subsequent commands in the same Bash call.
  • After running a v3 script, always verify your working directory with pwd before running git commands.
  • All git commands must be run from the repository root (/path/to/codap), not from v3/.
  • If a git diff or git status command produces no output, do NOT assume "no changes" — verify by checking the working directory and trying again with correct paths. Empty output from git commands that should show changes is a red flag that something is wrong.
Steps

IMPORTANT — Branch policy: Never commit directly to main. The release branch must be created before any commits (translations, version files, etc.).

  1. Create release branch:

    bash
    git checkout -b release-{version}

    Branch naming rules:

    • Pattern: release-{version} where {version} is from Phase 1 (e.g., release-3.0.5)
    • Do NOT use / in branch names
    • Do NOT invent your own pattern
  2. Sync translations with POEditor:

    V3 owns all string pushes to POEditor — both DG.* and V3.* keys. All English strings live in a single file: src/utilities/translation/lang/en-US.json5.

    API Token: All scripts resolve the token in order: -a argument > ~/.porc > $POEDITOR_API_TOKEN env var. Only ask the user for a token if none of these are configured.

    2a. Preview English string changes before pushing:

    Before pushing, pull the current English strings from POEditor and diff them against the local en-US.json5 so the user can validate the changes.

    bash
    cd v3
    # Pull current English strings from POEditor to a temp file
    ./scripts/strings-pull.sh -p 125447 -l en-US -o /tmp
    # Convert local JSON5 to JSON for comparison
    node -e "
    const fs = require('fs');
    const JSON5 = require('json5');
    const data = JSON5.parse(fs.readFileSync('src/utilities/translation/lang/en-US.json5', 'utf8'));
    fs.writeFileSync('/tmp/en-US-local.json', JSON.stringify(data, null, 4) + '\n');
    "
    # Detailed diff showing new keys, changed values, and keys only in POEditor
    node -e "
    const poeditor = require('/tmp/en-US.json');
    const local = require('/tmp/en-US-local.json');
    const changed = [], newKeys = [], missingLocally = [];
    for (const k of Object.keys(local)) {
      if (!(k in poeditor)) newKeys.push(k);
      else if (poeditor[k] !== local[k]) changed.push({key: k, old: poeditor[k], new: local[k]});
    }
    for (const k of Object.keys(poeditor)) {
      if (!(k in local)) missingLocally.push(k);
    }
    console.log('=== VALUE CHANGES (' + changed.length + ' keys) ===');
    changed.forEach(c => {
      console.log('  ' + c.key);
      console.log('    POEditor: ' + JSON.stringify(c.old));
      console.log('    Local:    ' + JSON.stringify(c.new));
      console.log();
    });
    console.log('=== NEW KEYS (' + newKeys.length + ' keys) ===');
    newKeys.forEach(k => console.log('  ' + k + ': ' + JSON.stringify(local[k])));
    console.log();
    console.log('=== KEYS IN POEDITOR BUT NOT LOCAL (' + missingLocally.length + ' keys) ===');
    missingLocally.forEach(k => console.log('  ' + k + ': ' + JSON.stringify(poeditor[k])));
    "

    Show the diff to the user. Common expected changes:

    • New keys added since the last release (lines only in local)
    • Updated string values

    Red flags to call out:

    • Keys present in POEditor but missing locally (would NOT be deleted since sync_terms=0, but worth noting)
    • Unexpected value changes to existing keys

    Decide whether to push — based solely on whether English strings changed:

    • No value changes AND no new keys (English strings unchanged): skip the push entirely — do not ask. There is nothing to upload, so the push (2b) would be a no-op. Note that English is unchanged and go straight to the mandatory pull (2c).
    • There ARE English string changes (new keys or changed values): show the diff and ask the user to approve the push before proceeding to 2b.

    2b. Push English strings to POEditor (only when 2a found English changes):

    bash
    ./scripts/strings-push-project.sh

    This pushes all strings from en-US.json5 (both DG and V3 keys) to POEditor. The push is additive (sync_terms=0) — it adds new terms and updates existing values but never deletes terms. Push first so that the subsequent pull includes any new keys added since the last release.

    2c. Pull non-English translations:

    bash
    ./scripts/strings-pull-project.sh

    This pulls translated strings for all supported languages. Report results to the user (the streaming output may be collapsed in the UI).

    Always run the pull — never skip it and never ask whether to pull. Every build must pull from POEditor, because translators may have added or updated non-English strings since the last release even when the English strings are unchanged. (The push in 2b is conditional; this pull is not.)

    2d. Verify and commit pulled translations:

    Return to the repository root before running git commands:

    bash
    cd /path/to/codap   # repository root, NOT v3/
    git status -- v3/src/utilities/translation/lang/

    Report results to the user. Every language normally gains the new English keys from 2b (untranslated keys arrive with the English text). Also review changes to existing translations — values translators edited in POEditor since the last release. These go straight into the release, and nothing else checks them. List them, filtering out the new keys:

    bash
    git diff -U0 -- v3/src/utilities/translation/lang/ | grep -E '^(\+\+\+|[-+] )' \
      | grep -vE '<new-key-pattern>'   # e.g. 'pointShape|section\.graph|...' from 2a's NEW KEYS

    A changed line can also be just a trailing comma, where a new key was appended after what used to be the last entry; ignore those. Show the user each changed value, old → new, and flag anything that looks wrong: typos, broken placeholders (%@), lost punctuation. In the 3.1.1 release, two French typos arrived this way.

    If a translation needs fixing, fix it in POEditor, not in the local file (the next pull would overwrite a local fix), then re-run 2c. Fixing it is a gated action. Write the corrections to a JSON file and call the POEditor API; ~/.porc defines API_TOKEN:

    bash
    # fixes.json: [{"term":"<key>","context":"","translation":{"content":"<corrected text>"}}]
    source ~/.porc
    curl -s -X POST https://api.poeditor.com/v2/translations/update \
      -d api_token="$API_TOKEN" -d id=125447 -d language=<lang> --data-urlencode data@fixes.json
    # expect: "translations":{"parsed":N,"updated":N}

    After re-running 2c, grep the language file to confirm the corrected values arrived.

    Then commit:

    bash
    git add v3/src/utilities/translation/lang/
    git commit -m "Update translations from POEditor"

    Zero-width space handling: POEditor treats truly empty strings as "untranslated," so the scripts convert between empty strings and zero-width spaces (\u200b) at the boundary:

    • Push (strings-push.sh): "" → "\u200b" before uploading
    • Pull (strings-pull.sh): "\u200b" → "" after downloading

    The source file (en-US.json5) and all runtime language files use "" for intentionally blank strings — zero-width spaces should never appear in the repository.

  3. Update package.json version:

    bash
    cd v3
    npm version --no-git-tag-version {version}

    IMPORTANT: Use the npm version command - do NOT manually edit package.json. The npm command updates both package.json AND package-lock.json.

  4. Update versions.md:

    Add new row at top of versions table (using release date from Phase 1):

    markdown
    | [{version}](https://codap3.concord.org/version/{version}/) | Month Day, Year |
  5. Update CHANGELOG.md:

    • Insert content from Phase 2 at top (after # Changelog heading)
    • Asset Sizes section added in Phase 4
  6. Stage version files:

    bash
    git add v3/package.json v3/package-lock.json v3/versions.md v3/CHANGELOG.md

Phase 4: Create Release PR

Goal: Build, capture asset sizes, commit, and create PR.

Steps
  1. Run build:

    bash
    cd v3 && npm run build
  2. Get asset sizes:

    bash
    ls -la v3/dist/assets
    • Find main.*.css file, get its size
    • Find all index.*.js files, use the largest one
    • Strip hashes for display: index.f6eac39a783c91ae9ea5.js → index.js
  3. Calculate % change:

    • Read previous sizes from top entry in CHANGELOG.md
    • Calculate: ((new - old) / old) * 100
    • Format: X.XX%, <0.01% for very small increases, negative for decreases (e.g., -0.50%)
  4. Add Asset Sizes to CHANGELOG:

    markdown
    ### Asset Sizes
    |      File |          Size | % Change from Previous Release |
    |-----------|---------------|--------------------------------|
    |  main.css |  XXXXXX bytes |                          X.XX% |
    |  index.js | XXXXXXX bytes |                          X.XX% |
  5. Commit and push:

    bash
    git add v3/CHANGELOG.md
    git commit -m "Release {version}"
    git push -u origin release-{version}

    Note: Only commit the version files (package.json, package-lock.json, versions.md, CHANGELOG.md). Do not commit the dist/ build output.

    The push and the PR creation (step 6) are gated. Show the branch, the PR title, and the full PR body, and get one approval covering both.

  6. Create PR with labels:

    bash
    gh pr create \
      --title "Release {version}" \
      --body "{release_notes_from_phase_2}" \
      --label "v3" \
      --label "run regression"
  7. Inform user:

    PR created: {url}

    CI is running. The run regression label triggers the full Cypress test suite.

    After CI passes and PR is reviewed/merged, run /codap-v3-build tag to continue.

  8. If CI fails or stalls, check for a GitHub outage before suspecting the release. During the 3.1.1 release, a GitHub Actions incident made jobs wait for runners that never came: they ran no steps and were cancelled exactly 15 minutes after being queued, while jobs that did get runners passed slowly. That pattern means the infrastructure, not the code:

    bash
    gh run view <id> --json jobs \
      --jq '.jobs[] | "\(.name)\t\(.conclusion)\t\(.startedAt) -> \(.completedAt)\t\(.steps|length) steps"'
    curl -s https://www.githubstatus.com/api/v2/incidents/unresolved.json \
      | python3 -c "import json,sys; [print(i['name'], i['status'], i['created_at']) for i in json.load(sys.stdin)['incidents']]"

    If an Actions incident is open, poll the status API in the background (e.g. every 3 minutes) until it clears, then re-run the affected runs (gh run rerun <id>, or --failed for only the cancelled jobs) and watch them with gh run watch <id> --exit-status.

Phase 5: Tag

Goal: After PR merge, create the git tag that triggers the S3 build.

Do NOT create the GitHub release here. Publishing the GitHub release at tag time confused external users: they saw a published release for a version that was not yet available in production (staging QA can take 1+ days). The GitHub release is created later, in Phase 6, only after the build is live in production. The tag still must be pushed now, because the tag push is what triggers the CI build that deploys to S3 (needed for staging).

Prerequisite: Release PR must be merged, and the automatic "Increment the build number" commit that follows the merge must have landed on main (see step 2).

Steps
  1. Checkout main and pull:

    bash
    git checkout main
    git pull
  2. Verify the build-number increment commit has landed — do NOT skip this:

    Merging the release PR triggers an automation that pushes an "Increment the build number" commit to main. That increment commit is the one to tag — not the Release {version} merge commit.

    bash
    git log --oneline -2

    The output must show the increment commit sitting on top of the release merge:

    f999f1445 Increment the build number     <- tag THIS one (HEAD)
    936451ef5 Release {version} (#NNNN)

    If HEAD is still the Release {version} merge commit, the automation has not pushed yet. Wait, re-run git pull, and check again until the increment commit appears.

    Why this matters: tagging immediately after the merge, before the increment lands, points the tag at the release merge commit instead. This has happened before. The resulting build carries the wrong build number, and undoing it means deleting the tag and its S3 deploy. Every correct release tag (3.0.0 through 3.0.3) points at an "Increment the build number" commit — use that as your check.

  3. Create the annotated tag, confirm its target, then push it. Create it locally and check that it is on the increment commit:

    bash
    git tag -a {version} -m "Version {version}"
    git log -1 --format='%h %s' {version}
    # expected: <sha> Increment the build number

    The push is gated. Show the tag, its commit, and the command, then:

    bash
    git push origin {version}
    git ls-remote --tags origin '{version}^{}'   # the commit it points at; must match
    git rev-list -n1 {version}

    The tag push triggers a CI build that deploys to S3. The GitHub release is not created until after the production deploy (Phase 6).

  4. Watch the tag's CI run until the S3 deploy finishes. The tag push starts the "Continuous Integration (CODAP v3)" workflow (v3.yml), whose S3 Deploy job publishes version/{version}/. Find the run for the tag's commit; it can take a few seconds to appear, so retry if the list is empty:

    bash
    gh run list --workflow v3.yml --branch {version} \
      --json databaseId,headSha,status,createdAt
    git rev-list -n1 {version}   # the run's headSha must match this
    gh run watch <id> --exit-status

    Then confirm the version folder is served:

    bash
    curl -s -o /dev/null -w '%{http_code}\n' https://codap3.concord.org/version/{version}/
    # expected: 200

    Do not trigger the staging workflow until the run has succeeded and the folder returns 200. The staging workflow copies version/{version}/index-top.html, so it fails if the S3 deploy hasn't finished. If the run fails, stop and show the user the failed job (gh run view <id> --log-failed).

  5. Inform user:

    Tag pushed and deployed to S3: https://codap3.concord.org/version/{version}/ (The GitHub release will be created later, after the production deploy, so external users don't see a release for a version that isn't live yet.)

    Ready to deploy to staging? (Or run /codap-v3-build deploy {version} later.)

Show full SKILL.md (3,110 more words)Show less

Phase 6: Deploy

Goal: Stage, test, deploy to production and beta, publish the GitHub release, finalize Jira, and announce.

Steps
  1. Deploy to staging (gated) — dispatch, watch, and verify release-v3-staging.yml. Expect version/{version}/ on both /index-staging.html and /staging.

    Staging deployed and verified. Test at: https://codap3.concord.org/index-staging.html

  2. Post the release announcement to #codap-v3 (gated), following Slack Posts: preview in the self-DM, get approval, post, and record the message's ts for step 7.

    Announcement format (standard markdown, same items, titles, and order as CHANGELOG.md):

    markdown
    CODAP {version} is available for testing at https://codap3.concord.org/staging.
    
    ### ✨ Features & Improvements:
    - **[CODAP-XXX](https://concord-consortium.atlassian.net/browse/CODAP-XXX):** Feature title here
    - **[CODAP-YYY](https://concord-consortium.atlassian.net/browse/CODAP-YYY):** Another feature
    
    ### 🐞 Bug Fixes:
    - **[CODAP-AAA](https://concord-consortium.atlassian.net/browse/CODAP-AAA):** Bug fix title
    
    ### 🛠️ Under the Hood:
    - **[CODAP-ZZZ](https://concord-consortium.atlassian.net/browse/CODAP-ZZZ):** Internal change
    
    The [beta](https://codap3.concord.org/beta) and [production](https://codap3.concord.org/) URLs will be updated once the staging build passes QA.

    Rules:

    • Include only the sections that have items.
    • Every item starts with - , even when a section has only one item. Slack collapses consecutive non-list lines into one paragraph. The self-DM preview shows whether this went wrong.
    • Every Jira key is a link, never a bare CODAP-XXX. A bare key makes the Jira bot post a preview card for each item in the channel.
    • Items without a Jira key keep their plain **Title** form.
  3. Wait for external QA (may take 1+ days).

    Let me know when staging QA is complete and we can proceed with the production deployment.

    If QA finds a show-stopper, switch to Staging QA Failure.

  4. Deploy to production (gated) — dispatch, watch, and verify release-v3-production.yml. Expect version/{version}/ on /.

  5. Deploy to beta (gated) — dispatch, watch, and verify release-v3-beta.yml. Expect version/{version}/ on /beta.

  6. Publish the GitHub release (gated). Do this only after step 4 has verified that production serves {version}, so external users never see a published release for a version that isn't live yet (staging QA can take 1+ days). Write the Phase 2 release notes to a file in the scratchpad and pass it with --notes-file:

    bash
    gh release create {version} --title "Version {version}" --notes-file <scratchpad>/notes.md
    gh release view {version} --json url,isDraft,isPrerelease
    gh release list --limit 1    # {version} should be marked Latest
  7. Announce that production is live (gated) as a reply in the announcement's thread (thread_ts = the ts recorded in step 2), following Slack Posts:

    markdown
    CODAP {version} is now live on [production](https://codap3.concord.org/) and [beta](https://codap3.concord.org/beta). [GitHub release notes](<GitHub release URL from step 6>)

    Say "GitHub release notes", not "Release notes": there is a separate, user-facing release notes document, and the two shouldn't be confused.

    If the session was resumed and the ts is no longer known, find the announcement with mcp__slack__conversations_history on #codap-v3 (text starting CODAP {version} is available for testing) and confirm with the user that it's the right message.

  8. Check unresolved issues on the fix version. Have a subagent run:

    project = CODAP AND fixVersion = "{version}" AND statusCategory != Done ORDER BY key

    and report key, summary, status, assignee, and Project Team Approver. Show the list grouped by status. Stories in "In Project Team Review" are normal at this point; anything earlier (In Progress, In Code Review, Ready for Merge) suggests the story isn't actually in the build. The Release {version} tracking issue appears here too, with "Automation for Jira" as its Project Team Approver; that is expected. Ask the user whether to nudge the owners (a Slack message to anyone else is gated), to move a story's Fix Version (a gated Jira edit), or to release as is.

  9. Mark the Jira version released. The Atlassian MCP tools have no version-management tool, so the user does this in the Jira UI: CODAPv3 → Releases → {version} → Release, with the release date agreed in Phase 1. Wait for the user to confirm.

  10. Go through the Done when checklist before calling the release finished.

Done when

Check every item and report each one's status. The release is not finished until all of them are true:

  • / and /beta serve version/{version}/ (re-run the curl checks)
  • The GitHub release {version} is published, not a draft or pre-release, and marked Latest
  • The Jira version {version} is marked Released
  • Unresolved issues on the version were reviewed with the user (step 8)
  • The staging announcement is in #codap-v3 and the production-live reply is in its thread
Manual Completion Instructions

If you prefer to complete deployment outside of Claude Code, run these in order. The GitHub release must not be published until the production deploy has succeeded.

bash
gh workflow run release-v3-production.yml -f version={version}
gh workflow run release-v3-beta.yml -f version={version}
gh release create {version} --title "Version {version}" --notes "{release_notes_from_phase_2}"

The workflows can also be run from the GitHub UI: production, beta. Then mark the Jira version released (CODAPv3 → Releases → {version} → Release).

Resume Later

To complete deployment in Claude Code after QA:

/codap-v3-build deploy {version}

On resume, establish where things stand before acting: which pages serve {version} (the curl checks), whether gh release view {version} finds a release, and whether the #codap-v3 announcement exists. Then continue from the first step that isn't done.

Staging QA Failure — Revised Release

Trigger: A show-stopper bug is found during Phase 6 staging QA, and a fix has been merged to main.

Invocation: /codap-v3-build fix {old-version} (e.g., /codap-v3-build fix 3.0.5)

When invoked, introduce the situation:

A bug was found during staging QA for {old-version} and a fix has been merged. This workflow will create a revised release. In the pre-release phase that means a new version number; in the production phase the version stays the same and only the build number and tag move (see Step 1.3).

I'll walk you through:

  1. Determine the new version number and release date
  2. Decide whether release notes need updating
  3. Update version files
  4. Build and create a new release PR
  5. Clean up the old tag/release and create new ones
  6. Update Jira and re-deploy to staging
Step 1: Gather Context
  1. Ensure on main with latest:

    bash
    git checkout main
    git pull
  2. Get current build number and verify the fix is present:

    bash
    cat v3/build_number.json
    git log --oneline {old-version}..HEAD

    Confirm with the user that the expected fix commit(s) appear in the log.

  3. Determine the new version number — this is phase-dependent (see Phase 1, step 6, for how to identify the phase):

    PhaseRevised release version
    Production releaseThe version does not change. {old-version} is reused as-is. Only the build number changes (it increments when the revised release PR merges), and the build number is not part of the version. The respin is reflected in the CHANGELOG, not the version string.
    Pre-release developmentThe version does change, because the build number is part of it. Current build number is N; the release PR increments it once more on merge → new version is N + 1, matching {old-version}'s prefix. Example: build 2804 → 3.0.0-beta.2805.

    The production-phase rule reshapes this whole workflow. With the version unchanged there is no "old vs new version" to reconcile: versions.md needs no edit, the Jira release needs no rename, and npm version is a no-op. What still must happen is re-tagging — delete the {version} tag and recreate it on the new increment commit (Step 5) — plus any CHANGELOG corrections and a fresh staging deploy. Read the steps below with that in mind and skip the version-migration parts; they apply only in the pre-release phase.

    The steps below are written in {old-version} → {new-version} terms, which collapses in the production phase — the two are the same string. Read every {new-version} as {version}.

    This creates a branch-name collision in the commands below. They create and push release-{new-version}, which in the production phase is release-{version} — the branch the original release already used, still present locally and on the remote. Choose a distinct respin branch name (e.g. release-{version}-fix) and substitute it for release-{new-version} everywhere it appears below. This note calls that name {respin-branch}; in the pre-release phase {respin-branch} is just release-{new-version} (no collision, since the version is new). Do not reuse or force-push the original release branch.

    This production-phase path has not yet been exercised as of 3.0.5. Confirm the approach with the user before running it rather than assuming these notes are complete.

  4. Confirm release date:

    • The original release date (from Phase 1) may no longer be appropriate if QA and the fix took multiple days.
    • Show the original release date and today's date.
    • Ask the user to confirm or update the release date.
    • This date is used in CHANGELOG.md and the Jira release — plus versions.md in the pre-release phase, where the row's version string changes. In the production phase the versions.md row already carries the right version, so it needs an edit only if the date itself changed.
  5. Confirm with user — use the wording for the current phase:

    Production phase (version unchanged — do not present this as a version change):

    The fix is on main. The version stays {version}; the respin changes only the build number ({old-build} → {new-build}) and moves the {version} tag to the new increment commit. Release date: {release-date}

    Does this look correct?

    Pre-release phase (version changes):

    The fix is on main. New version will be {new-version} (old was {old-version}). Release date: {release-date}

    Does this look correct?

Step 2: Release Notes Decision

Ask the user:

Do the release notes need to be updated?

  • No changes needed — The bug was introduced in this release cycle, so users never saw it
  • Add the fix — The bug existed in a prior release and the fix should be documented

If no changes needed:

  • The existing CHANGELOG content will be reused with only the version number and date updated in the header.

If release notes need updating:

  • Walk through the new fix item(s) using the same interactive process as Phase 2, step 5 (present title options, ask for section and title).
  • Insert the new item(s) into the appropriate section(s) of the existing release notes, maintaining numeric Jira ID order.
  • Present the updated CHANGELOG entry for approval.
  • Update Jira Fix Versions for any newly added stories.
Step 3: Create Release Branch and Update Files

Follow the same working directory rules as Phase 3.

  1. Create release branch ({respin-branch} — see the collision note in Step 1.3; in the pre-release phase this is release-{new-version}):

    bash
    git checkout -b {respin-branch}
  2. Sync translations:

    • Always pull translations from POEditor (Phase 3, step 2c) — every build must pull, since translators may have added or updated non-English strings even when the English strings are unchanged. Do not skip this.
    • Only push English strings (Phase 3, steps 2a–2b) if the bug fix introduced new or changed translatable strings. For most bug fixes there are none, so the push is skipped — but the pull still runs.
  3. Update package.json:

    bash
    cd v3
    npm version --no-git-tag-version {new-version}
  4. Update versions.md:

    • Replace the {old-version} row with the {new-version} row (using the confirmed release date)
    • Do NOT add a second row — this is a revision, not a separate release
  5. Update CHANGELOG.md:

    • Replace the ## Version {old-version} header with ## Version {new-version}, using the confirmed release date
    • If release notes content changed (Step 2), update the content as well
    • The Asset Sizes section will be updated after the build (Step 4)
  6. Commit version file changes:

    bash
    cd /path/to/codap
    git add v3/package.json v3/package-lock.json v3/versions.md v3/CHANGELOG.md
    git commit -m "Release {new-version}"
Step 4: Build, Asset Sizes, and Release PR

Follow the same process as Phase 4:

  1. Build:

    bash
    cd v3 && npm run build
  2. Update asset sizes in CHANGELOG.md (same process as Phase 4, steps 2–4).

    • Compare against the previous release before {old-version} for % change (since {old-version} is being replaced, not used as baseline).
  3. Commit, push, and create PR:

    bash
    cd /path/to/codap
    git add v3/CHANGELOG.md
    git commit --amend --no-edit
    git push -u origin {respin-branch}
    gh pr create \
      --title "Release {new-version}" \
      --body "{release_notes}" \
      --label "v3" \
      --label "run regression"

    ({respin-branch} is the distinct respin branch from Step 1.3; in the pre-release phase it is release-{new-version}. The PR title still uses {new-version}, which equals {version} in the production phase.)

  4. Inform user:

    PR created: {url}

    After CI passes and PR is merged, I'll clean up the old release and create the new one.

Step 5: After PR Merge — Clean Up and Re-tag

Prerequisite: Release PR must be merged, and the automatic "Increment the build number" commit that follows the merge must have landed on main.

  1. Checkout main, pull, and wait for the increment commit:

    bash
    git checkout main
    git pull
    git log --oneline -2

    As in Phase 5, the new tag must point at the "Increment the build number" commit that the merge automation pushes after the release merge — not at the Release {new-version} merge commit itself. If HEAD is still the release merge, wait, git pull again, and re-check until the increment commit appears.

  2. Delete the old tag (gated; get one approval covering this deletion and the push in step 3, and show both tags and the commit the new one will point at):

    bash
    git push origin --delete {old-version}
    git tag -d {old-version}

    The tag is safe to delete because it points to a known-buggy build that was never deployed to production or beta and that no external consumer depends on.

    No GitHub release to delete: under the current flow the GitHub release is only created after a production deploy (Phase 6). Since {old-version} failed staging QA, it never reached production and never had a release published. (If one somehow exists, remove it with gh release delete {old-version} --yes.)

  3. Create the new tag:

    bash
    git tag -a {new-version} -m "Version {new-version}"
    git log -1 --format='%h %s' {new-version}
    # expected: <sha> Increment the build number
    git push origin {new-version}
    git ls-remote --tags origin '{new-version}^{}'   # must match git rev-list -n1 {new-version}

    As in Phase 5, do not create the GitHub release here. The tag push triggers the S3 build; the GitHub release for {new-version} is published only after the revised build reaches production (Step 6 / the deploy phase).

  4. Delete the merged release branch(es) (optional cleanup):

    Pre-release phase — the buggy release's branch:

    bash
    git push origin --delete release-{old-version}
    git branch -d release-{old-version}

    Production phase — release-{old-version} is release-{version}, the original release's branch (already merged for the first attempt), and {respin-branch} is the branch this workflow just merged. Both are now merged and can be removed; the respin branch is the one that would otherwise dangle:

    bash
    git push origin --delete {respin-branch} && git branch -d {respin-branch}
    # optionally also remove the original release branch if it still exists:
    git push origin --delete release-{version} 2>/dev/null; git branch -d release-{version} 2>/dev/null || true
  5. Watch the tag's CI run until the S3 deploy finishes, exactly as in Phase 5, step 4, with {new-version}. Do not trigger the staging workflow until the run has succeeded and https://codap3.concord.org/version/{new-version}/ returns 200.

Step 6: Update Jira and Re-deploy
  1. Update Jira:

    • Pre-release phase only: the Jira release must be renamed from {old-version} to {new-version}. The Atlassian MCP tools can't edit versions, so ask the user to do it in the Jira UI (CODAPv3 → Releases).
    • If the release date changed, ask the user to update it in the same place.
    • If new stories were added to release notes (Step 2), update their Fix Versions. This is a gated Jira edit; delegate it to a subagent (same pattern as Phase 2, step 9).
  2. Re-deploy to staging (gated) — dispatch, watch, and verify release-v3-staging.yml with {new-version}.

  3. Post the updated announcement (gated) as a new top-level message in #codap-v3, following Slack Posts. Use the same format and rules as Phase 6, step 2 (linked Jira keys, - on every item), with a line noting the revised build. Record the new message's ts; the production-live reply (Phase 6, step 7) goes in this message's thread.

    markdown
    CODAP {new-version} is available for testing at https://codap3.concord.org/staging.
    (Revised build — replaces {old-version} which had a staging QA issue.)
    
    ### ✨ Features & Improvements:
    - **[CODAP-XXX](https://concord-consortium.atlassian.net/browse/CODAP-XXX):** Feature title here
    
    ### 🐞 Bug Fixes:
    - **[CODAP-AAA](https://concord-consortium.atlassian.net/browse/CODAP-AAA):** Bug fix title
    
    The [beta](https://codap3.concord.org/beta) and [production](https://codap3.concord.org/) URLs will be updated once the staging build passes QA.

    In the production phase {new-version} and {old-version} are the same string, so say "revised build of {version}" instead.

  4. Inform user:

    Revised release {new-version} deployed to staging.

    Test at: https://codap3.concord.org/index-staging.html

    When staging QA passes, run /codap-v3-build deploy {new-version} to continue with production deployment.

Dispatch, Watch, Verify

Use this for every staging, production, and beta deploy. The dispatch itself is gated.

gh workflow run returns before the new run exists, so gh run list --limit 1 can return the previous run and report a false success. Pick the run created after the dispatch instead:

bash
date -u +%Y-%m-%dT%H:%M:%SZ          # note this as <dispatched>
gh workflow run <workflow>.yml -f version={version}
gh run list --workflow <workflow>.yml --event workflow_dispatch \
  --json databaseId,createdAt \
  --jq '.[] | select(.createdAt >= "<dispatched>") | .databaseId'

If no id appears yet, re-run the gh run list with the same <dispatched> value. If more than one appears, stop and ask. Then:

bash
gh run watch <id> --exit-status

A non-zero exit means the deploy failed: stop and show the user gh run view <id> --log-failed.

Verify what is served. A successful run is not proof the page changed. Check the page references the new version folder:

bash
for p in index-staging.html staging "" beta; do
  printf '%-20s ' "/$p"
  curl -s "https://codap3.concord.org/$p" | grep -oE 'version/[^/"]+/' | sort -u | tr '\n' ' '
  echo
done

Each workflow updates only its own pages (staging: /index-staging.html and /staging; production: /; beta: /beta), so only those are expected to change. The pages are served with cache-control: no-cache, so the new version shows as soon as the run finishes.

Slack Posts

Every message to #codap-v3 (or anyone other than the developer) goes through these steps:

  1. Find the developer's self-DM. Call mcp__slack__channels_me with channel_types: "im"; the self-DM is the row named after the developer's own handle ("DM with <developer's name>").
  2. Post the exact message to the self-DM with mcp__slack__conversations_add_message (content_type: text/markdown). This isn't gated. The preview renders exactly as the channel will, which shows collapsed bullets, broken links, or stray formatting before anyone else sees them.
  3. Ask the user to check the preview and approve posting it to the channel. Claude can't edit or delete a message once it's posted, so any fix happens here.
  4. Post the same text to the channel (channel_id: #codap-v3, plus thread_ts for a thread reply). The result names the channel's ID (C…) and the message's ts; record and report both. A release spans days and often sessions, so also save them where a later session will find them (e.g. Claude's memory for this project), for the production-live thread reply.

If the Slack MCP server isn't available, show the user the draft and ask them to paste it into Slack themselves.

File Locations

FilePurpose
v3/build_number.jsonCurrent build number. Part of the version string only in the pre-release phase (Phase 1, step 6). In the production phase it is independent of the version, but its auto-increment commit is always the tag target (Phase 5).
v3/package.jsonVersion field
v3/versions.mdVersion history table
v3/CHANGELOG.mdRelease notes
v3/dist/assets/Built assets (after npm run build)
v3/src/utilities/translation/lang/en-US.json5All English strings (DG + V3, JSON5, source of truth)

Jira Integration

Use these constants for all Atlassian MCP tool calls:

ConstantValue
cloudIdconcord-consortium.atlassian.net
projectKeyCODAP

Note: The Atlassian MCP tools accept either a UUID cloud ID or a site URL for the cloudId parameter. The site URL format is used here for readability.

  • Use Atlassian MCP tools for all Jira operations
  • Stories tagged via Fix versions field
  • Release marked Released after production deploy

© concord-consortium, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/codap-v3-build of concord-consortium/codap.

Open the folder on GitHubat commit 4bcb0ff

Compare with similar skills

Codap V3 Build next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Codap V3 Build compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Codap V3 Build this skillconcord-consortium/codap106—~15kAutomated safety check: PassMIT
SDK Changelogbouffalolab/bouffalo_sdk501—~1.1kAutomated safety check: PassApache-2.0
Create Epic RecapDataDog/datadog-agent3.8k—~5kAutomated safety check: NotesApache-2.0
Review Release Noteschef/chef-web-docs143—~5.2kAutomated safety check: PassCustom licence
Changelog To JiraDevolutions/devolutions-gateway162—~2.7kAutomated safety check: PassApache-2.0
Prepare ReleaseDevolutions/devolutions-gateway162—~1.8kAutomated safety check: PassApache-2.0

Similar skills

  • SDK Changelog

    bouffalolab/bouffalo_sdk

    A skill your agent uses when generating a customer-facing CHANGELOG between two SDK release tags.

    501 GitHub stars~1.1k tokensUpdated 5 days ago
    DevelopmentAuto-check passed
  • Create Epic Recap

    DataDog/datadog-agent

    Official

    A skill your agent uses when an engineer or manager asks to recap, summarize, or post an update on a Jira Epic — a progress update for an in-progress Epic (how far along it is, what's shipped so…

    3.8k GitHub stars~5k tokensUpdated today
    DevelopmentAuto-check: notes
  • Review Release Notes

    chef/chef-web-docs

    Read a release notes file and edit it using Jira release data and GitHub pull requests as co-equal, optional sources.

    143 GitHub stars~5.2k tokensUpdated today
    DevelopmentAuto-check passed
  • Changelog To Jira

    Devolutions/devolutions-gateway

    Creates missing DGW Jira tickets from CHANGELOG.md entries and updates the file with the new ticket links.

    162 GitHub stars~2.7k tokensUpdated today
    DevelopmentAuto-check passed
  • Prepare Release

    Devolutions/devolutions-gateway

    Prepares a Devolutions Gateway / Devolutions Agent release commit: bumps the version, generates and inserts the changelog, creates Jira tickets, generates the ToolBox changelog, and produces the…

    162 GitHub stars~1.8k tokensUpdated today
    DevelopmentAuto-check passed
  • Generate Release Notes

    huytieu/COG-second-brain

    Generate categorized release notes from any source (GitHub, Linear, Jira, or manual input) with optional publishing

    1.3k GitHub stars~2.5k tokensUpdated 6 days ago
    DevelopmentAuto-check passed

More from concord-consortium/codap

  • Codap Resources

    concord-consortium/codap

    A skill your agent uses when deploying, updating, or syncing assets to the codap-resources S3 bucket - plugins, example documents, boundary files, banners, or notification configs

    106 GitHub stars~4.4k tokensUpdated yesterday
    Auto-check passed
  • Generate Log Events CSV

    concord-consortium/codap

    A skill your agent uses when regenerating, updating, or auditing the CODAP v3 log-events dictionary CSV (the list of every log event the v3 app can emit, with placeholders/parameters/descriptions).

    106 GitHub stars~1.4k tokensUpdated yesterday
    Auto-check passed

Works with

Categories

Questions about Codap V3 Build

What does Codap V3 Build do?

A skill your agent uses when preparing a CODAP v3 release, creating release notes, updating version files, creating release PRs, tagging releases, or deploying to staging/production. Codap V3 Build is an agent skill from concord-consortium/codap. Use when preparing a CODAP v3 release, creating release notes, updating version files, creating release PRs, tagging releases, or deploying to staging/production.

When should I use Codap V3 Build?

Codap V3 Build fits situations like: preparing a CODAP v3 release; creating release notes; updating version files; creating release PRs.

How do I install Codap V3 Build in Claude Code?

Run `npx skills add concord-consortium/codap --skill codap-v3-build -a claude-code`. Or copy the skill folder (.claude/skills/codap-v3-build in concord-consortium/codap) into .claude/skills/codap-v3-build in your project. Claude Code loads it when a task matches its description.

How do I install Codap V3 Build in Codex?

Run `npx skills add concord-consortium/codap --skill codap-v3-build -a codex`. Or copy the skill folder (.claude/skills/codap-v3-build in concord-consortium/codap) into .agents/skills/codap-v3-build in your project. Codex loads it when a task matches its description.

Can I use Codap V3 Build in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add concord-consortium/codap --skill codap-v3-build -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/codap-v3-build, .gemini/skills/codap-v3-build, .github/skills/codap-v3-build and .opencode/skills/codap-v3-build in your project.

What does Codap V3 Build need to run?

Going by SKILL.md and its folder, Codap V3 Build needs the command-line tools its instructions call (git, gh, npm, curl, node and python3) and credentials named API_TOKEN and POEDITOR_API_TOKEN.

Does Codap V3 Build access the network?

SKILL.md names 4 domains. In commands or code: codap3.concord.org, concord-consortium.atlassian.net, api.poeditor.com and githubstatus.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.

Is Codap V3 Build safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Codap V3 Build use?

Codap V3 Build is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Codap V3 Build use?

About 15k tokens (SKILL.md is roughly 59k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Codap V3 Build?

Skills that share tags, products or a category with Codap V3 Build: SDK Changelog (bouffalolab/bouffalo_sdk, 501 stars), Create Epic Recap (DataDog/datadog-agent, 3.8k stars), Review Release Notes (chef/chef-web-docs, 143 stars) and Changelog To Jira (Devolutions/devolutions-gateway, 162 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Codap V3 Build?

concord-consortium (a GitHub organization) maintains it in concord-consortium/codap, which has 106 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on October 7, 2026.

Source: concord-consortium/codap on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.