Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 95 additions & 35 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,110 @@
# [PROJECT_NAME] Constitution
<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->
# Webware Theme Constitution

## Core Principles

### [PRINCIPLE_1_NAME]
<!-- Example: I. Library-First -->
[PRINCIPLE_1_DESCRIPTION]
<!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries -->
### I. Library-First

### [PRINCIPLE_2_NAME]
<!-- Example: II. CLI Interface -->
[PRINCIPLE_2_DESCRIPTION]
<!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats -->
Every capability ships as a self-contained, independently testable library component. Components
MUST have a clear, singular purpose; organizational-only or kitchen-sink packages are prohibited.

### [PRINCIPLE_3_NAME]
<!-- Example: III. Test-First (NON-NEGOTIABLE) -->
[PRINCIPLE_3_DESCRIPTION]
<!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced -->
### II. Resolution by Lookup

### [PRINCIPLE_4_NAME]
<!-- Example: IV. Integration Testing -->
[PRINCIPLE_4_DESCRIPTION]
<!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas -->
Theme resolution is a lookup, never a path stack. The resolver reads the theme maps and returns a
template name or `false`; it performs no filesystem call:

### [PRINCIPLE_5_NAME]
<!-- Example: V. Observability, VI. Versioning & Breaking Changes, VII. Simplicity -->
[PRINCIPLE_5_DESCRIPTION]
<!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles -->
```text
maps[active][name] ?? maps['default'][name] ?? false
```

## [SECTION_2_NAME]
<!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. -->
- `ThemeResolver` is `final` and implements `ResolverInterface`.
- `ThemeResolverFactory` reads `theme.themes` and `theme.active`. The resolver never reads
configuration directly.
- `AggregateResolverFactory` attaches the theme resolver at priority 100,
`TemplateMapResolver` at 50, and the namespaced path stack at 1. The order is the contract: the
theme wins over the module map, and both win over the path stack.
- `ConfigProvider` MUST be merged after `Mezzio\LaminasView\ConfigProvider`. Merging before it
reorders the aggregate resolver chain and silently defeats it.

[SECTION_2_CONTENT]
<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. -->
### III. Consumer-Agnostic

## [SECTION_3_NAME]
<!-- Example: Development Workflow, Review Process, Quality Gates, etc. -->
The package provides theming; it does not know the application. It MUST NOT hard-couple to a
specific application or component. Configuration is contributed through `ConfigProvider` and
merged by the host, which is how the skeleton binds components (see the ecosystem composition
doctrine).

[SECTION_3_CONTENT]
<!-- Example: Code review requirements, testing gates, deployment approval process, etc. -->
### IV. Webware Quality Gates (NON-NEGOTIABLE)

Every change MUST pass the shared webware gates: Mago format, lint, analyze, and guard with no
silent suppression; PHPUnit 13 strict mode (coverage metadata, mock/stub split, `failOnNotice` /
`failOnDeprecation` / `failOnWarning`); and Infection mutation coverage at or above the configured
thresholds (MSI 95/95 from `webware-ci.json`). Test doubles follow PHPUnit 13 rules -
`createStub()` for value doubles, `createMock()` only with `expects()`.

Gates are checked by exit code, not by the last line of output.

### V. Naming & Compatibility

Namespace root is `Webware\Theme\`. Interface names end in `Interface`; trait names end in
`Trait`. A namespace segment never repeats its parent segment. Support only current supported PHP
versions (`~8.4.1 || ~8.5.0`). Named arguments are required in all code the package owns.

### VI. Asset Serving Contract

Assets are served from `/theme/<theme>/{css,js,img,fonts}`. They MUST NOT be served from a bare
`/<theme>/` path: `.htaccess` would let a directory shadow a route and the asset would become
unreachable.

The asset helper reads `view_helper_config.asset.resource_map` - not `view_manager.asset`. The
config key is part of the contract; consumers override it by key.

### VII. Cross-Platform Development Environment

Local tooling runs inside the persistent container (`docker compose exec tooling ...`) or the
VS Code Dev Container; the host is not required to carry a native PHP toolchain. `compose.yml` is
the single source of truth and the Dev Container is a thin wrapper over it. These files are kept in
sync with the central `webware/webware-tools` preset.

Agents working this repository run on the Windows host and drive the repository toolchain from
there; scripts invoked by tooling MUST be host-native.

### VIII. Reference Repositories Are Read-Only

`ims` (and any other repository named as reference-only) is READ-ONLY. It is a source of evidence
and original templates, never a place to write. Nothing in it is edited, moved, renamed, or
committed to, for any reason.

## Quality Gates

Every pull request passes, on all CI matrix legs:

- Mago format check, lint, analyze, guard
- Unit tests under lowest/locked/latest dependency strategies
- Integration tests where `run_integration` is set
- Codecov upload from the canonical coverage leg (report-only)
- Infection mutation score at or above the configured MSI thresholds

## Development Workflow

- Every change goes through a pull request. `1.0.x` is never pushed to directly. Releases and tags
belong to the owner.
- Each spec-driven step (constitution, specify, plan, tasks, implement, converge) lands as its own
branch and pull request.
- Commits use Conventional Commits and are signed off by the maintainer's real identity
(`git commit -s`, `Joey Smith <jsmith@webinertia.net>`). Merge commits by default.
- No file is deleted, renamed, or moved without owner approval.
- Form field names are never changed.
- Application code is never run while a debug session may be attached
(`ss -ltnp | grep -E ':(9000|9003)'`).

## Governance
<!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan -->

[GOVERNANCE_RULES]
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
- This constitution supersedes other practices; conflicts are resolved in its favor or the
constitution is amended via pull request.
- Amendments require a pull request that updates the version and Last Amended date.
- Every pull request is reviewed for compliance with the Core Principles.
- `.github/instructions/memory.instructions.md` carries the session handoff and operational rules
derived from this constitution. The full standing rules live in
`specs/001-theme-resolution/decisions.md` section 2 and
`vendor/webware/webware-tools/agent-working-agreements.md`.

**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
**Version**: 1.0.0 | **Ratified**: 2026-10-10 | **Last Amended**: 2026-10-10
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ single array read, with no filesystem walk.
[![PHP Version](https://img.shields.io/packagist/php-v/webware/webware-theme)](https://packagist.org/packages/webware/webware-theme)
[![Latest Version](https://img.shields.io/packagist/v/webware/webware-theme)](https://packagist.org/packages/webware/webware-theme)
[![License](https://img.shields.io/github/license/webinertia/webware-theme)](LICENSE)
[![Required CI](https://github.com/webinertia/webware-theme/actions/workflows/required/webinertia/.github/.github/workflows/org-required-ci.yml/badge.svg)](https://github.com/webinertia/webware-theme/actions/workflows/required/webinertia/.github/.github/workflows/org-required-ci.yml)
[![Continuous Integration](https://github.com/webinertia/webware-theme/actions/workflows/required/webinertia/.github/.github/workflows/continuous-integration.yml/badge.svg)](https://github.com/webinertia/webware-theme/actions/workflows/required/webinertia/.github/.github/workflows/continuous-integration.yml)
[![codecov](https://codecov.io/gh/webinertia/webware-theme/graph/badge.svg)](https://codecov.io/gh/webinertia/webware-theme)
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fwebinertia%2Fwebware-theme%2F1.0.x)](https://dashboard.stryker-mutator.io/reports/github.com/webinertia/webware-theme/1.0.x)

Expand Down