fix: draft() throws when {{#ulist}}/{{#olist}} contains direct variable references (#145)#148
Merged
Merged
Conversation
…le references (#145) When a `{{#ulist}}` / `{{#olist}}` body contains direct variable references (e.g. `{{volumeAbove}}`) with no leading list marker (`- ` or `1. `), the markdown parser does not wrap the body in a CommonMark `List > Item` structure — it produces a `Paragraph` directly under the `ListBlockDefinition`. The existing recursion in `generateRecursiveBlocks` blindly accessed `context.nodes[0].nodes[0]`, which assumed the `List > Item` wrapping. With the wrapping absent, that path pointed at a leaf node (a `VariableDefinition` or `Text`), and `generateAgreement` was then called with a `VariableDefinition` as its root `templateMark`. The traversal visited the root with an empty `this.path`, so `getJsonPath` threw `Error: Paths must be supplied`. When the first inline node was a `Text` rather than a `VariableDefinition`, no error was thrown but each output `Item` was silently empty. Detect whether the parser produced a `CommonMark.List` wrapper and pick the per-iteration template accordingly: when wrapped, descend into `firstChild.nodes[0]` (the `Item` template) and unwrap its children into each new output `Item` (preserving existing behaviour). Otherwise treat `firstChild` itself as the per-iteration template and wrap the processed block as the sole child of each new output `Item`, which keeps a valid `Item > Paragraph > inline...` hierarchy. Adds two snapshot-based fixtures (`volumediscountulist`, `volumediscountolist`) and three assertion-based regression tests that cover both failure modes (variable as the first inline node, and a leading text inline node). Signed-off-by: Claude <noreply@anthropic.com>
Coverage Report for CI Build 25947592408Coverage increased (+0.3%) to 64.167%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes list-block drafting when {{#ulist}} / {{#olist}} bodies contain direct variable references without markdown list markers, preventing throws or empty generated items.
Changes:
- Updates recursive list generation to handle both wrapped
List > Itembodies and unwrapped paragraph bodies. - Adds regression tests for direct variables in unordered/ordered list blocks.
- Adds snapshot fixture templates for volume discount ordered/unordered lists.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/TemplateMarkInterpreter.ts |
Detects wrapped vs unwrapped list-block body shape and drafts items accordingly. |
test/TemplateMarkInterpreter.test.ts |
Adds issue #145 regression coverage for variable-first and leading-text list bodies. |
test/__snapshots__/TemplateMarkInterpreter.test.ts.snap |
Adds expected snapshots for new volume discount list fixtures. |
test/templates/good/volumediscountulist/template.md |
Adds unordered-list regression fixture template. |
test/templates/good/volumediscountulist/model.cto |
Adds model for unordered-list fixture. |
test/templates/good/volumediscountulist/data.json |
Adds data for unordered-list fixture. |
test/templates/good/volumediscountolist/template.md |
Adds ordered-list regression fixture template. |
test/templates/good/volumediscountolist/model.cto |
Adds model for ordered-list fixture. |
test/templates/good/volumediscountolist/data.json |
Adds data for ordered-list fixture. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Fixes #145.
Root cause
When a
{{#ulist}}/{{#olist}}body contains direct variable references(e.g.
{{volumeAbove}}) with no leading list marker (-or1.), themarkdown parser does not wrap the body in a CommonMark
List > Itemstructure — it produces a
Paragraphdirectly under theListBlockDefinition:generateRecursiveBlocksinsrc/TemplateMarkInterpreter.tsblindlyaccessed
context.nodes[0].nodes[0], which assumed theList > Itemwrapping was always present. With the wrapping absent, that path points
at a leaf inline node (a
VariableDefinitionorText), andgenerateAgreementwas then called with that leaf as its roottemplateMark.Two failure modes followed:
Error: Paths must be suppliedfromgetJsonPathwhen thefirst inline node is a
VariableDefinition(matches thedist/index.js:703stack reported in the issue): the traversal visitsthe root with an empty
this.path, andgetJsonPathrejects emptypaths.
Items when the first inline node isText: noerror is thrown but each output
Itemends up withnodes: [].This is reproducible with the
src/volumediscountulistandsrc/volumediscountolisttemplates fromaccordproject/cicero-template-library#484.
Fix
Detect whether the parser produced a
CommonMark.Listwrapper and pickthe per-iteration template accordingly:
firstChild.nodes[0](theItemtemplate), recurse on it, and unwrapthe processed children into each new output
Item.firstChilditself (aParagraph) asthe per-iteration template and place the processed block as the sole
child of each new output
Item, keeping a validItem > Paragraph > inline...hierarchy.Tests
test/templates/good/volumediscountulistandtest/templates/good/volumediscountolist— snapshot fixtures thatexercise the failure mode through the regular
goodTemplatesloop.test/TemplateMarkInterpreter.test.ts— assertion-based regressionblock under
describe('issue #145: ...')covering both failure modes(variable as the first inline node, and a leading-text inline node)
for both
ulistandolist. All three tests fail onmainand passwith the fix.
Network-dependent tests in this repo (
models.accordproject.orgfetches,child-process JS evaluation) were failing identically before and after
this change due to the sandboxed environment; they are unrelated to this
fix.
Generated by Claude Code