* feat: extend UAT checkpoint frame language pack (9 more languages)
response_language is a free-form config value, but CHECKPOINT_FRAMES only
covered 9 languages — any other configured language silently fell back to
the English frame. Add Dutch, Polish, Russian, Ukrainian, Turkish, Hindi,
Arabic, Vietnamese, and Indonesian frames plus their aliases, with a
regression test asserting each resolves instead of falling back.
Follow-up to #2402 (PR #2457).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: add changeset for #2527
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(#2530): list UAT checkpoint frame languages in CONFIGURATION.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(#2530): point changeset fragment at PR #2557
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(#2530): address Unicode language-pack review
* fix: address checkpoint language review
* fix: count spacing combining marks in checkpoint width
* test: verify checkpoint aliases structurally
* fix: isolate RTL checkpoint frames
* fix: isolate RTL checkpoint frames correctly
* test(#2530): assert checkpoint aliases neither collide nor go unreachable
Review Minor #1. A duplicate alias key was invisible to the existing
catalog tests: the runtime object is well-formed after JS collapses the
literal, the self-alias assertion still holds, and the losing language
just stops resolving. tsc catches the byte-equal case (TS1117), but not
the two that survive compilation — an alias whose NFC-lowercase form
already belongs to another language, and an alias not in lookup form at
all, which resolveCheckpointFrame() can never produce.
The check reads the source literal rather than the object, since the
object no longer records what was written. Both assertions are
independently load-bearing: an NFD twin of an existing alias trips the
collision check, an uppercase alias trips the unreachability check.
Review Minor #2: changeset retyped Changed -> Added. Nine wholly new
supported response_language values are an addition under Keep a
Changelog, not a modification of existing behavior.
* test(#2530): check alias collisions on the catalog, not its source
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
Co-authored-by: Rezolv <dave@sienkowski.com>
* fix(#2402): honor response_language across orchestrator output + UAT checkpoint renderer
Replays the in-flight bot branch fix/2402-response-language-orchestrator-coverage
(seven commits, never pushed) onto current origin/next as a single squashed commit.
The original work was substantial and correct; this commit preserves its full scope,
trimmed where rebase conflicts + workflow size budgets required it.
Three independent layers where response_language was being dropped are closed:
Layer 1 — orchestrator-facing directives across workflows. Adds the strong
"All user-facing output in this workflow MUST be presented in {response_language};
technical terms, code, paths, and subagent prompts stay in English" directive
to ~40 workflows that previously either lacked it entirely (verify-work,
new-project, new-milestone, quick, manager, and ~35 more) or carried only
the weak subagent-prompt-only form (plan-phase, execute-phase). The directive
covers narration between tool calls and banner output, not just the
AskUserQuestion prompts.
Layer 2 — UAT checkpoint renderer (src/uat.cts). buildCheckpoint now accepts
an optional responseLanguage parameter and renders the frame strings
("CHECKPOINT: Verification Required", "Type `pass` or describe what's wrong.")
in any of 9 languages (English/Spanish/French/German/Portuguese/Japanese/
Chinese/Korean/Italian) with an alias table covering ~30 input variants
(en, es, español, ja, 日本語, etc.). cmdRenderCheckpoint reads
config.response_language via loadConfig(cwd) and passes it through, so the
byte-for-byte block verify-work.md reprints verbatim is already localized
when written — preserving the anti-injection hygiene rule at verify-work.md
(the model is forbidden to translate after the fact). CJK display width is
computed by East Asian Width property ranges (W/F) so the right ║ border of
the banner stays aligned for full-width characters. English fallback is
byte-identical to the pre-fix behavior when response_language is unset or
unrecognized.
Layer 3 — literal English report templates in execute-phase. The top-of-
workflow directive covers all template sites (templates are a structural
source, not literal output). Inline render-language notes that previously
sat at each template site were removed during the squash because they
pushed execute-phase.md over its frozen pre-phase-6 byte ceiling
(93600 — ADR-857 Phase 6 capstone). The single top directive covers the
same surface with fewer bytes.
Also extends src/docs.cts and src/init.cts to propagate response_language
into the init JSON bundle of the additional workflows so the directive can
read it.
Tests added:
- tests/uat.test.cjs: buildCheckpoint with unset/unrecognized language falls
back to English default; recognized language swaps only the two frame
strings while structural lines stay untouched; CJK display-width regression
(independent recomputation of East Asian Width W/F ranges).
- tests/workspace.test.cjs, tests/docs-update.test.cjs: response_language
wiring through docs.cts/init.cts.
References: #2402; reporter's three-layer triage + Layer-4 follow-up; the
byte-for-byte anti-injection hygiene rule at verify-work.md (the reason
Layer 2 must be renderer-side, not model-translated).
This is a squash of the in-flight bot branch — seven commits representing
the original implementation plus its subsequent fix/CJK-padding/test/
changeset/regen cycles, none of which were ever pushed or PR'd. The squash
captures the final coherent state.
* chore(#2402): backfill pr:2457 in .changeset/2402-response-language-orchestrator-coverage.md
* chore(#2402): regen golden + size baseline after rebase against #2315 (PR #2451)
Rebase conflicts were entirely in generated artifacts (golden-install-parity
fixtures + workflow-size-baseline.json). After taking theirs during rebase,
regenerated cleanly against the merged source tree.
parseUatItems only scanned '### N.' expected/result blocks and
parseVerificationItems only recognized table/bullet/numbered shapes, so
audit-uat returned a false-clean total_items:0 when a file recorded open
findings in a '## Gaps' section, declared items in a frontmatter
human_verification: array, or used the '### N. <label>'+bold-paragraph
verification shape.
parseUatItems now also scans '## Gaps' (via collectSection + iterateBullets)
and surfaces any entry whose status != resolved. parseVerificationItems
now treats the frontmatter human_verification: array (via extractFrontmatter)
as the primary source when present, and adds a tokenizeHeadings fallback
for the '### N.'+bold-paragraph shape, preserving the existing
table/bullet/numbered recognition (no double-count, no regression).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(security): neutralize spaced+closing injection markers; fix audit-uat resolved status
scanForInjection recognizes — adds <user> tags, whitespace-padded tags
(e.g. <user >), closing [/SYSTEM]/[/INST] markers, and closing <</SYS>>
markers. Five new regression tests confirm each gap is closed.
whose result column reads PASS or resolved, so items that were already
confirmed do not appear as outstanding in audit-uat --raw. Two new
regression tests cover item-level PASS and file-level status: passed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: add closing-tag assertion for spaced <user > sanitization
The test for 'neutralizes spaced tags like <user >' only asserted that the
opening token '<user' was removed. A spaced closing tag '</user >' could
survive sanitization undetected. Added assert.ok(!result.includes('</user'))
to the same test block so both sides of the tag are verified.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- uat.cjs: change result capture from \w+ to \[?(\w+)\]? so result: [pending],
[blocked], [skipped] are parsed correctly (Closes#2273)
- phase.cjs: capture zero-padded prefix in renameDecimalPhases so renamed dirs
preserve original format (e.g. 06.3-slug → 06.2-slug, not 6.2-slug)
- tests/uat.test.cjs: add regression test for bracketed result values (#2273)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(tests): standardize to node:assert/strict and t.after() per CONTRIBUTING.md
- Replace require('node:assert') with require('node:assert/strict') across
all 73 test files to enforce strict equality (no type coercion)
- Replace try/finally cleanup blocks with t.after() hooks in core.test.cjs
and hooks-opt-in.test.cjs per the test lifecycle standards
- Utility functions in codex-config and security-scan retain try/finally
as that is appropriate for per-function resource guards, not lifecycle hooks
Closes#1674
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* perf(tests): add --test-concurrency=4 to test runner for parallel file execution
Node.js --test-concurrency controls how many test files run as parallel child
processes. Set to 4 by default, configurable via TEST_CONCURRENCY env var.
Fixes tests at a known level rather than inheriting os.availableParallelism()
which varies across CI environments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(security): allowlist verify.test.cjs in prompt-injection scanner
tests/verify.test.cjs uses <human>...</human> as GSD phase task-type
XML (meaning "a human should verify this step"), which matches the
scanner's fake-message-boundary pattern for LLM APIs. This is a
false positive — add it to the allowlist alongside the other test files
that legitimately contain injection-adjacent patterns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The original PR (#1337) used \Z in a JavaScript regex, which is a
Perl/Python/Ruby anchor — JavaScript interprets it as a literal match
for the character 'Z', silently truncating expected text containing
that letter. Replace with a two-pass approach: try next-key lookahead
first, fall back to greedy match to end-of-string.
Also remove the redundant `to=all:` pattern in sanitizeForDisplay()
since it is a subset of the existing `to=[^:\s]+:` pattern.
Add regression tests proving the Z-truncation bug and verifying
expected blocks at end-of-section parse correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent silent loss of UAT/verification items when projects advance.
Surfaces outstanding items across all prior phases so nothing is forgotten.
New command:
- /gsd:audit-uat — cross-phase audit with categorized report and test plan
New capabilities:
- Cross-phase health check in /gsd:progress (Step 1.6)
- status: partial for incomplete UAT sessions
- result: blocked with blocked_by tag for dependency-gated tests
- human_needed items persisted as trackable HUMAN-UAT.md files
- Phase completion and transition warnings for verification debt
Files: 4 new, 14 modified (9 feature + 5 docs)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>