Skip to main content

Module template

Module template 

Source
Expand description

§Module: template

Template-mode support for in-place response documents. Parses structured patch blocks from agent responses and applies them to named component slots in the document, with boundary marker lifecycle management for CRDT-safe stream writes.

§Spec

  • parse_patches: Scans agent response text for <!-- patch:name -->...<!-- /patch:name --> blocks and returns a list of PatchBlock values plus any unmatched text (content outside patch blocks). Markers inside fenced code blocks (``` or ~~~) and inline code spans are ignored so examples in responses are never mis-parsed as real patches.
  • apply_patches: Delegates to apply_patches_with_overrides with an empty override map. Applies parsed patches to matching <!-- agent:name --> components in the document.
  • apply_patches_with_overrides: Core patch application pipeline:
    1. Pre-patch: strips all existing boundary markers, inserts a fresh boundary at the end of the exchange component (keyed to the file stem).
    2. Applies each patch using mode resolution: stream overrides > inline attr (patch= or mode=) > .agent-doc/config.toml ([components] section) > built-in defaults (exchange/findings default to append, all others to replace).
    3. For append mode, uses boundary-aware insertion when a boundary marker exists.
    4. Patches targeting missing component names are routed as overflow to exchange/output.
    5. Unmatched text and overflow are merged and appended to exchange/output (or an auto-created exchange component if none exists).
    6. Post-patch: if the boundary was consumed, re-inserts a fresh one at the end of exchange.
  • reposition_boundary_to_end: Removes all boundaries and inserts a new one at the end of exchange. Used by the IPC write path to keep boundary position current.
  • reposition_boundary_to_end_with_summary: Same, with optional human-readable suffix on the boundary ID (e.g. a0cfeb34:agent-doc).
  • template_info: Reads a document file, resolves its template mode flag, and returns a serializable TemplateInfo with per-component name, resolved mode, content, and line number. Used by editor plugins for rendering.
  • is_template_mode (test-only): Legacy helper to detect mode = "template" string.

§Agentic Contracts

  • parse_patches is pure and infallible for valid UTF-8; it returns Ok even for empty or patch-free responses.
  • Patch markers inside fenced code blocks are never extracted as real patches. Agents may include example markers in code blocks without triggering unintended writes.
  • Component patches are applied in reverse document order so earlier byte offsets remain valid throughout the operation.
  • A boundary marker always exists at the end of exchange after apply_patches_with_overrides returns. Callers that perform incremental (CRDT/stream) writes may rely on this invariant.
  • Missing-component patches never cause errors; content is silently routed to exchange/ output with a diagnostic written to stderr.
  • Mode precedence is deterministic: stream override > inline attr (patch= > mode=) > config.toml ([components] section) (patch key > mode key) > built-in default. Callers can rely on this ordering when constructing overrides for stream mode.
  • template_info reads the document from disk; callers must ensure the file is flushed before calling (especially in the IPC write path).

§Evals

  • parse_single_patch: single patch block → one PatchBlock, empty unmatched
  • parse_multiple_patches: two sequential patch blocks → two PatchBlocks in order, empty unmatched
  • parse_with_unmatched_content: text before and after patch block → unmatched contains both text segments
  • parse_empty_response: empty string → zero patches, empty unmatched
  • parse_no_patches: plain text with no markers → zero patches, full text in unmatched
  • parse_patches_ignores_markers_in_fenced_code_block: agent:component markers inside ``` are preserved as content
  • parse_patches_ignores_patch_markers_in_fenced_code_block: nested patch markers inside ``` are not parsed as patches
  • parse_patches_ignores_markers_in_tilde_fence: patch markers inside ~~~ are ignored
  • parse_patches_ignores_closing_marker_in_code_block: closing marker inside code block is skipped; real close is found
  • parse_patches_normal_markers_still_work: sanity — two back-to-back patches parse correctly
  • apply_patches_replace: patch to non-exchange component replaces existing content
  • apply_patches_unmatched_creates_exchange: unmatched text auto-creates <!-- agent:exchange --> when absent
  • apply_patches_unmatched_appends_to_existing_exchange: unmatched text appends to existing exchange; no duplicate component
  • repair_duplicate_exchange_opener_merges_two_blocks: two complete exchange blocks → merged into one
  • repair_duplicate_exchange_opener_returns_none_for_single: single exchange block → None (no repair needed)
  • apply_patches_post_patch_merges_duplicate_exchange_opener: post-patch guard merges duplicate exchange openers created during patching
  • apply_patches_missing_component_routes_to_exchange: patch targeting unknown component name appears in exchange
  • apply_patches_missing_component_creates_exchange: missing component + no exchange → auto-creates exchange with overflow
  • inline_attr_mode_overrides_config: mode=replace on tag wins over config.toml ([components] section) append config
  • inline_attr_mode_overrides_default: mode=replace on exchange wins over built-in append default
  • no_inline_attr_falls_back_to_config: no inline attr → config.toml ([components] section) append config applies
  • no_inline_attr_no_config_falls_back_to_default: no attr, no config → exchange defaults to append
  • inline_patch_attr_overrides_config: patch=replace on tag wins over config.toml ([components] section) append config
  • template_info_works: template-mode doc → TemplateInfo.template_mode = true, component list populated
  • template_info_legacy_mode_works: response_mode: template frontmatter key recognized
  • template_info_append_mode: non-template doc → template_mode = false, empty component list
  • is_template_mode_detection: Some("template") → true; other strings and None → false
  • (aspirational) apply_patches_boundary_invariant: after any apply_patches call with an exchange component, a boundary marker exists at end of exchange
  • (aspirational) reposition_boundary_removes_stale: multiple stale boundaries are reduced to exactly one at end of exchange

Structs§

ComponentInfo
Per-component info for plugin rendering.
PatchBlock
A parsed patch directive from an agent response.
TemplateInfo
Template info output for plugins.

Functions§

annotate_exchange_headings_against_baseline
apply_patches_pure
Apply patch blocks to a document’s components.
apply_patches_with_overrides_pure
Apply patches with per-component mode overrides (e.g., stream mode forces “replace” for cumulative buffers even on append-mode components like exchange).
deleted_conversation_tail_cleanup
Return after when it is exactly before with one safe escaped exchange tail outside agent:exchange deleted.
exchange_baseline_headings
Extract the set of stripped ### Re: heading lines from the exchange component of a document. Used by the commit path to build a baseline of headings already present in git HEAD so the reposition step can mark all new-this-cycle headings (not just the last one) with (HEAD).
guard_no_conversation_tail_outside_exchange
Fail closed when conversation content (prompts or response headings) would land outside the live <!-- agent:exchange --> block. This is a write-path guard — explicit agent-doc repair uses repair_conversation_tail_outside_exchange instead.
guard_no_duplicate_prompt_residue_outside_exchange
Fail closed when prompt text already recorded in agent:exchange still appears as freeform Markdown after the exchange close marker.
normalize_editor_visible_template_structure
Normalize the structural template invariants required before an editor IPC client mutates the visible document.
parse_patches
Parse <!-- patch:name -->...<!-- /patch:name --> blocks from an agent response.
remove_duplicate_answered_exchange_prompt_tail
Remove a prompt tail after the latest exchange boundary when that tail is an exact duplicate of a prompt block already answered earlier in the exchange.
remove_post_exchange_duplicate_prompt_comments
remove_post_exchange_duplicate_prompt_comments_preserving
Like remove_post_exchange_duplicate_prompt_comments, but keeps duplicate- looking comment lines that were already present in preserve_doc.
remove_post_exchange_duplicate_prompt_comments_preserving_docs
Like remove_post_exchange_duplicate_prompt_comments_preserving, but accepts several ownership-proof documents and preserves the union of their ordinary post-exchange comment lines.
repair_conversation_tail_outside_exchange
Repair the safe malformed-template case where conversation content escaped below <!-- /agent:exchange --> and now trails the document after sibling components like pending/todo.
repair_duplicate_exchange_close_mixed_scaffold_tail
Repair the malformed-template case where a duplicated scaffold contains safe live conversation text before the stray second exchange close marker.
repair_duplicate_exchange_close_scaffold
Repair the malformed-template case where a duplicate template scaffold is inserted between two <!-- /agent:exchange --> close markers.
repair_duplicate_exchange_close_tail
Repair the malformed-template case where a duplicate <!-- /agent:exchange --> lands after escaped conversation content.
repair_duplicate_exchange_opener
Merge duplicate <!-- agent:exchange --> openers into a single exchange block.
repair_prompt_tail_outside_exchange
Repair only prompt-target drift that escaped below <!-- /agent:exchange --> while leaving later markdown section separators outside the exchange block.
repair_queue_struck_items_escaped_below_marker
Remove struck queue items (- ~~…~~) that drifted BELOW the agent:queue closing marker into the inter-component parking lot (#queue-completed-items-escape-below-component).
reposition_boundary_to_end
Reposition the boundary marker to the end of the exchange component.
reposition_boundary_to_end_clean
Reposition the boundary marker without adding any (HEAD) annotations.
reposition_boundary_to_end_clean_with_id
Reposition boundary using an explicit boundary ID when provided.
reposition_boundary_to_end_clean_with_summary
Reposition boundary with an optional human-readable summary suffix, but do not add any (HEAD) heading annotations.
reposition_boundary_to_end_clean_with_summary_and_id
Like reposition_boundary_to_end_clean_with_summary but pins an explicit (deterministic) boundary ID instead of minting a fresh random one. The IPC patch builder seeds this from the stable patch_id so a single write’s socket / file / fallback rebuilds all carry the same boundary, preventing the plugin from appending the response twice (#finalize-visible-buffer-ipc-timeout-race).
reposition_boundary_to_end_preserve_head
Reposition boundary to end of exchange WITHOUT stripping (HEAD) markers.
reposition_boundary_to_end_preserve_head_with_id
Reposition boundary using an explicit ID, preserving (HEAD) markers.
reposition_boundary_to_end_with_baseline
Reposition boundary, with an optional set of baseline ### Re: headings (typically extracted from git HEAD). When a baseline is supplied, every ### Re: heading in the current exchange whose normalized text is NOT in the baseline is treated as “new this cycle” and receives a (HEAD) suffix. Headings already present in the baseline are stripped of any stale (HEAD) suffix. When the baseline is None, behavior matches the legacy annotate_latest_re_heading_with_head path: only the last ### Re: heading gets the marker.
reposition_boundary_to_end_with_summary
Reposition boundary with an optional human-readable summary suffix.
strip_conversation_tail_outside_exchange
Remove a safe escaped conversation tail below <!-- /agent:exchange -->.