Skip to content

feat: theme resolver (theme-keyed maps) and updated spec-kit documents - #4

Merged
tyrsson merged 13 commits into
1.0.xfrom
prototype/resolver
Oct 1, 2026
Merged

tyrsson merged 13 commits into
1.0.xfrom
prototype/resolver

Conversation

@tyrsson

@tyrsson tyrsson commented Oct 1, 2026

Copy link
Copy Markdown
Member

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

Description

Adds the theme resolver and brings the spec-kit documents up to date.

  • Webware\Theme\Resolver\ThemeResolver: a laminas-view ResolverInterface that answers an address from theme.themes[<theme>][<address>], with the active theme injected and default as the per-address fallback. No filesystem call per lookup.
  • ThemeResolverFactory and AggregateResolverFactory: the theme resolver is attached to the aggregate at priority 100, ahead of TemplateMapResolver (50) and the namespaced path stack (1). ConfigProvider must be merged after Mezzio\LaminasView\ConfigProvider.
  • ConfigProvider::getDependencies() stays public (from 1.0.x) and is tested directly.
  • specs/001-theme-resolution/decisions.md (new): current state, the owner's standing rules, the decision log D-001 to D-019, next actions and gotchas. spec.md, plan.md, tasks.md, data-model.md, research.md and quickstart.md are corrected to match what is built. New task phases: 10 (theme-aware asset helper), 11 (default-theme repository), 12 (template port), 13 (cross-repo).
  • .github/instructions/memory.instructions.md: handoff note pointing at decisions.md.

Not in this PR: the theme-aware asset() helper, asset publishing, the default theme assets, and the template port. They are tasks.md Phases 10 to 12.

Motivation and Context

The app and the component templates need a theme to be a lookup rather than a stack of paths. The prototype showed that a resolver registered in the aggregate gives that with no change to the renderer, and the webware application already depends on it (path repository pinned at the prototype commit).

How has this been tested?

mago format --check, lint, analyze and guard all exit 0. composer test: 8 tests, 8 assertions. composer test-integration: 5 tests, 9 assertions, against real theme directories. composer mutation-test: 8 mutants, all killed, MSI 100%.

Screenshots (if appropriate):

…me injected

The resolver prototype for Phase 2b: ThemeResolver takes theme => address => path maps and
the active theme name, resolves the active theme's entry for an address, falls back to
default per address, and returns false so the aggregate keeps walking. A factory reads
theme.active/theme.themes from configuration, so the active theme enters at construction.

AggregateResolverFactory states the order outright — theme 100, mezzio's map 50, path
stack 1 — because mezzio's own factory attaches the last two at equal priority, which
leaves their order to the queue. Composing that aggregate is also why
mezzio/mezzio-laminasviewrenderer becomes a dependency: NamespacedPathStackResolver is
its class. Whether that dependency is worth the guaranteed ordering is a finding for the
prototype to weigh.

Tests: unit covers hit, per-address fallback, unknown address, unknown active theme and
empty maps; integration uses real files and proves the theme wins an address mezzio's map
also carries, and that an address it lacks still resolves through the map.

Signed-off-by: Joey Smith <jsmith@webinertia.net>
Adds decisions.md (state, standing rules, decision log D-001 to D-019), brings spec, plan, tasks, data-model, research and quickstart in line with the built resolver, adds the asset helper, default theme and template port phases, and a handoff note.

Signed-off-by: Joey Smith <jsmith@webinertia.net>
Keeps the public getDependencies() from 1.0.x with the resolver factories, and tests it directly.

Signed-off-by: Joey Smith <jsmith@webinertia.net>
@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.62687% with 17 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Resolver/Container/ThemeResolverFactory.php 0.00% 11 Missing ⚠️
...rc/Resolver/Container/AggregateResolverFactory.php 0.00% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

Signed-off-by: Joey Smith <jsmith@webinertia.net>
Signed-off-by: Joey Smith <jsmith@webinertia.net>
Signed-off-by: Joey Smith <jsmith@webinertia.net>
…switches

Signed-off-by: Joey Smith <jsmith@webinertia.net>
Signed-off-by: Joey Smith <jsmith@webinertia.net>
Signed-off-by: Joey Smith <jsmith@webinertia.net>
AssetFactory builds the stock Asset helper from theme.assets, default first then the active theme, with per-name fallback. Relative values are served from /theme/<theme>/, absolute URLs pass through, and theme names must be a single path segment. ConfigProvider gains THEME, ACTIVE, THEMES and ASSETS constants.

Signed-off-by: Joey Smith <jsmith@webinertia.net>
Signed-off-by: Joey Smith <jsmith@webinertia.net>
feat: theme-aware asset helper and config key constants
Signed-off-by: Joey Smith <jsmith@webinertia.net>
@tyrsson
tyrsson merged commit b240139 into 1.0.x Oct 1, 2026
11 of 12 checks passed
@tyrsson
tyrsson deleted the prototype/resolver branch October 1, 2026 07:37
@tyrsson tyrsson mentioned this pull request Oct 1, 2026
4 tasks done
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