codymaster 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +285 -0
- package/adapters/antigravity.js +15 -0
- package/adapters/claude-code.js +17 -0
- package/adapters/cursor.js +16 -0
- package/commands/bootstrap.md +49 -0
- package/commands/build.md +48 -0
- package/commands/content.md +48 -0
- package/commands/continuity.md +60 -0
- package/commands/debug.md +51 -0
- package/commands/demo.md +96 -0
- package/commands/deploy.md +51 -0
- package/commands/plan.md +42 -0
- package/commands/review.md +55 -0
- package/commands/track.md +46 -0
- package/commands/ux.md +46 -0
- package/dist/agent-dispatch.js +161 -0
- package/dist/chains/builtin.js +85 -0
- package/dist/continuity.js +385 -0
- package/dist/dashboard.js +926 -0
- package/dist/data.js +122 -0
- package/dist/index.js +2434 -0
- package/dist/judge.js +252 -0
- package/dist/parallel-dispatch.js +359 -0
- package/dist/parallel-quality.js +172 -0
- package/dist/skill-chain.js +258 -0
- package/install.sh +513 -0
- package/package.json +79 -0
- package/skills/.content-factory-state.json +132 -0
- package/skills/.git 2/logs/refs/heads/main +1 -0
- package/skills/.git 2/logs/refs/remotes/origin/main +1 -0
- package/skills/.git 2/objects/02/fb0956734b5f8ba3f918b7defd04a89cfe0076 +0 -0
- package/skills/.git 2/objects/08/1e129d75dc6feac6c02037272e6bd1a04e3324 +0 -0
- package/skills/.git 2/objects/0c/5393416f3c5e01c9a655a802bff0dd52f76f0a +0 -0
- package/skills/.git 2/objects/10/0b9be46978a946a77188f68be725098a122001 +0 -0
- package/skills/.git 2/objects/10/cf041167fc9843610eb3d90259ef3396315fdc +0 -0
- package/skills/.git 2/objects/12/5e19538dd6e1338ffe74f6c4c165b00435bf48 +0 -0
- package/skills/.git 2/objects/16/a9b9d0088d5c1347628b45a2620b479d8ad57c +0 -0
- package/skills/.git 2/objects/17/8c2a9ef93c33ae4eec9d58e82321f9229843a1 +0 -0
- package/skills/.git 2/objects/25/397ae41d09104d763bdcac2695209d85cdea89 +0 -0
- package/skills/.git 2/objects/2f/a836b7947f2d458e1f639788bf4bb0983a3305 +0 -0
- package/skills/.git 2/objects/3a/baaaf0a1c0909c0828335791557125fba911e0 +0 -0
- package/skills/.git 2/objects/42/2924221b81f5ce3c4e4daac9a64a24f9b01f9a +0 -0
- package/skills/.git 2/objects/42/ec0ce707447dc11446a34c9995fb8533801731 +0 -0
- package/skills/.git 2/objects/46/e43ce92866d56ce74b1d750db307cfe6154a15 +0 -0
- package/skills/.git 2/objects/48/5e41b633c63f55b8277bcc59f44f67681f671a +0 -0
- package/skills/.git 2/objects/49/49c596a3a89fa240642acd95dd3258e261eb09 +0 -0
- package/skills/.git 2/objects/50/9d42d8412ef8eaf7f7e138476bac2e4d10ce60 +0 -0
- package/skills/.git 2/objects/55/0c8c389d981b463ef849aeb792d8be3ccb6ec8 +0 -0
- package/skills/.git 2/objects/5d/82d3b18410cdda3ace3677436f0cb599dbe2d2 +0 -0
- package/skills/.git 2/objects/60/0617c58e871a38b33bf29e282d132bb3c381ad +0 -0
- package/skills/.git 2/objects/6a/8369a99c687b7245c92ffaf0e0f0dab9014504 +0 -0
- package/skills/.git 2/objects/79/bea435d40ab531c1aaf6be0432c6a5b7aaed21 +0 -0
- package/skills/.git 2/objects/7e/5ebd79251c2f14e4aceb86c74b6b6daae6b500 +0 -0
- package/skills/.git 2/objects/81/98a822a60178d6d5023ddb3e222cddf048742e +0 -0
- package/skills/.git 2/objects/86/0a0e1943dfe53411d2e499a1f16f46a96ef758 +0 -0
- package/skills/.git 2/objects/86/971fb55fdc081fdbae52376f0f13e57a4e9b04 +0 -0
- package/skills/.git 2/objects/88/b89dd609a0a03f8d4fe8bfde20d5b8fc1d326d +0 -0
- package/skills/.git 2/objects/90/8737edb6b7809e32cc01590b4e08ba42a9d40d +0 -0
- package/skills/.git 2/objects/93/d5a8a9a7d4fb7f11491cb596a6880528725118 +0 -0
- package/skills/.git 2/objects/98/46a2ab81d0c3b3eb00ef88fc56989aa7e9f316 +0 -0
- package/skills/.git 2/objects/9b/d8dd1e49cf274eaf9c555f3ab39dce7af5715e +0 -0
- package/skills/.git 2/objects/a1/13329fb0cec96ae78b222d33a24c3b5bc7fa1f +0 -0
- package/skills/.git 2/objects/a9/e6effe626e8a3aea3a8fc3364b492191c6e7d0 +0 -0
- package/skills/.git 2/objects/ad/6de7e48d9782cca9353d1ff0aa1aab7fe1df85 +0 -0
- package/skills/.git 2/objects/af/54ae316f771ff692e299ffcd8bf2f06b413b59 +0 -0
- package/skills/.git 2/objects/b0/4cb8b0b00dad633e731c1472161419e738d674 +0 -0
- package/skills/.git 2/objects/b3/094abb0b9ed46419b269e4a4e36a459690e3b0 +0 -0
- package/skills/.git 2/objects/b9/435c5d4baac2cfc5c83009ddd27b46b60db5f1 +0 -0
- package/skills/.git 2/objects/ba/5da17dbaec5ec2dcfdfd126aead518d1171d5c +0 -0
- package/skills/.git 2/objects/c0/bf58703aa258ba5dd63083bebaec8f223d844c +0 -0
- package/skills/.git 2/objects/c4/701a34edf1fc1bad58ccc57bd03f9426acb59a +0 -0
- package/skills/.git 2/objects/c7/5ccce9a4e5cc74d9b3174550cf6d993ca43638 +0 -0
- package/skills/.git 2/objects/c7/710d59b5a35b0f1f0a0399386643a0bd94c929 +0 -0
- package/skills/.git 2/objects/d1/fe58237112e953e5fec52da22cf38e08be3df9 +5 -0
- package/skills/.git 2/objects/d2/2bbe9fd2f74c95bc5583e803f5e435f1e2cd86 +0 -0
- package/skills/.git 2/objects/d7/e72852ea2bff74581dbf247d400120086229f4 +0 -0
- package/skills/.git 2/objects/d8/d4c3b5553e4fd72807e1d4b49ef07d9ef3ac35 +0 -0
- package/skills/.git 2/objects/dc/75050c2876f6a02ae2a53a3c886f395b622977 +0 -0
- package/skills/.git 2/objects/ee/e8546f95acec500187c08a28a8b9ee02db0dec +0 -0
- package/skills/.git 2/objects/ef/263c059208b416c2146434f10cb2b9fabcba16 +0 -0
- package/skills/.git 2/objects/f3/ae597e84d9a59b88acd21c99bde2eaf686d785 +0 -0
- package/skills/.git 2/objects/f3/f6f5673c821d3d8e76fa267a9e882e7a5387ea +0 -0
- package/skills/.git 2/objects/f9/6e6d0ad02624dd11d5848594d056caef7a5e8b +0 -0
- package/skills/.git 2/objects/ff/278988fc1edf0db3abcf18de795f4cc0b4f3e1 +0 -0
- package/skills/.git 2/refs/heads/main +1 -0
- package/skills/.git 2/refs/remotes/origin/main +1 -0
- package/skills/.pytest_cache 2/v/cache/nodeids +76 -0
- package/skills/.pytest_cache 2/v/cache/stepwise +1 -0
- package/skills/_shared/helpers.md +123 -0
- package/skills/_shared/outputs-convention.md +24 -0
- package/skills/cm-ads-tracker/SKILL.md +109 -0
- package/skills/cm-ads-tracker/evals/evals.json +55 -0
- package/skills/cm-ads-tracker/references/gtm-architecture.md +321 -0
- package/skills/cm-ads-tracker/references/industry-events.md +294 -0
- package/skills/cm-ads-tracker/references/platforms-api.md +238 -0
- package/skills/cm-ads-tracker/templates/capi-payload.md +79 -0
- package/skills/cm-ads-tracker/templates/datalayer-push.js +104 -0
- package/skills/cm-ads-tracker/templates/gtm-variables.js +56 -0
- package/skills/cm-brainstorm-idea/SKILL.md +423 -0
- package/skills/cm-code-review/SKILL.md +151 -0
- package/skills/cm-content-factory/SKILL.md +416 -0
- package/skills/cm-continuity/SKILL.md +399 -0
- package/skills/cm-dashboard/SKILL.md +533 -0
- package/skills/cm-dashboard/ui/app.js +1270 -0
- package/skills/cm-dashboard/ui/index.html +206 -0
- package/skills/cm-dashboard/ui/style.css +440 -0
- package/skills/cm-debugging/SKILL.md +412 -0
- package/skills/cm-deep-search/SKILL.md +242 -0
- package/skills/cm-design-system/SKILL.md +97 -0
- package/skills/cm-design-system/resources/halo-modern.md +40 -0
- package/skills/cm-design-system/resources/lunaris-advanced.md +40 -0
- package/skills/cm-design-system/resources/nitro-enterprise.md +39 -0
- package/skills/cm-design-system/resources/shadcn-default.md +37 -0
- package/skills/cm-dockit/README.md +100 -0
- package/skills/cm-dockit/SKILL.md +302 -0
- package/skills/cm-dockit/index.html +443 -0
- package/skills/cm-dockit/package-lock.json +1850 -0
- package/skills/cm-dockit/package.json +14 -0
- package/skills/cm-dockit/prompts/analysis.md +34 -0
- package/skills/cm-dockit/prompts/api-reference.md +24 -0
- package/skills/cm-dockit/prompts/architecture.md +21 -0
- package/skills/cm-dockit/prompts/data-flow.md +20 -0
- package/skills/cm-dockit/prompts/database.md +21 -0
- package/skills/cm-dockit/prompts/deployment.md +22 -0
- package/skills/cm-dockit/prompts/flows.md +21 -0
- package/skills/cm-dockit/prompts/jtbd.md +20 -0
- package/skills/cm-dockit/prompts/personas.md +24 -0
- package/skills/cm-dockit/prompts/sop-modules.md +40 -0
- package/skills/cm-dockit/scripts/doc-gen.sh +121 -0
- package/skills/cm-dockit/scripts/dockit-dashboard.sh +142 -0
- package/skills/cm-dockit/scripts/dockit-runner.sh +607 -0
- package/skills/cm-dockit/scripts/dockit-task.sh +166 -0
- package/skills/cm-dockit/skills/analyze-codebase.md +174 -0
- package/skills/cm-dockit/skills/api-reference.md +237 -0
- package/skills/cm-dockit/skills/changelog-guide.md +195 -0
- package/skills/cm-dockit/skills/content-guidelines.md +190 -0
- package/skills/cm-dockit/skills/sop-guide.md +184 -0
- package/skills/cm-dockit/skills/tech-docs.md +287 -0
- package/skills/cm-dockit/templates/markdown/structure.md +60 -0
- package/skills/cm-dockit/templates/vitepress-premium/.vitepress/config.mts +110 -0
- package/skills/cm-dockit/templates/vitepress-premium/.vitepress/theme/custom.css +189 -0
- package/skills/cm-dockit/templates/vitepress-premium/.vitepress/theme/index.ts +4 -0
- package/skills/cm-dockit/templates/vitepress-premium/package.json +19 -0
- package/skills/cm-dockit/templates/vitepress-premium/tests/frontend.test.ts +45 -0
- package/skills/cm-dockit/tests/runner.test.ts +66 -0
- package/skills/cm-dockit/workflows/export-markdown.md +82 -0
- package/skills/cm-dockit/workflows/generate-docs.md +68 -0
- package/skills/cm-dockit/workflows/setup-vitepress.md +181 -0
- package/skills/cm-example/SKILL.md +26 -0
- package/skills/cm-execution/SKILL.md +268 -0
- package/skills/cm-git-worktrees/SKILL.md +164 -0
- package/skills/cm-how-it-work/SKILL.md +189 -0
- package/skills/cm-identity-guard/SKILL.md +412 -0
- package/skills/cm-jtbd/SKILL.md +98 -0
- package/skills/cm-planning/SKILL.md +130 -0
- package/skills/cm-project-bootstrap/SKILL.md +161 -0
- package/skills/cm-project-bootstrap/templates/AGENTS.md +42 -0
- package/skills/cm-project-bootstrap/templates/frontend-safety.test.js +51 -0
- package/skills/cm-project-bootstrap/templates/i18n-sync.test.js +38 -0
- package/skills/cm-project-bootstrap/templates/pr-template.md +12 -0
- package/skills/cm-project-bootstrap/templates/project-identity.json +29 -0
- package/skills/cm-project-bootstrap/templates/vitest.config.js +10 -0
- package/skills/cm-quality-gate/SKILL.md +218 -0
- package/skills/cm-readit/SKILL.md +289 -0
- package/skills/cm-readit/audio-player.md +206 -0
- package/skills/cm-readit/examples/blog-reader.js +352 -0
- package/skills/cm-readit/examples/voice-cro.js +390 -0
- package/skills/cm-readit/tts-engine.md +262 -0
- package/skills/cm-readit/ui-patterns.md +362 -0
- package/skills/cm-readit/voice-cro.md +223 -0
- package/skills/cm-safe-deploy/SKILL.md +120 -0
- package/skills/cm-safe-deploy/templates/deploy.sh +89 -0
- package/skills/cm-safe-i18n/SKILL.md +473 -0
- package/skills/cm-secret-shield/SKILL.md +580 -0
- package/skills/cm-skill-chain/SKILL.md +78 -0
- package/skills/cm-skill-index/SKILL.md +318 -0
- package/skills/cm-skill-mastery/SKILL.md +169 -0
- package/skills/cm-start/SKILL.md +65 -0
- package/skills/cm-status/SKILL.md +12 -0
- package/skills/cm-tdd/SKILL.md +370 -0
- package/skills/cm-terminal/SKILL.md +177 -0
- package/skills/cm-test-gate/SKILL.md +242 -0
- package/skills/cm-ui-preview/SKILL.md +291 -0
- package/skills/cm-ux-master/DESIGN_STANDARD_TEMPLATE.md +54 -0
- package/skills/cm-ux-master/SKILL.md +114 -0
- package/skills/cro-methodology/SKILL.md +98 -0
- package/skills/cro-methodology/references/COPYWRITING.md +178 -0
- package/skills/cro-methodology/references/OBJECTIONS.md +135 -0
- package/skills/cro-methodology/references/PERSUASION.md +158 -0
- package/skills/cro-methodology/references/RESEARCH.md +220 -0
- package/skills/cro-methodology/references/funnel-analysis.md +365 -0
- package/skills/cro-methodology/references/testing-methodology.md +330 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# deploy.sh — 9-Gate Deploy Pipeline
|
|
3
|
+
# Usage: ./deploy.sh [staging|production]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
TARGET="${1:-staging}"
|
|
7
|
+
echo "🚀 Deploying to $TARGET..."
|
|
8
|
+
|
|
9
|
+
# Gate 0: Secret Hygiene
|
|
10
|
+
echo "🔒 Gate 0: Secret Hygiene..."
|
|
11
|
+
if grep -rq 'SERVICE_KEY\|ANON_KEY\|DB_PASSWORD' wrangler.jsonc 2>/dev/null; then
|
|
12
|
+
echo "❌ Secrets found in wrangler.jsonc! Use 'wrangler secret put' instead."
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
if ! grep -q '.dev.vars' .gitignore; then
|
|
16
|
+
echo "❌ .dev.vars not in .gitignore!"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
echo "✅ Gate 0 passed"
|
|
20
|
+
|
|
21
|
+
# Gate 1: Syntax Validation
|
|
22
|
+
echo "📝 Gate 1: Syntax Validation..."
|
|
23
|
+
if [ -f "tsconfig.json" ]; then
|
|
24
|
+
npx tsc --noEmit
|
|
25
|
+
else
|
|
26
|
+
find src/ public/ -name "*.js" -exec node -c {} \; 2>&1
|
|
27
|
+
fi
|
|
28
|
+
echo "✅ Gate 1 passed"
|
|
29
|
+
|
|
30
|
+
# Gate 2: Test Suite
|
|
31
|
+
echo "🧪 Gate 2: Test Suite..."
|
|
32
|
+
npm run test:gate
|
|
33
|
+
echo "✅ Gate 2 passed"
|
|
34
|
+
|
|
35
|
+
# Gate 3: i18n Parity
|
|
36
|
+
echo "🌍 Gate 3: i18n Parity..."
|
|
37
|
+
if [ -d "public/static/i18n" ]; then
|
|
38
|
+
PRIMARY=$(wc -l < public/static/i18n/vi.json)
|
|
39
|
+
for f in public/static/i18n/*.json; do
|
|
40
|
+
COUNT=$(wc -l < "$f")
|
|
41
|
+
if [ "$COUNT" != "$PRIMARY" ]; then
|
|
42
|
+
echo "❌ $(basename $f) has $COUNT lines vs primary $PRIMARY"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
done
|
|
46
|
+
echo "✅ Gate 3 passed"
|
|
47
|
+
else
|
|
48
|
+
echo "⏭️ Gate 3 skipped (no i18n)"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Gate 4: Build
|
|
52
|
+
echo "🏗️ Gate 4: Build..."
|
|
53
|
+
if grep -q '"build"' package.json; then
|
|
54
|
+
npm run build
|
|
55
|
+
echo "✅ Gate 4 passed"
|
|
56
|
+
else
|
|
57
|
+
echo "⏭️ Gate 4 skipped (no build script)"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Gate 5: Dist Verification
|
|
61
|
+
echo "📦 Gate 5: Dist Verification..."
|
|
62
|
+
# Customize this for your project
|
|
63
|
+
echo "✅ Gate 5 passed"
|
|
64
|
+
|
|
65
|
+
# Gate 6: Deploy + Smoke Test
|
|
66
|
+
echo "🚀 Gate 6: Deploy..."
|
|
67
|
+
if [ "$TARGET" = "production" ]; then
|
|
68
|
+
BRANCH=$(git branch --show-current)
|
|
69
|
+
if [ "$BRANCH" != "production" ]; then
|
|
70
|
+
echo "❌ Must be on 'production' branch for production deploy"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
# Replace with your deploy command:
|
|
75
|
+
# npx wrangler pages deploy dist/ --project-name=YOUR_PROJECT
|
|
76
|
+
echo "✅ Gate 6 passed"
|
|
77
|
+
|
|
78
|
+
# Gate 7: Version Bump (post-deploy)
|
|
79
|
+
echo "🏷️ Gate 7: Version Bump..."
|
|
80
|
+
# npm run release:version
|
|
81
|
+
echo "✅ Gate 7 passed"
|
|
82
|
+
|
|
83
|
+
# Gate 8: Changelog (post-deploy)
|
|
84
|
+
echo "📋 Gate 8: Changelog..."
|
|
85
|
+
# npm run release:changelog
|
|
86
|
+
echo "✅ Gate 8 passed"
|
|
87
|
+
|
|
88
|
+
echo ""
|
|
89
|
+
echo "🎉 All 9 gates passed! Deployed to $TARGET."
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cm-safe-i18n
|
|
3
|
+
description: Use when translating, extracting, or mass-converting hardcoded strings to i18n t() calls. Enforces multi-pass batching, parallel-per-language dispatch, 8 audit gates, and HTML integrity checks. Battle-tested through 21+ batches and 12 bug categories from the March 2026 incidents.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Safe i18n Translation v2.0
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Mass i18n conversion is the most dangerous code transformation in a frontend monolith. A single-pass conversion of 600+ strings corrupted `app.js` beyond repair while 572 backend tests passed green. Additional incidents include HTML tag corruption, variable shadowing, and placeholder translation errors.
|
|
11
|
+
|
|
12
|
+
**Core principle:** Every batch of i18n changes MUST pass ALL 8 audit gates before proceeding. No exceptions.
|
|
13
|
+
|
|
14
|
+
**Violating the letter of this rule is violating the spirit of this rule.**
|
|
15
|
+
|
|
16
|
+
## The Iron Law
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
NO BATCH WITHOUT PASSING ALL 8 AUDIT GATES.
|
|
20
|
+
NO LANGUAGE FILE WITHOUT KEY PARITY.
|
|
21
|
+
NO DEPLOY WITHOUT FULL SYNTAX VALIDATION.
|
|
22
|
+
NO HTML TAG MODIFICATION — TEXT CONTENT ONLY.
|
|
23
|
+
NO REGEX TO FIX REGEX ERRORS — USE LEXICAL SCANNER.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## When to Use
|
|
27
|
+
|
|
28
|
+
**ALWAYS** when any of these happen:
|
|
29
|
+
- Extracting hardcoded strings to `t()` calls
|
|
30
|
+
- Adding new language file (e.g., `ph.json`)
|
|
31
|
+
- Mass-converting strings across >10 lines
|
|
32
|
+
- Updating translation keys or namespaces
|
|
33
|
+
- Migrating i18n library or pattern
|
|
34
|
+
|
|
35
|
+
**Don't use for:**
|
|
36
|
+
- Adding 1-3 translation keys (just add manually + test)
|
|
37
|
+
- Fixing a single typo in a JSON file
|
|
38
|
+
|
|
39
|
+
## The Protocol
|
|
40
|
+
|
|
41
|
+
```dot
|
|
42
|
+
digraph i18n_flow {
|
|
43
|
+
rankdir=TB;
|
|
44
|
+
"0. Pre-flight" [shape=box];
|
|
45
|
+
"1. Scan ALL files" [shape=box];
|
|
46
|
+
">10 strings?" [shape=diamond];
|
|
47
|
+
"Manual add + test" [shape=box];
|
|
48
|
+
"2. Plan passes" [shape=box];
|
|
49
|
+
"3. Extract batch (max 30)" [shape=box];
|
|
50
|
+
"4. 8-Gate Audit" [shape=box, style=filled, fillcolor="#ffffcc"];
|
|
51
|
+
"All 8 pass?" [shape=diamond];
|
|
52
|
+
"FIX or ROLLBACK" [shape=box, style=filled, fillcolor="#ffcccc"];
|
|
53
|
+
"More batches?" [shape=diamond];
|
|
54
|
+
"5. Parallel language sync" [shape=box];
|
|
55
|
+
"6. Final validation" [shape=box];
|
|
56
|
+
|
|
57
|
+
"0. Pre-flight" -> "1. Scan ALL files";
|
|
58
|
+
"1. Scan ALL files" -> ">10 strings?";
|
|
59
|
+
">10 strings?" -> "Manual add + test" [label="no"];
|
|
60
|
+
">10 strings?" -> "2. Plan passes" [label="yes"];
|
|
61
|
+
"2. Plan passes" -> "3. Extract batch (max 30)";
|
|
62
|
+
"3. Extract batch (max 30)" -> "4. 8-Gate Audit";
|
|
63
|
+
"4. 8-Gate Audit" -> "All 8 pass?";
|
|
64
|
+
"All 8 pass?" -> "FIX or ROLLBACK" [label="no"];
|
|
65
|
+
"FIX or ROLLBACK" -> "4. 8-Gate Audit";
|
|
66
|
+
"All 8 pass?" -> "More batches?" [label="yes"];
|
|
67
|
+
"More batches?" -> "3. Extract batch (max 30)" [label="yes"];
|
|
68
|
+
"More batches?" -> "5. Parallel language sync" [label="no"];
|
|
69
|
+
"5. Parallel language sync" -> "6. Final validation";
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### Phase 0: Pre-Flight Checks (NEW)
|
|
76
|
+
|
|
77
|
+
#### 0a. Load i18n Learnings (cm-continuity)
|
|
78
|
+
|
|
79
|
+
Before ANY i18n work, read `.cm/memory/learnings.json` filtered by `scope: "module:i18n"`.
|
|
80
|
+
Apply all active prevention patterns. Known high-value learnings:
|
|
81
|
+
- **L001**: Always run i18n-sync test after each batch extraction
|
|
82
|
+
- **L004**: Use namespace-split architecture (not monolithic JSON)
|
|
83
|
+
- **L005**: Add exclusion list to extraction scripts to prevent junk keys
|
|
84
|
+
|
|
85
|
+
> If learnings.json has relevant entries, reference them before starting. If no `.cm/` directory exists, proceed but create learnings after this workflow.
|
|
86
|
+
|
|
87
|
+
Before ANY i18n work:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# NEVER work on main
|
|
91
|
+
git checkout -b i18n/$(date +%Y%m%d)-target-description
|
|
92
|
+
|
|
93
|
+
# Verify baseline is clean
|
|
94
|
+
node -c public/static/app.js
|
|
95
|
+
npm run test:gate
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If either fails, DO NOT PROCEED. Fix the baseline first.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Phase 1: Scan ALL Frontend Files (IMPROVED)
|
|
103
|
+
|
|
104
|
+
> [!CAUTION]
|
|
105
|
+
> **Lesson #11:** `import-adapters.js` and `import-engine.js` had 60+ hardcoded strings that were initially missed because only `app.js` was scanned.
|
|
106
|
+
|
|
107
|
+
Scan EVERY file that produces user-visible UI text:
|
|
108
|
+
```bash
|
|
109
|
+
# Scan ALL .js files for Vietnamese strings
|
|
110
|
+
node scripts/i18n-lint.js
|
|
111
|
+
|
|
112
|
+
# Also check non-app.js files
|
|
113
|
+
grep -rnP '[àáạảãâầấậẩẫăằắặẳẵ]' public/static/*.js --include="*.js" | grep -v "\.backup" | grep -v "i18n"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Group strings by **functional domain** — never by file position:
|
|
117
|
+
|
|
118
|
+
| Pass | Domain | Example Keys |
|
|
119
|
+
|------|--------|-------------|
|
|
120
|
+
| 1 | Core UI | `sidebar.*`, `common.*`, `login.*` |
|
|
121
|
+
| 2 | Primary Feature | `vio.*`, `emp.*`, `scores.*` |
|
|
122
|
+
| 3 | Config & Settings | `config.*`, `benconf.*` |
|
|
123
|
+
| 4 | Reports & Export | `report.*`, `export.*` |
|
|
124
|
+
| 5 | Secondary Files | `import-adapters.js`, `import-engine.js` |
|
|
125
|
+
| 6 | Edge cases | Tooltips, error messages, dynamic labels |
|
|
126
|
+
|
|
127
|
+
**Output:** A numbered list of passes with estimated string count per pass per FILE.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Phase 2: Extract Batch (MAX 30 strings per batch)
|
|
132
|
+
|
|
133
|
+
> [!CAUTION]
|
|
134
|
+
> **MAX 30 strings per batch. Not 31. Not "about 30". Exactly 30 or fewer.**
|
|
135
|
+
> The i18n crash happened because 600+ strings were done in one pass.
|
|
136
|
+
|
|
137
|
+
For each batch:
|
|
138
|
+
|
|
139
|
+
1. **Identify** up to 30 hardcoded strings in the current pass domain
|
|
140
|
+
2. **Generate** namespace-compliant keys: `domain.descriptive_key`
|
|
141
|
+
3. **Replace** strings with `t('domain.key')` calls
|
|
142
|
+
4. **Add** keys to the **primary** language JSON (usually `vi.json`)
|
|
143
|
+
|
|
144
|
+
#### String Replacement Rules (12 Bug Categories Encoded)
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// ✅ CORRECT — backtick template with t() inside
|
|
148
|
+
`<div>${t('login.welcome')}</div>`
|
|
149
|
+
|
|
150
|
+
// ✅ CORRECT — concatenation
|
|
151
|
+
'<div>' + t('login.welcome') + '</div>'
|
|
152
|
+
|
|
153
|
+
// ❌ BUG #1 (FATAL) — single-quote wrapping template expression
|
|
154
|
+
'${t("login.welcome")}' // ← THIS DESTROYED APP.JS
|
|
155
|
+
|
|
156
|
+
// ❌ BUG #4 — mismatched delimiters
|
|
157
|
+
t('login.welcome`) // ← quote/backtick mismatch
|
|
158
|
+
t(`login.welcome') // ← backtick/quote mismatch
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Ternary Inside Template Literals (Bug #5)
|
|
162
|
+
```javascript
|
|
163
|
+
// ❌ BROKEN — single-quote ternary result with template expression
|
|
164
|
+
${ canDo ? '...${t('key')}...' : '' }
|
|
165
|
+
|
|
166
|
+
// ✅ CORRECT — backtick ternary result
|
|
167
|
+
${ canDo ? `...${t('key')}...` : '' }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Variable Shadowing (Bug #3)
|
|
171
|
+
```javascript
|
|
172
|
+
// ❌ BROKEN — shadows global t() translation function
|
|
173
|
+
items.map((t, i) => `<div>${t('key')}</div>`)
|
|
174
|
+
|
|
175
|
+
// ✅ CORRECT — use different variable name
|
|
176
|
+
items.map((item, i) => `<div>${t('key')}</div>`)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### HTML Tag Protection (Bug #2)
|
|
180
|
+
```javascript
|
|
181
|
+
// ❌ NEVER modify content inside HTML tags
|
|
182
|
+
`< div class="card" >` // spaces inside tags = broken rendering
|
|
183
|
+
`style = "color: red"` // space around = breaks attributes
|
|
184
|
+
`<!-- text-- >` // broken comment closers
|
|
185
|
+
|
|
186
|
+
// ✅ ONLY replace text content between tags
|
|
187
|
+
`<div class="card">${t('card.title')}</div>`
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### Static Keys Only (Bug #8)
|
|
191
|
+
```javascript
|
|
192
|
+
// ❌ FORBIDDEN — dynamic keys can't be statically validated
|
|
193
|
+
t('nav.' + pageName)
|
|
194
|
+
t(`messages.${type}`)
|
|
195
|
+
|
|
196
|
+
// ✅ REQUIRED — static keys only
|
|
197
|
+
t('nav.dashboard')
|
|
198
|
+
t('nav.employees')
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### Phase 3: 8-Gate Audit (MANDATORY after every batch)
|
|
204
|
+
|
|
205
|
+
> [!IMPORTANT]
|
|
206
|
+
> All 8 gates must pass. **Any failure = STOP and FIX before continuing.**
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Gate 1: JavaScript syntax (fast, <1s)
|
|
210
|
+
node -c public/static/app.js
|
|
211
|
+
# Must output: "public/static/app.js: No syntax errors"
|
|
212
|
+
|
|
213
|
+
# Gate 2: Syntax check on ALL modified .js files
|
|
214
|
+
node -c public/static/import-adapters.js 2>/dev/null
|
|
215
|
+
node -c public/static/import-engine.js 2>/dev/null
|
|
216
|
+
|
|
217
|
+
# Gate 3: Corruption pattern check (catches what node -c misses)
|
|
218
|
+
grep -nP "=\s*'[^']*\$\{t\(" public/static/app.js
|
|
219
|
+
# Must return 0 matches
|
|
220
|
+
|
|
221
|
+
# Gate 4: Mismatched delimiter check
|
|
222
|
+
grep -nP "t\('[^']*\`\)" public/static/app.js
|
|
223
|
+
grep -nP "t\(\`[^']*'\)" public/static/app.js
|
|
224
|
+
# Must return 0 matches each
|
|
225
|
+
|
|
226
|
+
# Gate 5: HTML tag integrity (NEW — Bug #2)
|
|
227
|
+
grep -nP "<\s+\w" public/static/app.js | head -5
|
|
228
|
+
grep -nP "</\s+\w" public/static/app.js | head -5
|
|
229
|
+
grep -nP "--\s+>" public/static/app.js | head -5
|
|
230
|
+
grep -nP '\w+\s+=\s+"' public/static/app.js | grep -v "==\|!=\|<=\|>=" | head -5
|
|
231
|
+
# Must return 0 matches (excluding legitimate JS operators)
|
|
232
|
+
|
|
233
|
+
# Gate 6: Variable shadowing check
|
|
234
|
+
grep -nP "\.\s*(map|filter|forEach|reduce)\s*\(\s*\(\s*t\s*[,)]" public/static/app.js
|
|
235
|
+
# Must return 0 matches
|
|
236
|
+
|
|
237
|
+
# Gate 7: JSON validity
|
|
238
|
+
node -e "JSON.parse(require('fs').readFileSync('public/static/i18n/vi.json'))"
|
|
239
|
+
|
|
240
|
+
# Gate 8: Full test suite
|
|
241
|
+
npm run test:gate
|
|
242
|
+
# Must output: 0 failures
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Audit Summary Table:**
|
|
246
|
+
|
|
247
|
+
| Gate | Check | Command | Pass Criteria | Bug # Prevented |
|
|
248
|
+
|------|-------|---------|---------------|-----------------|
|
|
249
|
+
| 1 | JS syntax (main) | `node -c app.js` | No syntax errors | #1, #4 |
|
|
250
|
+
| 2 | JS syntax (all files) | `node -c *.js` | No syntax errors | #11 |
|
|
251
|
+
| 3 | Corruption pattern | grep `= '..${t(` | 0 matches | #1 |
|
|
252
|
+
| 4 | Delimiter mismatch | grep mixed delims | 0 matches | #4 |
|
|
253
|
+
| 5 | HTML tag integrity | grep `< div`, `</ div` | 0 matches | #2 |
|
|
254
|
+
| 6 | Variable shadowing | grep `.map((t,` | 0 matches | #3 |
|
|
255
|
+
| 7 | JSON valid | `JSON.parse()` | No parse errors | #6 |
|
|
256
|
+
| 8 | Full test suite | `npm run test:gate` | 0 failures | #9 |
|
|
257
|
+
|
|
258
|
+
**If ALL 8 gates pass → commit:**
|
|
259
|
+
```bash
|
|
260
|
+
git add -A && git commit -m "i18n pass N batch M/T: domain description (X strings)"
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**If ANY gate fails → FIX immediately.** Do NOT proceed to next batch.
|
|
264
|
+
|
|
265
|
+
**If fix attempt uses regex → STOP.** Use the lexical scanner instead (Bug #10).
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### Phase 4: Parallel Language Sync
|
|
270
|
+
|
|
271
|
+
**REQUIRED SUB-SKILL:** Use `cm-execution` (Parallel mode).
|
|
272
|
+
|
|
273
|
+
After ALL strings are extracted to the primary language, sync remaining languages **in parallel**:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
Agent 1 → Translate all keys to en.json (English)
|
|
277
|
+
Agent 2 → Translate all keys to th.json (Thai)
|
|
278
|
+
Agent 3 → Translate all keys to ph.json (Filipino)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Each agent prompt MUST include:**
|
|
282
|
+
```markdown
|
|
283
|
+
Translate the following i18n keys from vi.json to [LANGUAGE]:
|
|
284
|
+
|
|
285
|
+
Source file: public/static/lang/vi.json
|
|
286
|
+
Target file: public/static/lang/[LANG].json
|
|
287
|
+
|
|
288
|
+
Rules:
|
|
289
|
+
1. Translate ALL keys — missing keys will break the app
|
|
290
|
+
2. Keep key names EXACTLY the same (only values change)
|
|
291
|
+
3. Keep {{param}} interpolation placeholders intact — NEVER translate them
|
|
292
|
+
4. Do NOT translate technical terms (e.g., PPH, KPI, CSV)
|
|
293
|
+
5. Preserve HTML entities if present in values
|
|
294
|
+
6. Do NOT produce empty string "" values — every key must have content
|
|
295
|
+
7. Preserve the exact same JSON structure/nesting
|
|
296
|
+
|
|
297
|
+
After translation:
|
|
298
|
+
1. Validate JSON: node -e "JSON.parse(require('fs').readFileSync('[LANG].json'))"
|
|
299
|
+
2. Count keys must EQUAL vi.json key count
|
|
300
|
+
3. No null or empty string values
|
|
301
|
+
4. All {{param}} placeholders preserved identically
|
|
302
|
+
|
|
303
|
+
Return: Key count + any untranslatable terms flagged.
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**After agents return — 3-Point Parity Check:**
|
|
307
|
+
```bash
|
|
308
|
+
# Check 1: Key count parity
|
|
309
|
+
node -e "
|
|
310
|
+
const fs = require('fs');
|
|
311
|
+
const langs = ['vi','en','th','ph'];
|
|
312
|
+
const counts = langs.map(l => {
|
|
313
|
+
const keys = Object.keys(JSON.parse(fs.readFileSync('public/static/i18n/'+l+'.json')));
|
|
314
|
+
console.log(l + ': ' + keys.length + ' keys');
|
|
315
|
+
return keys.length;
|
|
316
|
+
});
|
|
317
|
+
if (new Set(counts).size !== 1) {
|
|
318
|
+
console.error('❌ KEY PARITY FAILURE! Counts differ across languages.');
|
|
319
|
+
process.exit(1);
|
|
320
|
+
} else {
|
|
321
|
+
console.log('✅ Key parity: all languages have ' + counts[0] + ' keys');
|
|
322
|
+
}
|
|
323
|
+
"
|
|
324
|
+
|
|
325
|
+
# Check 2: Empty value detection (NEW — prevents blank UI)
|
|
326
|
+
node -e "
|
|
327
|
+
const fs = require('fs');
|
|
328
|
+
const langs = ['vi','en','th','ph'];
|
|
329
|
+
let hasEmpty = false;
|
|
330
|
+
for (const lang of langs) {
|
|
331
|
+
const data = JSON.parse(fs.readFileSync('public/static/i18n/' + lang + '.json'));
|
|
332
|
+
const check = (obj, prefix) => {
|
|
333
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
334
|
+
const key = prefix ? prefix + '.' + k : k;
|
|
335
|
+
if (v === '' || v === null) { console.error('❌ Empty value: ' + lang + ':' + key); hasEmpty = true; }
|
|
336
|
+
if (typeof v === 'object' && v !== null) check(v, key);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
check(data, '');
|
|
340
|
+
}
|
|
341
|
+
if (hasEmpty) process.exit(1);
|
|
342
|
+
console.log('✅ No empty values');
|
|
343
|
+
"
|
|
344
|
+
|
|
345
|
+
# Check 3: Placeholder preservation (NEW — Bug #7)
|
|
346
|
+
node -e "
|
|
347
|
+
const fs = require('fs');
|
|
348
|
+
const vi = JSON.parse(fs.readFileSync('public/static/i18n/vi.json'));
|
|
349
|
+
const flatten = (obj, pre='') => Object.entries(obj).reduce((a, [k,v]) => {
|
|
350
|
+
const key = pre ? pre+'.'+k : k;
|
|
351
|
+
if (typeof v === 'object' && v !== null && !Array.isArray(v)) return [...a, ...flatten(v, key)];
|
|
352
|
+
return [...a, [key, v]];
|
|
353
|
+
}, []);
|
|
354
|
+
const viFlat = Object.fromEntries(flatten(vi));
|
|
355
|
+
let errors = 0;
|
|
356
|
+
for (const lang of ['en','th','ph']) {
|
|
357
|
+
const other = Object.fromEntries(flatten(JSON.parse(fs.readFileSync('public/static/i18n/'+lang+'.json'))));
|
|
358
|
+
for (const [key, viVal] of Object.entries(viFlat)) {
|
|
359
|
+
if (typeof viVal !== 'string') continue;
|
|
360
|
+
const viParams = (viVal.match(/\{\{[^}]+\}\}/g) || []).sort().join(',');
|
|
361
|
+
const otherVal = other[key] || '';
|
|
362
|
+
const otherParams = (otherVal.match(/\{\{[^}]+\}\}/g) || []).sort().join(',');
|
|
363
|
+
if (viParams && viParams !== otherParams) {
|
|
364
|
+
console.error('❌ ' + lang + ':' + key + ' placeholder mismatch: vi=' + viParams + ' ' + lang + '=' + otherParams);
|
|
365
|
+
errors++;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (errors) process.exit(1);
|
|
370
|
+
console.log('✅ All placeholders preserved');
|
|
371
|
+
"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### Phase 5: Final Validation
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
# 1. Full syntax check on ALL frontend files
|
|
380
|
+
for f in public/static/app.js public/static/import-adapters.js public/static/import-engine.js; do
|
|
381
|
+
[ -f "$f" ] && node -c "$f"
|
|
382
|
+
done
|
|
383
|
+
|
|
384
|
+
# 2. Full test gate (includes frontend-safety + i18n-sync tests)
|
|
385
|
+
npm run test:gate
|
|
386
|
+
|
|
387
|
+
# 3. Build
|
|
388
|
+
npm run build
|
|
389
|
+
|
|
390
|
+
# 4. Remaining hardcoded scan (should be ~0)
|
|
391
|
+
node scripts/i18n-lint.js
|
|
392
|
+
|
|
393
|
+
# 5. Manual smoke test — switch languages in browser
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Commit and merge:**
|
|
397
|
+
```bash
|
|
398
|
+
git add -A && git commit -m "i18n: complete [scope] - N strings across M languages"
|
|
399
|
+
git checkout main
|
|
400
|
+
git merge i18n/...
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### Post-completion: Update Memory (cm-continuity)
|
|
404
|
+
|
|
405
|
+
After final validation passes:
|
|
406
|
+
1. Update `.cm/CONTINUITY.md` → Just Completed: "i18n: [N] strings across [M] languages"
|
|
407
|
+
2. Record any NEW bug patterns to `.cm/memory/learnings.json` with `scope: "module:i18n"`
|
|
408
|
+
3. **Anti-duplicate**: If same pattern as existing learning → reinforce (count++), don't create new entry
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Quick Reference: Key Naming Convention
|
|
413
|
+
|
|
414
|
+
| Namespace | Usage | Example |
|
|
415
|
+
|-----------|-------|---------|
|
|
416
|
+
| `common` | Shared UI (buttons, statuses) | `common.save`, `common.cancel` |
|
|
417
|
+
| `login` | Authentication flows | `login.welcome`, `login.forgot_pw` |
|
|
418
|
+
| `sidebar` | Navigation menu | `sidebar.dashboard`, `sidebar.logout` |
|
|
419
|
+
| `vio` | Violations module | `vio.confirm_title`, `vio.level_high` |
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## The 13 Bug Categories — Quick Reference
|
|
424
|
+
|
|
425
|
+
| # | Bug | Pattern | Detection Gate |
|
|
426
|
+
|---|-----|---------|---------------|
|
|
427
|
+
| 1 | Single-quote wrapping `${t()}` | `= '..${t(..'` | Gate 3 |
|
|
428
|
+
| 2 | HTML tag corruption | `< div`, `</ div`, `-- >` | Gate 5 |
|
|
429
|
+
| 3 | Variable shadowing | `.map((t,` | Gate 6 |
|
|
430
|
+
| 4 | Mismatched delimiters | `t('key\`)`, `t(\`key')` | Gate 4 |
|
|
431
|
+
| 5 | Ternary nesting trap | Single-quote branch with `${t()}` | Gate 1 + 3 |
|
|
432
|
+
| 6 | Key parity failures | Missing keys in some languages | Phase 4 parity |
|
|
433
|
+
| 7 | Placeholder translation | `{{count}}` → `{{จำนวน}}` | Phase 4 placeholder check |
|
|
434
|
+
| 8 | Dynamic key concatenation | `t('nav.' + var)` | Static analysis |
|
|
435
|
+
| 9 | Backend pass, frontend broken | 572 tests green, white screen | Gate 1 + 8 |
|
|
436
|
+
| 10 | Regex fixing regex | Infinite fix-break loop | Use lexical scanner |
|
|
437
|
+
| 11 | Missed files | Only scanned app.js | Phase 1 scan ALL |
|
|
438
|
+
| 12 | Line number drift | Stale line refs across batches | Target by function name |
|
|
439
|
+
| 13 | **Flat vs nested key count mismatch** | Test vs Gate metrics | i18n-sync test design |
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Red Flags — STOP Immediately
|
|
444
|
+
|
|
445
|
+
- ❌ Converting >30 strings without running ALL 8 audit gates
|
|
446
|
+
- ❌ Using find-replace without verifying backtick vs single-quote context
|
|
447
|
+
- ❌ Committing all translations in a single commit
|
|
448
|
+
- ❌ Skipping key parity check across language files
|
|
449
|
+
- ❌ "It's just a string replacement, it'll be fine"
|
|
450
|
+
|
|
451
|
+
## Rationalization Table
|
|
452
|
+
|
|
453
|
+
| Excuse | Reality |
|
|
454
|
+
|--------|---------|
|
|
455
|
+
| "It's just find-replace" | Find-replace destroyed app.js. MAX 30 strings per batch. |
|
|
456
|
+
| "The regex handles everything" | Regex false-positives crashed the app. 8 audit gates after each batch. |
|
|
457
|
+
| "I'll test at the end" | You won't find which of 600 changes broke it. Test after each 30. |
|
|
458
|
+
| "One commit is cleaner" | One commit = one rollback point for 600 changes. Granular commits. |
|
|
459
|
+
|
|
460
|
+
## Integration with Other Skills
|
|
461
|
+
|
|
462
|
+
| Skill | When |
|
|
463
|
+
|-------|------|
|
|
464
|
+
| `cm-quality-gate` | Final test gate before deploy |
|
|
465
|
+
| `cm-execution` | Phase 4: Parallel language translation |
|
|
466
|
+
| `cm-terminal` | While running audit commands |
|
|
467
|
+
| `cm-continuity` | Phase 0: load i18n learnings; Post-completion: record new patterns |
|
|
468
|
+
|
|
469
|
+
## The Bottom Line
|
|
470
|
+
|
|
471
|
+
**30 strings per batch. 8 audit gates after each. No exceptions.**
|
|
472
|
+
|
|
473
|
+
The i18n incidents of March 2026 produced 12 distinct bug categories. This skill encodes protections against every single one. Follow the protocol exactly.
|