v0.1.2 — render() v1 (folder + file + heading)
Implement the address-rendering function from Ch 22 §3 — the single coupling point between the recipe and the vault layout. Pure function; deterministic; vault-independent at Pass 1; produces an Address per the v0.1 schema spec. Wires three of the five mechanisms (folder, file, heading); leaves tag and wikilink schema-reserved for v0.2.
Status
Section titled “Status”✅ Done (2026-05-05). Pure render(Recipe, ConceptIdentity) → Address shipped with folder/file/heading mechanisms wired; tag/wikilink layout-mechanisms throw informative “deferred to v0.2” errors; 7-filter closed pipeline; 37 unit tests + 6 E2E tests (including determinism: 100 unit iterations + 50 E2E iterations all producing byte-identical output).
Dependencies
Section titled “Dependencies”- v0.1.1 — Type system + validation foundation (typed
RecipeandConceptIdentityinterfaces required)
Blocks: v0.1.3 (generation-engine integration) — without render(), the engine has no recipe-driven layout to call
In:
render(recipe, identity) → Addresspure function- Mechanisms wired:
folder,file,heading(Ch 22 §10 v0.1 scope) - Template grammar: R2RML-style
{var}interpolation - Filter set:
lower,upper,title,slug,tagsafe,fs-safe,truncate(N)(Ch 22 §3.3) - Templates referencing source-level scope variables (e.g.
{control.id},{family.title}) - Pass 1 only — vault-independent, deterministic, hashable
Out:
- Mechanisms
tagandwikilinkas layout levels (validators warn “deferred to v0.2”) graph_edges(validators warn “deferred to v0.2”)- Pass 2 link minimizer (
linkStyle: shortest) — deferred to v0.3 - Auto-generated folder-tag-sync rules — Ch 22 §4.2; arrives with v0.2 tag mechanism
Concrete tasks
Section titled “Concrete tasks”- New
src/render/index.ts— exportsrender(recipe, identity, sourceScope) → Address - New
src/render/template.ts— implements{var}interpolation + filter pipeline; closed filter set - Per-mechanism modules:
src/render/mechanisms/folder.ts,file.ts,heading.ts - Stubs:
src/render/mechanisms/tag.ts,wikilink.ts— throw “not yet implemented; coming in v0.2” when referenced by a recipe in v0.1 - Pass-1 invariant: same
(recipe, identity, sourceScope)always produces byte-identicalAddressoutput - Unit tests against the 3 worked NIST 800-53 examples in
spec/recipe.schema.json(nist-80053r5-allfolders,nist-80053r5-mostly-headings,nist-80053r5-hybrid) - Determinism test — call
render()1000 times with the same inputs; assert identical output every time - Property test — for any valid recipe + identity,
Address.primary.pathsurvives JSON serialization round-trip
Success criteria
Section titled “Success criteria”- All 3 worked NIST examples produce expected
Addressoutputs (paths, anchors, frontmatter) - Determinism test passes — no hidden timestamps, no
Date.now()leaking into render output - Filter set closed — calling
{var|unknown_filter}throws a recipe-validation error, not a silent miss - Mechanism stubs (tag, wikilink) fail fast with informative error mentioning “v0.2”
- Test coverage > 90% for
src/render/
Files to touch
Section titled “Files to touch”src/render/index.ts— newsrc/render/template.ts— newsrc/render/mechanisms/{folder,file,heading,tag,wikilink}.ts— newsrc/render/types.ts— new (Address, RenderResult)tests/render.test.ts— newtests/render-determinism.test.ts— new
Open questions
Section titled “Open questions”- How to scope
sourceScope(the{control.id}/{family.title}resolution context)? Likely aMap<string, unknown>populated by the source iteration step (upstream of render) - What does
render()do when a template references an undefined variable? Throw vs. emit empty string vs. leave literal{var}in output
Related
Section titled “Related”Concept pages:
- Hierarchy primitives — the 5 mechanisms (folder, file, heading, tag, wikilink) render() dispatches over
- ETL and import (schema-as-primitive) — why render() is the only coupling point between recipe and vault
- Terminology — render(), Address, mechanism, template, ConceptIdentity, SourceScope
- What makes Crosswalker unique — the closed 5-mechanism grammar is the architectural distinguisher
Agent context:
- v0.1 schema spec — Address shape, recipe shape
- Vision — runtime-agnostic recipe schema; render() is the canonical reference implementation
- Tradeoffs — closed grammar vs. open extensibility
Design decisions (synthesis logs):
- Ch 22 synthesis (target-structure expressivity) — recipe grammar + render() function spec; the design contract this milestone implements
- Ch 23 synthesis (TS in-plugin engine) — why render() is TypeScript and runs in-plugin
- v0.1 import-engine design (2026-05-04) — design-phase summary
- v0.1.2 delivery log (2026-05-05) — what shipped + system-design diagram
Research deliverables:
- Ch 22 deliverable (target-structure expressivity) — §3 has the formal render signature
- Ch 23 deliverable (bundle/engine/language) — chooses TS + AJV + JSONata
- Ch 20 deliverable c (RML retargeted) — R2RML lineage of the
{var|filter}template grammar
Spec & schema files:
spec/recipe.schema.json— layout_entry, mechanism, template$defsspec/tier1.schema.json— what render() ultimately produces
Other milestones:
- v0.1.1 — Type system + validation foundation — dependency
- v0.1.3 — Generation engine integration — what render() unblocks
- v0.1.4 — Junction notes + crosswalk edges — kind dispatch extends render()
- Milestone hub