Release 1.1.2 — the default path works end to end

Fix release for 1.1.1: the minimal path end to end, engine commands that
run on Godot, Unity and Unreal, hook warnings you can see, and verdicts
that say when they could not check. Details in CHANGELOG.md; upgrade
steps in UPGRADING.md (v1.1.1 → v1.1.2).

Fixes #67
Fixes #131

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WuJn2FdynEybfL5KC697nJ
This commit is contained in:
Donchitos
2026-09-29 21:32:19 +10:00
co-authored by Claude Opus 5.5
parent 7ed2c3e9c4
commit b21fa0f7f2
383 changed files with 22888 additions and 10618 deletions
@@ -2,175 +2,247 @@
## Skill Summary
`/localize` manages the full localization pipeline: it extracts all player-facing
strings from source files, manages translation files in `assets/localization/`,
and validates completeness across all locale files. For new languages, it creates
a locale file skeleton with all current strings as keys and empty values. For
existing locale files, it produces a diff showing additions, removals, and
changed keys.
`/localize` runs the localization pipeline through one required subcommand:
`scan`, `extract`, `validate`, `status`, `brief`, `cultural-review`,
`vo-pipeline`, `rtl-check`, `freeze` or `qa`. It works on a single source string
table under `assets/data/strings/` (`strings-en.json` is the English source; the
other locales sit beside it) and writes reports under `production/localization/`.
Translation files are written to `assets/localization/[locale-code].csv` (or
engine-appropriate format) after a "May I write" ask. No director gates apply.
Verdicts: LOCALIZATION COMPLETE (all locales are complete) or GAPS FOUND (at
least one locale is missing string keys).
`scan`, `validate` and `status` are read-only. `extract` shows a diff of new
keyed entries and asks "May I write these new entries to
`assets/data/strings/strings-en.json`?" before appending only the diff; its
verdict is **COMPLETE**. With no subcommand the skill prints usage and stops with
verdict **FAIL** — missing required subcommand. A scan whose code root cannot be
resolved reports `NOT ASSESSED — code root unresolved`; a status run with no
string table reports `NOT ASSESSED — no string table found`. `cultural-review`
and `qa` spawn `localization-lead`; `qa` issues a per-locale
PASS / PASS WITH CONDITIONS / NOT ASSESSED / FAIL verdict, first match FAIL →
NOT ASSESSED → PASS WITH CONDITIONS → PASS. Its checks are playthrough-based, so a
locale nobody has played is NOT ASSESSED, and the release gate does not count it
as passed. No director gates apply.
---
## Static Assertions (Structural)
Verified automatically by `/skill-test static` — no fixture needed.
Checked against the SKILL.md by `/skill-test spec` — no fixture needed.
- [ ] Has required frontmatter fields: `name`, `description`, `argument-hint`, `user-invocable`, `allowed-tools`
- [ ] Has ≥2 phase headings
- [ ] Contains verdict keywords: LOCALIZATION COMPLETE, GAPS FOUND
- [ ] Contains "May I write" collaborative protocol language before writing locale files
- [ ] Has a next-step handoff (e.g., send locale skeletons to translators)
- [ ] Contains verdict keywords: COMPLETE, FAIL, NOT ASSESSED, PASS WITH CONDITIONS
- [ ] Contains "May I write" collaborative protocol language before writing the string table or a report
- [ ] Has a next-step handoff (the Recommended Workflow chain ending in `/gate-check release`)
- [ ] The QA gate note is tiered: at `workflow: full` the Polish → Release gate requires PASS or PASS WITH CONDITIONS for every translated locale being shipped (recommended at `standard`, dropped at `minimal`)
- [ ] The `freeze` question names the file it writes — "This writes `production/localization/freeze-status.md`" — before the user answers
---
## Director Gate Checks
None. `/localize` is a pipeline utility. No director gates apply. Localization
lead agent may review separately but is not invoked within this skill.
None. `/localize` is a pipeline utility and spawns no director. The only agent
it spawns is `localization-lead` (an operations-tier agent), and only in the
`cultural-review` and `qa` modes.
---
## Test Cases
### Case 1: New Language — String Extraction and Locale Skeleton Created
### Case 1: Extract — New Strings Keyed and Appended After Approval
**Fixture:**
- Source code in `src/` contains player-facing strings (UI text, tutorial messages)
- Existing locale: `assets/localization/en.csv`
- No French locale exists
- `project.yaml` has `engine.name: Godot`, so the code root is `src/`
- `src/ui/hud.gd` wraps three strings in `tr()` that have no entry in
`assets/data/strings/strings-en.json`
- No `production/localization/freeze-status.md` exists
**Input:** `/localize fr`
**Input:** `/localize extract`
**Expected behavior:**
1. Skill extracts all player-facing strings from source files
2. Skill finds the same strings in `en.csv` as a reference
3. Skill generates `fr.csv` skeleton with all string keys and empty values
4. Skill asks "May I write to `assets/localization/fr.csv`?"
5. File written on approval; verdict is GAPS FOUND (file created but empty values)
6. Skill notes: "fr.csv created — send to translator to fill values"
1. Skill scans source files for localized string references and compares them
against the table in `assets/data/strings/`
2. Skill proposes one new entry per unkeyed string, with a key in the form
`[category].[subcategory].[description]` (e.g. `ui.hud.health_label`)
3. Each new entry carries a `context` field: where it appears, maximum length,
placeholder meaning
4. Skill presents the diff and asks "May I write these new entries to
`assets/data/strings/strings-en.json`?"
5. On approval it writes only the new entries (not a full replacement);
verdict is COMPLETE
**Assertions:**
- [ ] All string keys from `en.csv` are present in `fr.csv`
- [ ] All values in `fr.csv` are empty (not copied from English)
- [ ] "May I write" is asked before creating the file
- [ ] Verdict is GAPS FOUND (file is created but untranslated)
- [ ] Proposed keys follow `[category].[subcategory].[description]`
- [ ] Every new entry includes a `context` field
- [ ] The diff is shown and "May I write" names `assets/data/strings/strings-en.json` before any write
- [ ] Only the three new entries are written — existing entries are not rewritten
- [ ] Verdict is COMPLETE
---
### Case 2: Existing Locale Diff — Additions, Removals, and Changes Listed
### Case 2: No Subcommand — Usage and FAIL
**Fixture:**
- `assets/localization/fr.csv` exists with 20 string keys translated
- Source code has changed: 3 new strings added, 1 string removed, 2 strings
with changed English source text
**Input:** `/localize fr`
**Expected behavior:**
1. Skill extracts current strings from source
2. Skill diffs against existing `fr.csv`
3. Skill produces diff report:
- 3 new keys (need translation — listed as empty in fr.csv)
- 1 removed key (marked as obsolete — suggest removal)
- 2 changed keys (English source changed — French may need update, flagged)
4. Skill asks "May I update `assets/localization/fr.csv`?"
5. File updated with new empty keys added, obsolete keys marked; verdict is GAPS FOUND
**Assertions:**
- [ ] New keys appear as empty in the updated file (not auto-translated)
- [ ] Removed keys are flagged as obsolete (not silently deleted)
- [ ] Changed source strings are flagged for translator review
- [ ] Verdict is GAPS FOUND (new empty keys exist)
---
### Case 3: String Missing in One Locale — GAPS FOUND With Missing Key List
**Fixture:**
- 3 locale files exist: `en.csv`, `fr.csv`, `de.csv`
- `de.csv` is missing 4 keys that exist in both `en.csv` and `fr.csv`
- Any project state
**Input:** `/localize`
**Expected behavior:**
1. Skill reads all 3 locale files and cross-references keys
2. `de.csv` is missing 4 keys
3. Skill produces GAPS FOUND report listing the 4 missing keys by locale:
"de.csv missing: [key1], [key2], [key3], [key4]"
4. Skill offers to add the missing keys as empty values to `de.csv`
5. After approval: file updated; verdict remains GAPS FOUND (values still empty)
1. Skill finds no subcommand in the argument
2. Skill outputs usage (the ten modes) and stops
3. Verdict is FAIL — missing required subcommand
**Assertions:**
- [ ] Missing keys are listed explicitly (not just a count)
- [ ] Missing keys are attributed to the specific locale file
- [ ] Verdict is GAPS FOUND (not LOCALIZATION COMPLETE)
- [ ] Missing keys are added as empty (not auto-translated from English)
- [ ] Usage listing the available modes is printed
- [ ] Verdict is FAIL with the reason "missing required subcommand"
- [ ] No scan, extract or validation work is performed
- [ ] No file is written and no "May I write" ask appears
---
### Case 4: Translation File Has Syntax Error — Error With Line Reference
### Case 3: Validate — Gaps Reported by Locale, Nothing Written
**Fixture:**
- `assets/localization/fr.csv` has a malformed line at line 47
(missing quote closure)
- `assets/data/strings/` holds `strings-en.json`, `strings-fr.json` and
`strings-de.json`
- `strings-de.json` lacks 4 keys present in the English source
- One French entry omits the `{playerName}` placeholder its English source has
**Input:** `/localize fr`
**Input:** `/localize validate`
**Expected behavior:**
1. Skill reads `fr.csv` and encounters a parse error at line 47
2. Skill outputs: "Parse error in fr.csv at line 47: [error detail]"
3. Skill cannot diff or validate the file until the error is fixed
4. Skill does NOT attempt to overwrite or auto-fix the malformed file
5. Skill suggests fixing the file manually and re-running `/localize`
1. Skill reads every string table file in `assets/data/strings/`
2. For `de` it reports the 4 keys as Completeness gaps
3. For `fr` it reports a Placeholder mismatch on the affected key
4. Results are grouped by locale and severity
5. Skill writes nothing — validate is read-only
**Assertions:**
- [ ] Error message includes line number (line 47)
- [ ] Error detail describes the nature of the parse error
- [ ] Skill does NOT overwrite or modify the malformed file
- [ ] Manual fix + re-run is suggested as remediation
- [ ] The 4 missing keys are named and attributed to the `de` locale
- [ ] The placeholder mismatch is reported against the `fr` locale and its key
- [ ] Output is grouped by locale and severity
- [ ] No file is written or modified, and no "May I write" ask appears
---
### Case 5: Director Gate Check — No gate; localization is a pipeline utility
### Case 4: Status With No String Table — NOT ASSESSED
**Fixture:**
- Source code with player-facing strings
- `assets/data/strings/` does not exist
**Input:** `/localize fr`
**Input:** `/localize status`
**Expected behavior:**
1. Skill extracts strings and manages locale files
2. No director agents are spawned
3. No gate IDs appear in output
1. Skill looks for the source string table and finds none
2. The whole status output is `NOT ASSESSED — no string table found`
3. Skill does not produce a coverage matrix of zeros, and does not pre-fill the
source row as `100%`
**Assertions:**
- [ ] No director gate is invoked
- [ ] No gate skip messages appear
- [ ] Verdict is LOCALIZATION COMPLETE or GAPS FOUND — no gate verdict
- [ ] Output is `NOT ASSESSED — no string table found`
- [ ] No coverage matrix with zero counts is produced
- [ ] The source locale is not reported as `100%` coverage
- [ ] No file is written (status is read-only)
---
### Case 5: Director Gate Check — None; Cultural Review Uses localization-lead
**Fixture:**
- `assets/data/strings/strings-en.json` exists with player-facing strings
- Any review mode
**Input:** `/localize cultural-review`
**Expected behavior:**
1. Skill spawns `localization-lead` to audit strings and assets for cultural
sensitivity
2. Findings are presented as a table with a severity of BLOCKING, ADVISORY or
NOTE per finding
3. Skill asks "May I write this cultural review report to
`production/localization/cultural-review-[date].md`?"
4. No director agent is spawned and no gate ID appears in output
**Assertions:**
- [ ] `localization-lead` is the only agent spawned
- [ ] No director gate (creative, technical, producer, art) is invoked and no gate skip message appears
- [ ] Each finding carries BLOCKING, ADVISORY or NOTE severity
- [ ] "May I write" names `production/localization/cultural-review-[date].md` before the report is written
---
### Case 6: Scan With an Unresolved Code Root — NOT ASSESSED, never "no hardcoded strings"
**Fixture:**
- `project.yaml` has no `engine.name`; `.claude/docs/technical-preferences.md`
reads `[TO BE CONFIGURED]`
- Both `src/` and `Assets/` exist and hold UI code with hardcoded strings
(ambiguous tree)
**Input:** `/localize scan`
**Expected behavior:**
1. Skill tries to resolve the code root and cannot (no engine, two candidate roots)
2. It reports `NOT ASSESSED — code root unresolved` for the hardcoded-string
search and says which checks did not run
3. It does not guess `src/`, and does not report zero hits or "no hardcoded
strings found" — that would read as "nothing to localize"
4. Nothing is written — scan is read-only
**Assertions:**
- [ ] Output reads `NOT ASSESSED — code root unresolved` for the code search
- [ ] No "no hardcoded strings" or zero-hit result is reported
- [ ] The code root is not assumed to be `src/`
- [ ] No file is written and no "May I write" ask appears
---
### Case 7: QA on a Locale Nobody Has Played — NOT ASSESSED
**Fixture:**
- `assets/data/strings/strings-en.json` and `strings-fr.json` exist and validate
cleanly
- No one has played the French build: there are no in-game observations for the
functional, overflow, contextual-accuracy or VO checks
**Input:** `/localize qa`
**Expected behavior:**
1. Skill spawns `localization-lead` for the QA plan
2. The playthrough-based checks have no results, and no BLOCKING finding is open,
so the French verdict is **NOT ASSESSED**, naming each check that did not run
3. The verdict is not PASS or PASS WITH CONDITIONS on the strength of the string
tables alone
4. Skill asks "May I write this localization QA report to
`production/localization/loc-qa-fr-[date].md`?"; the report says the locale
has not passed QA for the release gate
**Assertions:**
- [ ] The French Status is NOT ASSESSED, with the unrun checks named
- [ ] No PASS or PASS WITH CONDITIONS is issued for an unplayed locale
- [ ] "May I write" names the loc-qa report path before it is written
- [ ] The report does not present the locale as cleared for the Polish → Release gate
---
## Protocol Compliance
- [ ] Extracts strings from source before operating on locale files
- [ ] Creates new locale files with all keys as empty values (not auto-translated)
- [ ] Diffs existing locale files against current source strings
- [ ] Flags missing keys by locale and by key name
- [ ] Asks "May I write" before creating or updating any locale file
- [ ] Verdict is LOCALIZATION COMPLETE (all locales fully translated) or GAPS FOUND
- [ ] Stops with usage and verdict FAIL when no subcommand is given
- [ ] `scan`, `validate` and `status` write no files
- [ ] Asks "May I write" with the target path before writing the string table (extract) or any report (brief, cultural-review, vo-pipeline script, rtl-check, qa)
- [ ] Extract writes only the diff of new entries, never a full replacement of the table
- [ ] Reports NOT ASSESSED instead of zero results when the code root or the string table is missing
- [ ] `qa` issues PASS, PASS WITH CONDITIONS, NOT ASSESSED or FAIL per locale, first match FAIL → NOT ASSESSED → PASS WITH CONDITIONS → PASS
- [ ] Ends with the Recommended Workflow chain / `/gate-check release` handoff
---
## Coverage Notes
- LOCALIZATION COMPLETE is only achievable when all locale files have all keys
with non-empty values; new-language skeleton creation always results in GAPS FOUND.
- Engine-specific locale formats (Godot `.translation`, Unity `.po` files) are
handled by the skill body; `.csv` is used as the canonical format in tests.
- The case where source strings change at a very high rate (continuous integration
of new UI text) is not tested; the diff logic handles this case.
- `scan` with a resolved code root (findings with file paths and line numbers)
is not separately tested; Case 6 covers the unresolved root.
- `extract` while `freeze-status.md` shows Status: ACTIVE (new keys appended to
`## Post-Freeze Changes` with a freeze-violation warning) is not tested.
- `freeze` (pre-freeze checklist and `AskUserQuestion` [A]/[B]), `brief`,
`vo-pipeline` and `rtl-check` are not exercised by a dedicated case; `qa` is
covered only for its NOT ASSESSED branch (Case 7).
- The `qa` gate note — a FAIL blocks release for that locale only — needs a live
run with multiple locales to verify.