fix: replace invalid \Z regex anchor and remove redundant pattern
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>
This commit is contained in:
@@ -237,7 +237,6 @@ function sanitizeForDisplay(text) {
|
||||
|
||||
const protocolLeakPatterns = [
|
||||
/^\s*(?:assistant|user|system)\s+to=[^:\s]+:[^\n]+$/i,
|
||||
/^\s*(?:assistant|user|system)\s+to=all:[^\n]+$/i,
|
||||
/^\s*<\|(?:assistant|user|system)[^|]*\|>\s*$/i,
|
||||
];
|
||||
|
||||
|
||||
@@ -135,7 +135,8 @@ function parseCurrentTest(content) {
|
||||
|
||||
const numberMatch = section.match(/^number:\s*(\d+)\s*$/m);
|
||||
const nameMatch = section.match(/^name:\s*(.+)\s*$/m);
|
||||
const expectedBlockMatch = section.match(/^expected:\s*\|\n([\s\S]*?)(?=^\w[\w-]*:\s|\Z)/m);
|
||||
const expectedBlockMatch = section.match(/^expected:\s*\|\n([\s\S]*?)(?=^\w[\w-]*:\s)/m)
|
||||
|| section.match(/^expected:\s*\|\n([\s\S]+)/m);
|
||||
const expectedInlineMatch = section.match(/^expected:\s*(.+)\s*$/m);
|
||||
|
||||
if (!numberMatch || !nameMatch || (!expectedBlockMatch && !expectedInlineMatch)) {
|
||||
|
||||
@@ -390,6 +390,51 @@ awaiting: user response
|
||||
assert.ok(result.output.includes('Chinese strings render correctly.'));
|
||||
});
|
||||
|
||||
test('does not truncate expected text containing the letter Z', () => {
|
||||
fs.writeFileSync(uatPath, `---
|
||||
status: testing
|
||||
phase: 01-test-phase
|
||||
---
|
||||
|
||||
## Current Test
|
||||
|
||||
number: 3
|
||||
name: Timezone display
|
||||
expected: |
|
||||
Timezone abbreviation shows CET.
|
||||
Zero-offset zones display correctly.
|
||||
awaiting: user response
|
||||
`);
|
||||
|
||||
const result = runGsdTools(['uat', 'render-checkpoint', '--file', '.planning/phases/01-test-phase/01-UAT.md', '--raw'], tmpDir);
|
||||
assert.strictEqual(result.success, true, `render-checkpoint failed: ${result.error}`);
|
||||
assert.ok(result.output.includes('Timezone abbreviation shows CET.'),
|
||||
'Expected text before Z-containing word should be present');
|
||||
assert.ok(result.output.includes('Zero-offset zones display correctly.'),
|
||||
'Expected text starting with Z should not be truncated by \\Z regex bug');
|
||||
});
|
||||
|
||||
test('parses expected block when it is the last field in the section', () => {
|
||||
fs.writeFileSync(uatPath, `---
|
||||
status: testing
|
||||
phase: 01-test-phase
|
||||
---
|
||||
|
||||
## Current Test
|
||||
|
||||
number: 4
|
||||
name: Final field test
|
||||
expected: |
|
||||
This block has no trailing YAML key.
|
||||
It ends at the section boundary.
|
||||
`);
|
||||
|
||||
const result = runGsdTools(['uat', 'render-checkpoint', '--file', '.planning/phases/01-test-phase/01-UAT.md', '--raw'], tmpDir);
|
||||
assert.strictEqual(result.success, true, `render-checkpoint failed: ${result.error}`);
|
||||
assert.ok(result.output.includes('This block has no trailing YAML key.'));
|
||||
assert.ok(result.output.includes('It ends at the section boundary.'));
|
||||
});
|
||||
|
||||
test('fails when testing is already complete', () => {
|
||||
fs.writeFileSync(uatPath, `---
|
||||
status: complete
|
||||
|
||||
Reference in New Issue
Block a user