Architecture decisions
Three records pin the public shape of the library. They are referenced from the README and inline source comments, and they are the document a contributor reads before changing anything load-bearing.
ADR-0001 · Default delimiter is {{var}}
Why double-brace, not single-brace, not ${...}, not $var. The decision turns on three weights, in order:
- JSON-output collision safety — single-brace silently matches identifiers inside JSON examples.
- Convention alignment — LangChain, BAML, OpenAI's prompt cookbook, Anthropic's prompt library all use
{{var}}. - Reader-side discoverability — a
{{usrName}}typo in the IDE reads as a typed prompt-template error to anyone who has touched a prompt library.
ADR-0002 · Pluggable-parser architecture
Three coordinated layers — a single Compiled<Strings, Open, Close> generic, the makePromptTag({ open, close }) factory, and pre-applied subpath exports for the common cases. Subpath namespace is reserved for behaviour variants only — no tprompt/compat, no module-system flavours.
Includes the implementation note on why this is a function call (prompt('...')) and not a tagged template — TypeScript does not preserve literal types in tagged-template strings inference (microsoft/TypeScript#47660, open since 2021).
ADR-0003 · ESM/CJS interop boundary
ESM source, dual-publish, conditional exports. The five non-negotiable invariants the boundary depends on — and what each of them is buying. Adding any of {module-level cache, instanceof check, Symbol registry, top-level await, module-init work} is a structural change, not a feature change.