Skip to content

cardigann: parse templates once at load (parse-don't-validate lifecycle) #696

Description

@forkwright

Finding

The Cardigann engine parses every template twice: once at definition load for validation (template::validate / template::validate_row_scoped in crates/eksetasis/src/client/cardigann/template.rs), and again at render time (render_specs / the extractor path). The load-time functions return Result<(), _> and discard the parsed AST.

Evidence

  • crates/eksetasis/src/client/cardigann/template.rs:108 (validate) and :116 (validate_row_scoped) both call parse(template)? then discard the nodes.
  • Call sites in definition.rs:365,371,381,383 use them purely as validators; the definition struct stores raw template strings.
  • RUST/validate-returns-unit fires on validate_row_scoped (the validate_ prefix); the sibling validate escapes the pattern but has the same shallow shape.

Why this matters

Parse-don't-validate (RUST.md#validation-constructors): a parsed template is the constrained type. Storing parsed ASTs in the definition would make load-time validation produce the value the render path consumes — one parse, no re-parse per search, and invalid states unrepresentable past load. This is the same class as aitesis#301.

Desired correction

Refactor the definition lifecycle: CardigannDefinition stores parsed templates (or a ParsedTemplate newtype over the node AST) produced once at load; the render path consumes them. validate/validate_row_scoped fold into the parser.

Done when: templates parse exactly once per definition load; no validate_* function returns Result<(), _>; the .kanon-lint-ignore entry added under #513 is removed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorCode quality and architecture improvementstech-debtTechnical debt and cleanup

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions