|
| 1 | +<!-- |
| 2 | +SYNC IMPACT REPORT |
| 3 | +================== |
| 4 | +Version change: n/a → 1.0.0 (initial ratification — first fill of template) |
| 5 | +
|
| 6 | +Modified principles: n/a (all new) |
| 7 | +
|
| 8 | +Added sections: |
| 9 | + - I. Framework Independence |
| 10 | + - II. Component Lifecycle Discipline |
| 11 | + - III. Test-First Development |
| 12 | + - IV. Minimal Footprint / YAGNI |
| 13 | + - V. Security by Default |
| 14 | + - Development Standards |
| 15 | + - Adapter Contract |
| 16 | + - Governance |
| 17 | +
|
| 18 | +Removed sections: n/a |
| 19 | +
|
| 20 | +Template propagation: |
| 21 | + ✅ .specify/templates/tasks-template.md — updated "Tests are OPTIONAL" note |
| 22 | + to reflect Principle III (test-first is mandatory; waiver requires explicit |
| 23 | + documented rationale in the feature spec) |
| 24 | + ✅ .specify/templates/plan-template.md — Constitution Check stub updated to |
| 25 | + enumerate the five principle gates explicitly |
| 26 | + ✅ .specify/templates/spec-template.md — no structural changes required; |
| 27 | + existing mandatory sections already align with constitution requirements |
| 28 | +
|
| 29 | +Follow-up TODOs: |
| 30 | + - TODO(RATIFICATION_DATE): Using 2026-02-22 (first session this constitution |
| 31 | + was authored). Confirm this is acceptable or update to formal project date. |
| 32 | +--> |
| 33 | + |
| 34 | +# Component Framework Constitution |
| 35 | + |
| 36 | +## Core Principles |
| 37 | + |
| 38 | +### I. Framework Independence |
| 39 | + |
| 40 | +The `component_framework/core/` package MUST NOT import from any web framework |
| 41 | +(`fastapi`, `django`, `flask`, `litestar`, `jinjax`, `uvicorn`, or any adapter |
| 42 | +module). All framework-specific code belongs exclusively in `adapters/`. |
| 43 | + |
| 44 | +Every web framework adapter MUST be declared as an optional extras group in |
| 45 | +`pyproject.toml`. The only mandatory runtime dependency is `pydantic`. No user |
| 46 | +who installs the base package should receive framework code they did not |
| 47 | +explicitly request. |
| 48 | + |
| 49 | +No adapter module MAY import from another adapter module. |
| 50 | + |
| 51 | +**Rationale**: A Django developer must not receive FastAPI as a transitive |
| 52 | +dependency, and vice versa. Strict layering keeps the core independently |
| 53 | +testable, auditable, and portable to frameworks not yet written. |
| 54 | + |
| 55 | +### II. Component Lifecycle Discipline |
| 56 | + |
| 57 | +All components MUST: |
| 58 | +- Extend the `Component` base class. |
| 59 | +- Be registered via `@registry.register("name")`. |
| 60 | +- Manage state exclusively through `self.state` (a JSON-serializable `dict`). |
| 61 | +- Follow the canonical lifecycle: `mount → hydrate → handle_event → render → dehydrate`. |
| 62 | +- Name event handlers with the `on_<event>` convention. |
| 63 | + |
| 64 | +Components MUST NOT contain domain logic. Business rules belong in models and |
| 65 | +service classes; components are thin orchestration shells. |
| 66 | + |
| 67 | +**Rationale**: Consistent lifecycle ordering guarantees predictable state |
| 68 | +transitions across frameworks and prevents subtle rendering or state-corruption |
| 69 | +bugs caused by out-of-order hook execution. |
| 70 | + |
| 71 | +### III. Test-First Development (NON-NEGOTIABLE) |
| 72 | + |
| 73 | +Tests MUST be written and confirmed to fail before implementation begins. |
| 74 | +The Red-Green-Refactor cycle is mandatory. |
| 75 | + |
| 76 | +Components MUST be testable as pure Python — no live HTTP server, no real |
| 77 | +renderer, no database — using mock renderers and `ComponentTestCase`. |
| 78 | + |
| 79 | +The full test suite (`just test`) MUST pass before any PR is merged. No |
| 80 | +exceptions without a documented, reviewed waiver in the PR description. |
| 81 | + |
| 82 | +**Rationale**: Pure-Python testability is a direct consequence of Principle I. |
| 83 | +Enforcing it as a hard gate ensures the lifecycle interface and adapter |
| 84 | +boundaries remain clean and that regressions are caught immediately. |
| 85 | + |
| 86 | +### IV. Minimal Footprint / YAGNI |
| 87 | + |
| 88 | +New features MUST follow this layering order: |
| 89 | +`core/` → `adapters/` → `examples/` → `docs/` → `tests/` |
| 90 | + |
| 91 | +No abstraction layer, helper utility, or shared module may be introduced |
| 92 | +without at least two concrete, present use-cases in the codebase. The default |
| 93 | +answer to "should we add this?" is **no** until a second real need appears. |
| 94 | + |
| 95 | +Complexity deviating from this principle MUST be explicitly justified in a |
| 96 | +"Complexity Tracking" table in the feature's `plan.md`. |
| 97 | + |
| 98 | +**Rationale**: The library is embedded in downstream stacks. Every byte of |
| 99 | +unnecessary complexity compounds across every project that depends on it. |
| 100 | + |
| 101 | +### V. Security by Default |
| 102 | + |
| 103 | +The following controls are NON-NEGOTIABLE: |
| 104 | + |
| 105 | +- **CSRF**: All state-mutating endpoints MUST be CSRF-protected at the adapter |
| 106 | + layer. WebSocket connections require manual token validation (noted as a |
| 107 | + known limitation until automated). |
| 108 | +- **Input validation**: ALL user input MUST pass through a Pydantic schema |
| 109 | + before touching component state. Raw request data MUST NOT be placed into |
| 110 | + `self.state` directly. |
| 111 | +- **Client state**: State received from the client MUST be treated as |
| 112 | + untrusted. Re-validate on the server before acting. |
| 113 | +- **Permissions**: Every mutable endpoint MUST apply a permission check via |
| 114 | + `permission_classes` (CBV) or an FBV decorator. Unauthenticated access MUST |
| 115 | + return JSON 401/403 — never a redirect. |
| 116 | +- **Output escaping**: The renderer MUST escape output by default. Components |
| 117 | + MUST NOT produce raw HTML strings that bypass the renderer. |
| 118 | +- **Rate limiting**: Production deployments SHOULD apply `RateLimitMixin` to |
| 119 | + components that trigger side effects. |
| 120 | + |
| 121 | +**Rationale**: Components manage server-side state shared across requests. A |
| 122 | +compromised component can affect all users sharing that process, making |
| 123 | +defense-in-depth a hard requirement rather than a best-effort concern. |
| 124 | + |
| 125 | +## Development Standards |
| 126 | + |
| 127 | +These rules apply to all code in this repository regardless of feature or |
| 128 | +adapter: |
| 129 | + |
| 130 | +- **Formatter**: `ruff format` — line length 100, `quote-style = "double"` |
| 131 | +- **Linter**: `ruff check` — rule sets E, F, I, N, W, UP; no ignores without |
| 132 | + an inline comment explaining the rationale |
| 133 | +- **Type checker**: `ty` — public APIs MUST carry type hints; `ty` warnings |
| 134 | + are treated as errors in CI |
| 135 | +- **Docstrings**: REQUIRED for all public classes and public methods; OPTIONAL |
| 136 | + for private helpers |
| 137 | +- **Pre-commit gate**: `just check` (ruff + ty) MUST pass; enforced by |
| 138 | + pre-commit hooks via `prek` |
| 139 | +- **Commit discipline**: Each commit SHOULD represent one logical unit of work |
| 140 | + and pass `just check` independently |
| 141 | + |
| 142 | +## Adapter Contract |
| 143 | + |
| 144 | +A conforming adapter MUST provide: |
| 145 | + |
| 146 | +1. A `Renderer` subclass in `adapters/<framework>.py` implementing the |
| 147 | + `Renderer` interface from `core/renderer.py` |
| 148 | +2. An HTTP endpoint handler that dispatches events to the component registry |
| 149 | +3. (Optional) A WebSocket handler following the same event protocol as the |
| 150 | + HTTP handler |
| 151 | +4. An optional extras group in `pyproject.toml` |
| 152 | + (e.g., `[project.optional-dependencies] flask = [...]`) |
| 153 | +5. At least one working example in `examples/` |
| 154 | +6. pdoc-compatible docstrings on all public symbols |
| 155 | + |
| 156 | +No adapter MAY import from another adapter's module. |
| 157 | + |
| 158 | +## Governance |
| 159 | + |
| 160 | +This constitution supersedes all informal practices, README guidance, and |
| 161 | +prior conventions. Any amendment requires: |
| 162 | + |
| 163 | +1. A GitHub issue describing the proposed change and its rationale, opened |
| 164 | + before implementation begins. |
| 165 | +2. A version bump following semantic versioning: |
| 166 | + - **MAJOR**: Backward-incompatible governance change, principle removal, or |
| 167 | + redefinition that breaks existing compliant code. |
| 168 | + - **MINOR**: New principle or section added; materially expanded guidance. |
| 169 | + - **PATCH**: Clarifications, wording improvements, typo fixes, or |
| 170 | + non-semantic refinements. |
| 171 | +3. Updates to all dependent templates in `.specify/templates/` included in the |
| 172 | + same PR as the constitution change. |
| 173 | +4. A compliance review verifying no existing test, example, or core module |
| 174 | + violates the amended principles. |
| 175 | + |
| 176 | +All feature plans MUST include a Constitution Check section confirming |
| 177 | +compliance with Principles I–V before Phase 0 research begins, and again after |
| 178 | +Phase 1 design. |
| 179 | + |
| 180 | +**Version**: 1.0.0 | **Ratified**: 2026-02-22 | **Last Amended**: 2026-02-22 |
0 commit comments