Skip to content

Mail template holes accept a second | formatter silently: {{ x | default:0 | number:2 }} renders the literal text '0 | number:2', and validate and lint pass it #26673

Closed

Description

@objectstack-fleet

Found by the hotclm app (objectstack-ai/hotclm#183) with EmailService.renderTemplate from @objectstack/plugin-email@18.2.0.

What happens. A mail hole takes one formatter. A chained hole {{ actual_amount | default:0 | number:2 }} is read as default with the argument 0 | number:2. It renders the literal text 0 | number:2 for null, and the bare unformatted value (71100) otherwise, in the subject, text and html faces. os validate and os lint both exit 0 on it with no diagnostic. Neither the formula parseHole nor plugin-email's FILTER_RE refuses the second pipe: each takes everything after the first colon as the argument.

Reach. Any app author who reaches for chaining, the natural spelling in most template languages, ships a notice that prints formatter syntax to recipients, and no gate tells them.

Expected. Either validate and lint refuse a hole with more than one |, naming the one-formatter rule and the 'branch in the caller' remedy, or the renderer supports chaining. The refusal is the smaller fix and matches the engine's stated data-only design.

Seam. spec email_template subject/body hole grammar → plugin-email template-engine render → formula formatValue.

Dedupe: searched mail template formatter chain / second pipe / hole argument. The nearest are #25082, #25189 and #25351 (hole placement encoding, a different rule). Sibling: #26660 (currency code literal-only).


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    area:i18nThe customer's own language, across UI, metadata and notifications
    on Oct 27, 2026
  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: audit line · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T13:02Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • Triage: lands in the mail-template hole parser (plugin-email); rationale: p3 · domain:server (lane merge: domain:services → domain:server) · area:i18n. A chained formatter is refused at os validate / save, with the refusal naming the one-formatter rule. Supporting chains would widen the template grammar and come back to triage. ⛔ Never the literal text.

    Generated by Claude Code

  3. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    on Oct 27, 2026
  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (family dispatch member; chain head #26660 carries the full claim and serial constraints)
    Session: session_01QSeKYmQXM3rHJcPnrEPk4B
    Account: os-justin
    Branch: claude/issue-26660-mail-hole-grammar
    Worktree: objectstack-issue-26660
    Domain: domain:server
    Seat: domain:server#3
    File surface: as the chain head #26660 declares (plugin-email and formula template engines, the mail-template validate rule if it is the producer) (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: "no path-derived mandate … default opus"), effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Responsibility: the hole parsers take everything after the first colon as the argument, so a second | is swallowed into it | none: os validate and os lint exit 0 on the chained hole | any mail-template author; hotclm hit it on 18.2.0
    Thread-read: 6442560667
    Premise: holds (seat re-check on origin/main 74db43ef: FILTER_RE as quoted on #26660's claim; no second-pipe refusal)
    Serial constraints cleared: see #26660's claim.

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #26799 comment 6450350754 · contract review PASS 6450017524 · domain:server seat 3 · 2026-10-27T18:30Z. Landing waits on CI green at 898b6a1f.

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:server seat 3 · 2026-10-27T19:13Z

    reading value
    PR #26799 merged through the queue, merge commit dabe77182
    this card closed completed by the PR's Fixes line

    State: pm:dispatched removed in the same pass. #26803 (spec lane, Blocked-by: #26673) is now unblocked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions