Fix: _parseFields accepts natural-English heading variants (#201)#217
Merged
Conversation
`lib/wrap-steps/ai-content.js:_parseFields` previously required exact- string equality (after `.toLowerCase()`) between `## Heading` text and the declared `captureField` name. This silently rejected natural Markdown a human or AI would write — `## Next Steps` vs `captureFields: ['nextSteps']` — and halted the wrap pipeline with "Required captureField missing." Surfaced as Critic n1 on PR #200. - lib/wrap-steps/ai-content.js: new `_normalizeFieldKey(s)` helper — lowercase + strip every non-`[a-z0-9]` — applied symmetrically to both sides of the heading-vs-captureField comparison. Output map keys remain the DECLARED captureField name (camelCase), not the normalized form, so downstream consumers (the summary deriver in `lib/sessions.js:_completeV2Wrap`, the prawduct lockstep pins) need no changes. - test/wrap-pipeline.test.js: 11 new tests — natural-English variants (space, kebab, screaming snake, dotted), canonical form still matches (regression pin against over-correction), mixed- style multi-heading, output-key contract, plus a dedicated `_normalizeFieldKey` describe (stripping, case folding, digit preservation, defensive coercion). - CHANGELOG.md: `### Fixed` entry under `[Unreleased]`. Prawduct lockstep + inverse-drift tests in `test/prawduct-aicontent-prompts.test.js` are intentionally left untouched — they enforce a STRICTER convention (single-word PascalCase headings) than the parser, as a style guide for bundled methodology prompts. The looser parser is the safety net for community-authored methodologies and for AI responses that drift from canonical. Normalization is a strict superset of the prior equality contract: every input the old parser matched still matches; only the previously-rejected variants now also match. Critic verdict GREEN — no MAJORs, no MINORs. Full suite 2360/2360 (net +11 from this fix). Closes #201.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lib/wrap-steps/ai-content.js:_parseFieldspreviously required exact-string equality (after.toLowerCase()) between## Headingtext and the declaredcaptureFieldname. This silently rejected the most natural Markdown a human or AI would write —## Next Steps(space-separated) vscaptureFields: ['nextSteps']— and halted the wrap pipeline withRequired captureField "nextSteps" missing or empty in AI response. Surfaced as Critic n1 on PR #200.Closes #201.
What changed
lib/wrap-steps/ai-content.js— new_normalizeFieldKey(s)helper that lowercases and strips every non-[a-z0-9]character. Applied symmetrically to both sides of the comparison. After normalization,Next Steps,next-steps,next_steps,NEXT.STEPS, andnextStepsall collapse to the same match keynextsteps. Output map keys remain the DECLARED captureField name (camelCase), not the normalized form, so downstream consumers (the summary deriver atlib/sessions.js:_completeV2Wrap, the prawduct lockstep + inverse-drift pins attest/prawduct-aicontent-prompts.test.js:117-159) need no changes.test/wrap-pipeline.test.js— 11 new tests:_parseFieldsdescribe: natural-English variants (space, kebab, screaming snake, dotted), canonical form still works (regression pin against over-correction), mixed-style multi-heading, output-key contract pin (declared name, not normalized form)._normalizeFieldKeydescribe: non-alphanumeric stripping, case folding, digit preservation, defensive coercion (null/undefined/ number).CHANGELOG.md—### Fixedentry under[Unreleased].What was deliberately NOT changed
The prawduct lockstep and inverse-drift tests in
test/prawduct-aicontent-prompts.test.jsenforce a STRICTER convention than the parser: bundled prawduct prompts must use single-word PascalCase headings that match the captureFields literally. The looser parser is the safety net for community-authored methodologies and for AI responses that drift from canonical. Tightening the prawduct tests to also use_normalizeFieldKeywould weaken the style guide they enforce — explicitly out of scope.Why this is safe
Normalization is a strict superset of the prior equality contract. Every input the old parser matched still matches; only the previously-rejected variants now also match. Pinned by the
still matches the exact canonical form '## nextSteps'test.Test plan
node --test test/wrap-pipeline.test.js— 271/271 pass (11 new + 260 pre-existing).node --test test/*.test.js— full suite 2360/2360 pass (net +11 from this fix).grep -E "^## \[" CHANGELOG.mdshows[Unreleased]→[3.17.0]adjacent —feedback_verify_changelog_structure_post_editpin).test/prawduct-aicontent-prompts.test.jsstill pass — confirms the fix doesn't break the stricter bundled-prompt convention.Refs