From 77e1c630b787c73f4d7c46578d46342b228e8a1e Mon Sep 17 00:00:00 2001 From: Kirk Swenson Date: Sat, 14 Feb 2026 00:18:32 -0800 Subject: [PATCH 1/5] CODAP-1112: add ownership-aware string handling to V2 build skill Update Phase 2 Step 4 to categorize string differences by ownership (DG.* vs V3.*), accept V3 changes from POEditor locally, and filter push to only send DG-owned keys. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/codap-v2-build/SKILL.md | 77 +++++++++++++++++++++----- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/.claude/skills/codap-v2-build/SKILL.md b/.claude/skills/codap-v2-build/SKILL.md index 8fb5741ea5..ed711054d1 100644 --- a/.claude/skills/codap-v2-build/SKILL.md +++ b/.claude/skills/codap-v2-build/SKILL.md @@ -257,12 +257,14 @@ Wait for user confirmation before starting Phase 1. Explain to the user: > CODAP supports 17 languages via POEditor (poeditor.com). POEditor is the single > source of truth for translations — the push from git to POEditor is additive only - > (it can add new terms and update existing values, but never deletes terms). This is - > important because CODAP V2 and V3 share the same POEditor project, so each must be - > able to push without affecting the other's strings. + > (it can add new terms and update existing values, but never deletes terms). CODAP V2 + > and V3 share the same POEditor project (ID 125447), with an ownership model: + > - **V2 owns `DG.*` strings** — V2 pushes only these keys + > - **V3 owns `V3.*` strings** — V3 pushes only these keys > > Before pushing, we'll compare the local English strings against what's currently in - > POEditor so you can see what will be added or changed. + > POEditor, categorize differences by ownership, accept any V3 changes from POEditor, + > and push only DG changes. **Constants:** - POEditor project ID: `125447` @@ -300,25 +302,74 @@ Wait for user confirmation before starting Phase 1. Clean up temp files and skip to step 4b. - **If different:** Analyze the differences using the two normalized JSON files: + **If different:** Categorize differences by ownership: + ```bash + # Categorize differences by ownership + node -e " + const local = require('/tmp/local-en.json'); + const remote = require('/tmp/poeditor-en.json'); + const dgDiffs = [], v3Diffs = []; + for (const k of new Set([...Object.keys(local), ...Object.keys(remote)])) { + if (local[k] !== remote[k]) { + (k.startsWith('V3.') ? v3Diffs : dgDiffs).push(k); + } + } + if (v3Diffs.length) console.log('V3 changes from POEditor (will be accepted):', v3Diffs.join(', ')); + if (dgDiffs.length) console.log('DG changes to push:', dgDiffs.join(', ')); + " + ``` + + Analyze and present differences in two groups: + + **DG.\* differences** (V2-owned): - **Keys in local but not in POEditor** — will be ADDED as new terms - **Keys in POEditor but not in local** — will be LEFT ALONE (the push is additive only) - **Keys with different values** — English text will be UPDATED in POEditor + **V3.\* differences** (V3-owned): + - Inform user: "V3 made these string changes in POEditor: [list]. These will be + accepted into your local file." + - Accept V3 changes by updating the local `lang/strings/en-US.json` with the + POEditor values for V3.* keys. Use the Read tool to get the current file content, + then use the Edit tool to update each changed V3.* value in place. Preserve any + JSON comments in the file. + Present a summary, e.g.: > **English strings: local vs. POEditor** - > - 3 new terms to add + > - **DG.\* (V2-owned):** 3 new terms to add, 2 terms with changed values + > - **V3.\* (V3-owned):** 4 changes from POEditor (will be accepted locally) > - 5 terms only in POEditor (will not be affected) - > - 2 terms with changed values > - > [show the specific additions and changes] - - Use AskUserQuestion: "Your local English strings differ from POEditor as shown - above. Do you want to push these changes to POEditor?" - - **Yes, push to POEditor** — Run `./bin/strings-push-project.sh -a "$API_TOKEN"` - and show the API response + > [show the specific additions and changes, grouped by ownership] + + If there are DG.* differences, use AskUserQuestion: "Your local DG.* strings differ + from POEditor as shown above. Do you want to push DG changes to POEditor?" + - **Yes, push DG strings to POEditor** — Filter and push only DG.* keys: + ```bash + # Extract DG-only strings for push (V3 strings are managed by the V3 build) + node -e " + const fs = require('fs'); + const stripComments = require('strip-json-comments'); + const raw = fs.readFileSync('lang/strings/en-US.json', 'utf8'); + const data = JSON.parse(stripComments(raw)); + const dg = {}; + for (const [k, v] of Object.entries(data)) { + if (k.startsWith('DG.')) dg[k] = v; + } + fs.writeFileSync('/tmp/dg-strings-push.json', JSON.stringify(dg)); + console.log('Pushing ' + Object.keys(dg).length + ' DG strings (filtering out ' + + (Object.keys(data).length - Object.keys(dg).length) + ' V3 strings)'); + " + + ./bin/strings-push.sh -p 125447 -i /tmp/dg-strings-push.json -a "$API_TOKEN" + rm -f /tmp/dg-strings-push.json + ``` + Show the API response. - **No, skip the push** — Continue without pushing + If there are no DG.* differences (only V3 changes were accepted), report: + > No DG.* changes to push. V3 changes have been accepted locally. + Clean up temp files: ```bash rm -f /tmp/en-US.json /tmp/poeditor-en.json /tmp/local-en.json From 7938a751ba71c32926dbd14424a6d4a7f454a958 Mon Sep 17 00:00:00 2001 From: Kirk Swenson Date: Tue, 17 Feb 2026 11:58:13 -0800 Subject: [PATCH 2/5] CODAP-1112: address Copilot review feedback on V2 build skill In response to Copilot code review on PR #2390: - Clarify that only DG.* and V3.* prefixes exist in the project - Handle new V3 keys from POEditor (not just updates to existing keys) - Fix "V3 strings" wording to "non-DG strings" for precision Co-Authored-By: Claude Opus 4.6 --- .claude/skills/codap-v2-build/SKILL.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.claude/skills/codap-v2-build/SKILL.md b/.claude/skills/codap-v2-build/SKILL.md index ed711054d1..2f92b4733f 100644 --- a/.claude/skills/codap-v2-build/SKILL.md +++ b/.claude/skills/codap-v2-build/SKILL.md @@ -261,6 +261,7 @@ Wait for user confirmation before starting Phase 1. > and V3 share the same POEditor project (ID 125447), with an ownership model: > - **V2 owns `DG.*` strings** — V2 pushes only these keys > - **V3 owns `V3.*` strings** — V3 pushes only these keys + > - All keys in the project use one of these two prefixes; no other prefixes exist > > Before pushing, we'll compare the local English strings against what's currently in > POEditor, categorize differences by ownership, accept any V3 changes from POEditor, @@ -329,10 +330,15 @@ Wait for user confirmation before starting Phase 1. **V3.\* differences** (V3-owned): - Inform user: "V3 made these string changes in POEditor: [list]. These will be accepted into your local file." - - Accept V3 changes by updating the local `lang/strings/en-US.json` with the - POEditor values for V3.* keys. Use the Read tool to get the current file content, - then use the Edit tool to update each changed V3.* value in place. Preserve any - JSON comments in the file. + - Accept V3 changes by synchronizing the local `lang/strings/en-US.json` with the + POEditor values for V3.* keys: + - For V3.* keys that already exist locally, update their values in place to match + POEditor. + - For V3.* keys that are present only in POEditor (new V3 terms), add those keys + and values to `lang/strings/en-US.json`, placing them near related V3 entries to + preserve the existing JSON ordering and structure. + Use the Read tool to get the current file content, then use the Edit tool to make + these updates/additions while preserving any JSON comments in the file. Present a summary, e.g.: > **English strings: local vs. POEditor** @@ -358,7 +364,7 @@ Wait for user confirmation before starting Phase 1. } fs.writeFileSync('/tmp/dg-strings-push.json', JSON.stringify(dg)); console.log('Pushing ' + Object.keys(dg).length + ' DG strings (filtering out ' + - (Object.keys(data).length - Object.keys(dg).length) + ' V3 strings)'); + (Object.keys(data).length - Object.keys(dg).length) + ' non-DG strings)'); " ./bin/strings-push.sh -p 125447 -i /tmp/dg-strings-push.json -a "$API_TOKEN" From 24bdbcea7ff7a88170f4f0129d14adeda41b68de Mon Sep 17 00:00:00 2001 From: Kirk Swenson Date: Tue, 17 Feb 2026 16:22:38 -0800 Subject: [PATCH 3/5] CODAP-1112: replace hardcoded "17 languages" with "multiple languages" The language count varies between V2 and V3 scripts and may change over time, so avoid specifying a number that can go stale. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/codap-v2-build/SKILL.md | 4 ++-- CLAUDE.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude/skills/codap-v2-build/SKILL.md b/.claude/skills/codap-v2-build/SKILL.md index 2f92b4733f..6f446fb38a 100644 --- a/.claude/skills/codap-v2-build/SKILL.md +++ b/.claude/skills/codap-v2-build/SKILL.md @@ -255,7 +255,7 @@ Wait for user confirmation before starting Phase 1. 4. **Update CODAP translations:** Explain to the user: - > CODAP supports 17 languages via POEditor (poeditor.com). POEditor is the single + > CODAP supports multiple languages via POEditor (poeditor.com). POEditor is the single > source of truth for translations — the push from git to POEditor is additive only > (it can add new terms and update existing values, but never deletes terms). CODAP V2 > and V3 share the same POEditor project (ID 125447), with an ownership model: @@ -573,7 +573,7 @@ Explain to the user: Explain to the user: > This is the main build step. `makeCodapZip` orchestrates the entire release assembly: - > 1. Runs `sproutcore build` to compile the CODAP application (minified, all 17 languages) + > 1. Runs `sproutcore build` to compile the CODAP application (minified, all languages) > 2. Runs `makeExtn` to build standard plugins (from `codap-data-interactives`) and example documents (from `codap-data`) > 3. Generates a top-level `index.html` that detects the user's browser language and redirects to the appropriate localized version > 4. Fixes absolute path references in HTML, JS, and CSS files so the build can run from any URL path diff --git a/CLAUDE.md b/CLAUDE.md index 3bf9333a24..9221ee4a63 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,7 @@ apps/dg/ # Main application ├── react/ # React component wrappers ├── resources/ # Assets, cloud-file-manager integration ├── tests/ # Unit tests -└── *.lproj/ # Localization (17 languages) +└── *.lproj/ # Localization (multiple languages) ``` ### Data Model From fc73215a8628339ca55db7e759a15beb553a09e5 Mon Sep 17 00:00:00 2001 From: Kirk Swenson Date: Tue, 17 Feb 2026 17:23:38 -0800 Subject: [PATCH 4/5] CODAP-1112: add Arabic (ar) to V2 pull script language list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings V2 in sync with V3 — both now pull all 18 POEditor languages with ≥50% translation coverage. Co-Authored-By: Claude Opus 4.6 --- bin/strings-pull-project.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/strings-pull-project.sh b/bin/strings-pull-project.sh index f354648f33..b4e0cee004 100755 --- a/bin/strings-pull-project.sh +++ b/bin/strings-pull-project.sh @@ -7,7 +7,7 @@ # PROJECT_ID=125447 OUTPUT_DIR=lang/strings -LANGUAGES=("de" "el" "es" "fa" "fr" "he" "ja" "ko" "nb" "nl" "nn" "pl" "pt-BR" "th" "tr" "zh-TW" "zh-Hans") +LANGUAGES=("ar" "de" "el" "es" "fa" "fr" "he" "ja" "ko" "nb" "nl" "nn" "pl" "pt-BR" "th" "tr" "zh-TW" "zh-Hans") MAX_RETRIES=3 TIMEOUT_SECS=60 From 3e507c47869c414e27364d697e8bee2c18952bf1 Mon Sep 17 00:00:00 2001 From: Kirk Swenson Date: Tue, 17 Feb 2026 17:31:20 -0800 Subject: [PATCH 5/5] CODAP-1112: use npm ci instead of npm install in V2 build skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm ci is the correct command for reproducible builds — it installs from the lockfile exactly and never modifies package-lock.json. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/codap-v2-build/SKILL.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.claude/skills/codap-v2-build/SKILL.md b/.claude/skills/codap-v2-build/SKILL.md index 6f446fb38a..bf1af35ed8 100644 --- a/.claude/skills/codap-v2-build/SKILL.md +++ b/.claude/skills/codap-v2-build/SKILL.md @@ -157,11 +157,9 @@ Wait for user confirmation before starting Phase 1. - `noaa-codap-plugin` - `story-builder` - If any are missing, offer to run `npm install` (with `--legacy-peer-deps` for codap and CFM). + If any are missing, offer to run `npm ci` (with `--legacy-peer-deps` for codap and CFM). - Even if all `node_modules` directories exist, offer the user the option to re-run `npm install` in all repositories — `package.json` or `package-lock.json` may have changed since dependencies were last installed (e.g. after `git pull`). - - **After running `npm install`, re-verify cleanliness** — `npm install` can modify `package-lock.json` in some repos. Re-check modified file counts and ask the user to commit or discard any changes before proceeding. + Even if all `node_modules` directories exist, offer the user the option to re-run `npm ci` in all repositories — `package.json` or `package-lock.json` may have changed since dependencies were last installed (e.g. after `git pull`). 7. **Summary:**