Problem
marko/admin-panel currently ships 5 .latte template files under resources/views/. A user installing marko/view-twig instead of marko/view-latte would hit TemplateNotFoundException when admin routes try to render — the templates don't match the configured extension.
This couples a core UI package to a specific template engine. It doesn't scale: every UI package the framework ships faces the same problem, and there's no canonical way to fix it.
Solution
Establish the marko/{module}-{engine} sibling-package pattern:
marko/admin-panel — PHP only (controllers, services, routes). Engine-agnostic.
marko/admin-panel-latte — .latte templates, requires marko/view-latte
marko/admin-panel-twig — .twig templates (hand-translated to equivalent HTML), requires marko/view-twig
Siblings declare "extra": { "marko": { "templates_for": "marko/admin-panel" } }. Enhance ModuleTemplateResolver to honor this metadata — when resolving admin-panel::path, it searches both the parent module AND any sibling declaring templates_for. Controllers continue to call $this->view->render('admin-panel::dashboard/index', ...) — the abstraction stays clean.
The right templates get selected at runtime based on view.extension config: if extension is .twig, resolver looks for auth/login.twig, only admin-panel-twig provides it; if .latte, only admin-panel-latte provides it. Installing both is harmless (just wasteful disk space) — no Composer conflict declaration needed, no DI-level conflict.
Parity enforcement
Add marko/view/known-engines.php registering the core engines (Twig, Latte) and a CrossEngineTemplateParityTest that walks all marko/* packages, finds those declaring templates_for, and asserts every entry in known-engines.php has a corresponding sibling for each parent module. Adopting a new core engine becomes a real commitment: the build fails until you ship templates for every existing UI module.
Pattern documentation
Add a new section to .claude/architecture.md documenting the marko/{module}-{engine} pattern, the templates_for composer-extra key, when to use the pattern (reusable UI packages) vs. when not (single-application modules).
Out of scope (separate concerns)
Implementation
Plan written and ready: .claude/plans/admin-panel-engine-siblings/ on branch feature/admin-panel-engine-siblings. 10 tasks, ~4 parallel batches. Devil's-advocate review applied. Depends on PR #92 (drop view-conflicts) merging first.
Problem
marko/admin-panelcurrently ships 5.lattetemplate files underresources/views/. A user installingmarko/view-twiginstead ofmarko/view-lattewould hitTemplateNotFoundExceptionwhen admin routes try to render — the templates don't match the configured extension.This couples a core UI package to a specific template engine. It doesn't scale: every UI package the framework ships faces the same problem, and there's no canonical way to fix it.
Solution
Establish the
marko/{module}-{engine}sibling-package pattern:marko/admin-panel— PHP only (controllers, services, routes). Engine-agnostic.marko/admin-panel-latte—.lattetemplates, requiresmarko/view-lattemarko/admin-panel-twig—.twigtemplates (hand-translated to equivalent HTML), requiresmarko/view-twigSiblings declare
"extra": { "marko": { "templates_for": "marko/admin-panel" } }. EnhanceModuleTemplateResolverto honor this metadata — when resolvingadmin-panel::path, it searches both the parent module AND any sibling declaringtemplates_for. Controllers continue to call$this->view->render('admin-panel::dashboard/index', ...)— the abstraction stays clean.The right templates get selected at runtime based on
view.extensionconfig: if extension is.twig, resolver looks forauth/login.twig, only admin-panel-twig provides it; if.latte, only admin-panel-latte provides it. Installing both is harmless (just wasteful disk space) — no Composerconflictdeclaration needed, no DI-level conflict.Parity enforcement
Add
marko/view/known-engines.phpregistering the core engines (Twig, Latte) and aCrossEngineTemplateParityTestthat walks allmarko/*packages, finds those declaringtemplates_for, and asserts every entry inknown-engines.phphas a corresponding sibling for each parent module. Adopting a new core engine becomes a real commitment: the build fails until you ship templates for every existing UI module.Pattern documentation
Add a new section to
.claude/architecture.mddocumenting themarko/{module}-{engine}pattern, thetemplates_forcomposer-extra key, when to use the pattern (reusable UI packages) vs. when not (single-application modules).Out of scope (separate concerns)
composer.jsonsuggestblock updates for the new packages — handled by issue Centralize driver registries with known-drivers.php pattern #89 / PR feat: centralize driver registries with known-drivers.php pattern #91 (known-drivers-registry) task 025Implementation
Plan written and ready:
.claude/plans/admin-panel-engine-siblings/on branchfeature/admin-panel-engine-siblings. 10 tasks, ~4 parallel batches. Devil's-advocate review applied. Depends on PR #92 (drop view-conflicts) merging first.