Reliable table formatting in templates - angle bracket syntax discovery after recent model change
Joshua Wilks
I've been building structured allied health report templates for ACC (NZ) documentation - the kind with dynamic tables (work history, treatment goals, assessment findings) where row counts vary per patient.
Had tables rendering reliably for weeks using standard parentheses-based AI instructions. Roughly a week ago, all table formatting broke simultaneously. Nothing changed on my end.
After some investigation, I suspect the underlying generation model was updated — possibly from Opus 4.5 to 4.6? Anthropic's documentation for Opus 4.6 notes that the model is deliberately more relaxed with formatting unless given explicit structural cues. Parentheses-style instructions that previously enforced table structure are now treated as suggestions rather than requirements.
What fixed it: Wrapping AI instructions in XML-style angle brackets instead of parentheses. The model treats content in angle brackets as explicit structural directives rather than soft guidance. Tables that were inconsistent with parentheses now render correctly every time with this syntax.
Example — this was unreliable:
(Present work history as a table with columns: Job Title | Employer | Duration | Key Duties. Add one row per job mentioned.)
This is now consistent:
<Present work history as a table with columns: Job Title | Employer | Duration | Key Duties. Add one row per job mentioned.>
This may be worth looking into for the template documentation and the template editor guidance — especially if other users are experiencing formatting inconsistencies with existing templates. Happy to share more examples or template patterns if helpful.
Joshua Wilks
Positive framing outperforms negative framing on instructions. "Include if stated in the contextual notes" reliably beats "Do not include unless stated in the contextual notes" for the same template, even though the intent is identical. Applies to most instruction types, not just source restrictions.
Both of those are consistent with the current generation of frontier LLM prompt-engineering guidance more broadly - not just Heidi-specific behaviour - for whatever that's worth when you're deciding how to phrase the updated docs.
One small structural note: we've also seen that global rules placed at the very end of the template are more reliably applied than the same rules at the top, which lines up with the guidance already in your advanced template docs. Worth keeping that emphasis in the rewrite.
Validation status: the XML pattern has been applied across 43 tables in 7 production templates, and end-to-end verified on two so far - SRNA (Social Rehabilitation Needs Assessment) and SAW (stay-at-work) - each tested against different synthetic sessions with different clinician contexts. Both rendered cleanly across all table types (contacts, work history, activity plans, assessment findings, goals). The remaining five templates use the same pattern and should work, but we're validating each as real or synthetic sessions come through. Happy to report back as the rest land.
3) Before/after example
Representative pair from the Work History section of the SRNA template:
-----------------------------------------------------------
Before (rendered as raw markdown pipe text in output):
Work History
(Present as a table with these exact columns: Job | Employer | Dates | Physical Demands.
Create one row for each job explicitly mentioned.
Do not fabricate entries.)
[Work history entries as discussed]
-----------------------------------------------------
After (renders as a native bordered table):
Work History
<output_format>
Output this section as a markdown table. The table has these exact columns: Job title | Employer | Duration | Key physical demands.
Create one row for each job explicitly mentioned in the transcript or contextual notes. Order from most recent to oldest. Do not fabricate entries not discussed.
</output_format>
[Work history entries as discussed]
-----------------------------------------------------------
Since this is public i wont attach evidence but happy to share with the team on a call.
Josh
(joshua.wilks@apmworkcare.co.nz)
Joshua Wilks
2 parts due to char limit
Happy to help! this was a fun one to diagnose, and glad it's useful to the team.
1) What table format we're aiming for
Native bordered tables in the Heidi output, the kind that render visually in the preview and in exported PDFs, with proper cell borders, header rows, and column alignment. That's what was reliably produced before the behaviour shifted - what we were getting afterwards was raw markdown pipe syntax showing through as literal text (| Column A | Column B | with a ---|---|--- separator row below).
For context, the templates we're working with are ACC allied-health assessment reports - SRNA, SAW, SNA, IOA, SDA-Nursing, WASA. Each has 2–11 tables, many dynamic: work history, qualifications, transferable skills, assessment findings, goals with target dates, etc. The clinician and the ACC case manager both read the final PDF, so clean bordered tables are a real usability thing, not cosmetic.
2) Consistency across dynamic tables
The XML <output_format> wrapper has been reliable across all 43 tables we've updated (spanning those 7 templates). But there are four patterns we've found are load-bearing — if any are missing, individual tables still fall back to raw markdown, and that can cascade-break subsequent tables in the same output (we've been calling it the "poison effect"):
Include the word "markdown" — e.g., Output this section as a markdown table. Dropping it reduces reliability.
Use the phrasing "The table has these exact columns:" more reliable in our testing than with columns:.
Domain-specific column names only. Generic names like Field | Details, Item | Notes, Category | Description trigger the markdown fallback. Names like Job title | Employer | Duration | Key physical demands render natively every time.
Don't nest XML inside parentheses. When a table block has policy guidance (source restriction, professional voice) plus the table spec, we split into two siblings — policy stays in (...), table spec in <output_format>...</output_format>. Nesting the XML inside round brackets seems to confuse parsing.
Two more things worth flagging since you're updating docs:
CAPS imperatives (MUST, CRITICAL, REQUIRED) seem to hurt compliance rather than help. Removing them improved output consistency across several templates. Plain-language directives with a short motivating reason outperform aggressive emphasis.
Heidi Team
Thanks so much for sharing this — super helpful write-up, and the before/after examples make it really clear.
The angle-bracket (XML-style) cue behaving more “directive” than parentheses is a great discovery, especially for ACC-style templates where table structure needs to be consistent even when row counts vary.
We’ll share this with the team so we can look at updating our template documentation/editor guidance to reflect what’s most reliable.
If you’re up for it, a couple quick questions to help us validate and document this properly:
1) What table format are you aiming for in the output (Markdown table, plain-text aligned columns, or something else)?
2) Does the angle-bracket approach stay consistent across all your dynamic tables (e.g., goals, findings), or are there specific ones that still occasionally break?
3) Could you paste one more example template snippet (or a screenshot) showing a “broken with parentheses” vs “fixed with angle brackets” output?
Really appreciate you flagging this — it’ll likely help a lot of other template builders too.