Skip to content

Refactor/tpl text template migration#109

Merged
josealekhine merged 9 commits into
mainfrom
refactor/tpl-text-template-migration
May 31, 2026
Merged

Refactor/tpl text template migration#109
josealekhine merged 9 commits into
mainfrom
refactor/tpl-text-template-migration

Conversation

@josealekhine
Copy link
Copy Markdown
Member

No description provided.

Add the design spec for migrating the multi-line block templates in
internal/assets/tpl/tpl_*.go off fmt.Sprintf format-string constants
to Go text/template + embedded files, and wire its reference into the
line-252 task.

Three tiers: (1) multi-line documents/scripts/config -> one embedded
file each, parsed at init into *template.Template handles (no magic
name literals at call sites); (2) the recall <details>/<table> HTML
assembly -> two data-driven block templates (metaTable, details) that
delete the scattered paired-tag constants; (3) single-line format
strings, pure positional joins, and the RecallListRow meta-format stay
fmt.Sprintf. No-panic init parse, gated by TestTemplatesParse.
Behavior-preserving, asserted by byte-for-byte golden tests.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
First slice of the tpl migration (task 252): stand up the rendering
pipeline and convert ObsidianReadme as the proof-of-pattern.

- internal/assets/tpl: tpl-local //go:embed templates/*, a Render
  helper generalizing message/render.go, and an init parse table.
  parseTemplate returns a non-nil template even on failure (recording
  the cause in parseErrs) so Render never panics on a nil handle;
  TestTemplatesParse turns a malformed template into a CI failure
  instead -- no template.Must, per the no-panic invariant.
- ObsidianReadme is now a *template.Template handle; obsidian/vault.go
  renders via tpl.Render(tpl.ObsidianReadme, tpl.ObsidianData{...}),
  passing a typed struct (no map-key literal) so the non-exempt caller
  stays magic-string clean.
- File split honors the audit: exported funcs in render.go, unexported
  loader in load.go, the data type in types.go.
- TestObsidianReadmeMatchesLegacy keeps the pre-migration format string
  verbatim and asserts byte-for-byte identical render output.
- Spec refined: embed is tpl-local (leaf package, cycle avoidance),
  not assets.FS.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…cision

Chunk 1 of the tpl migration (task 252): the four straightforward
interpolated block templates move to embedded files behind handles.

- New templates/{journal-site-readme.md,trigger-script.sh,learning.md,
  decision.md}.tmpl; the consts are deleted (tpl_trigger.go, which held
  only TriggerScript, is removed entirely).
- TriggerScript drops fmt's positional %[1]s/%[2]s for {{.Name}}/
  {{.Type}}; Decision drops the repeated-%s title for {{.Title}} twice
  -- both eliminate positional-argument fragility.
- Render returns (string, error), so the bare-string helpers it now
  feeds gain an error return: generate.SiteReadme, format.Learning,
  format.Decision, with callers (journal/cmd/site, entry/write)
  propagating. trigger/cmd/add renders inline; its fmt import is gone.
- Golden tests keep each legacy format string verbatim and assert
  byte-for-byte identical output.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Chunk 2 (task 252): ZensicalProject and ZensicalTheme were static
multi-line consts (zero interpolation), so they move to embedded
.toml files loaded verbatim as string vars at init via loadStatic
(no text/template parse). Call sites are unchanged -- a string var
swaps in for a string const transparently, so generate.ZensicalToml
output stays identical.

- Bodies extracted from the consts byte-for-byte (written by Go, not
  retyped); consts removed from tpl_journal.go (ZensicalExtraCSS, a
  one-liner, stays Tier-3).
- embed glob extended to templates/*.toml; loadStatic added (no parse).
- TestZensicalStaticLoaded pins the loaded blocks structurally; the
  generate package's ZensicalToml tests cover full output end-to-end.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Chunk 3 (task 252): the Ralph-loop bash script -- the composed
template -- moves to an embedded file.

- templates/loop-script.sh.tmpl absorbs LoopMaxIter as a {{if .MaxIter}}
  block (replacing the Go-side maxIterCheck pre-format) and inlines
  LoopNotify literally at both completion points; the LoopScript,
  LoopMaxIter, and LoopNotify consts are removed. LoopCmd*/Load* stay
  Tier-3 (script.go still selects the tool command and passes it as
  {{.AICommand}}).
- script.Generate returns (string, error); its sole caller
  (loop/cmd/root) propagates. filepath.Abs's error -- previously
  discarded -- is now handled, since the signature already carries one.
- Verified by golden fixtures captured from the legacy code path
  (testdata/*.golden): byte-for-byte across the iteration-cap on/off
  branch and each tool.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Chunk 4 (task 252, final): the recall formatter's <details>/<table>
blocks move from scattered paired-tag constants to two embedded block
templates, completing the migration.

- metaTable template ({Summary, Rows}) replaces MetaDetailsOpen +
  per-row MetaRow + MetaDetailsClose in source/format (metadata and
  token-stats tables): format.go builds a typed row slice (conditional
  Branch/Model/Parts rows) and renders once instead of scattering
  conditional Fprintf calls.
- details template ({Summary, Body}) replaces RecallDetailsOpen/Close
  and RecallPlanOpen/Close across three sites: the plan block, the
  collapsed tool-result block (format.go), and collapse.ToolOutputs.
  Seven paired-tag consts are deleted; PlanSummary stays (Tier-3).
- tpl.RenderOr added for best-effort string builders whose callers
  don't return errors (the recall formatter, the Import counter): it
  logs warn.TemplateRender and returns a fallback on the parse-gated-
  impossible error rather than contorting those signatures. The
  error-returning tpl.Render still serves chunks 1-3.
- Byte-for-byte goldens captured from the legacy code path cover the
  metadata table (all conditional rows), the plan block, fenced +
  collapsed tool results, and the collapse wrap path.

Closes task 252.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
The spec described only the error-returning Render. The implementation
added tpl.RenderOr for best-effort string builders (the recall
formatter, the Import counter) whose signatures should not grow an
error return for a parse-gated, unreachable branch: it logs
warn.TemplateRender and falls back, mirroring message/render.go.

Update the Rendering-helper subsection (both entry points), the Error
Handling table (by caller shape), the exec-error edge case, and add
Settled Decision 5 recording the split.

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Bring the non-error-handling sections in line with what shipped:

- Happy Path: templates load from the tpl-local embedded FS, not
  assets.FS (already stated in Approach + Decision 4).
- Whitespace-fidelity edge case: the templates reproduce exact bytes
  with plain {{range}}/{{if}} + literal newlines + no-trailing-newline
  files; no {{-/-}} trimming was needed.
- metaTable input is MetaTableData{Summary; Rows []MetaRow}.
- Files table: separate meta-table.html.tmpl / details.html.tmpl (not
  one blocks.tmpl); the tpl package is split render.go/load.go/
  static.go/types.go, and TestTemplatesParse is an in-package test
  reading parseErrs (no exported ParseErrors()).

Spec: specs/tpl-text-template-migration.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
@josealekhine josealekhine self-assigned this May 31, 2026
@josealekhine josealekhine merged commit 426c6c5 into main May 31, 2026
16 checks passed
@josealekhine josealekhine deleted the refactor/tpl-text-template-migration branch May 31, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant