Skip to content

refactor: make materializer a generic transformer — derive output dir from ABox template name #335

@jwulf

Description

@jwulf

Context

Follow-up to #333 / #334. PR #334 hoisted the scenario-template wiring into materializer/src/templateRegistry.ts as a single source of truth, but the registry still carries a hand-maintained outputDir per template name. That field encodes a materializer aesthetic (the short names edges/, entities/, runtime-entities/, state-transitions/) that has no ontology basis — the ABox in configs/<config>/ontology/scenario-templates.json only knows about name, appliesTo.kind, and steps[].

The remaining materializer-side knowledge should go too. The materializer should be a generic transformer: given an ABox row, derive everything mechanically.

Proposal: Option A — mirror the planner's layout

The planner already writes scenarios to generated/<config>/scenarios/templates/<TemplateName>/. The materializer mirrors it exactly:

generated/<config>/playwright/templates/EdgeLifecycle/<EdgeName>.lifecycle.spec.ts
generated/<config>/playwright/templates/EntityLifecycle/<EntityName>.lifecycle.spec.ts
generated/<config>/playwright/templates/UpdatedFieldVisibleOnReadBack/<RuntimeEntityName>.lifecycle.spec.ts
generated/<config>/playwright/templates/StateTransitionVisibleAfterAction/<RuntimeEntityName>.lifecycle.spec.ts

The output dir is template.name verbatim. No translation table anywhere; the relationship between scenario JSON and rendered spec is visible from the directory structure alone.

Considered and rejected:

  • Derive from appliesTo.kind alone — fails. Two RuntimeEntity templates (UpdatedFieldVisibleOnReadBack, StateTransitionVisibleAfterAction) would collide.
  • Keep short names via a compound key (e.g. appliesTo.kind + terminal Observe.expect) — works but re-encodes the materializer's aesthetic split inside a derivation function. Same problem, different shape.
  • Keep the registry — the status quo after refactor: hoist scenario-template registry out of materializer orchestrator #334. Forces every new template to be added in two places (ABox + registry).

Implementation sketch

  1. Delete materializer/src/templateRegistry.ts.
  2. materializer/src/index.ts reads the scenario-templates ABox via the existing loader and iterates abox.templates. For each row, the materializer reads scenarios/templates/<row.name>/ → emits to playwright/templates/<row.name>/.
  3. materializer/src/coverage.ts takes the ABox (or the iterated names) instead of templates: TemplateBinding[].
  4. Renderer dispatch stays as a single function for now (emitTemplateSuites for all four). If a second renderer appears later, key it on an explicit ABox renderer field — that's a separate, intentional design step.
  5. Rotate tests/codegen/template-registry.test.ts (or delete it) so the symmetry guard asserts "every ABox row maps to a unique on-disk directory" using row.name alone.

One-time cost (mechanical renames)

Path constants and doc references that mention the old short names need updating:

  • README.md L363 — playwright/edges/playwright/templates/EdgeLifecycle/
  • configs/camunda-oca/regression-invariants.test.ts — ~6 sites (L2330, L7883, L7899, L8110, L8708, L8729, L8750) with path constants like EDGES_SUITE_DIR and string literals 'state-transitions', etc.
  • materializer/src/playwright/templateEmitter.ts — JSDoc reference
  • path-analyser/src/types.ts — JSDoc reference

These are path renames only — no test logic changes, no scenario count changes, no L3 assertions reshape.

Acceptance criteria

  • TEMPLATE_REGISTRY is deleted.
  • The materializer reads the scenario-templates ABox and derives its output layout from template.name alone, with no template-name string literals in materializer code.
  • L3 invariants in configs/camunda-oca/regression-invariants.test.ts pass with the renamed paths (same count: +26 lifecycle suites, –73 suppressed).
  • Symmetry guard test asserts every ABox template name maps to a unique on-disk directory.
  • Full pre-push gate green; pipeline regenerates byte-identically to the new layout.

Branch hygiene

After #334 merges. Fresh branch off main (e.g. refactor/generic-materializer-335).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions