Skip to content

Commit ad41fa4

Browse files
fsecada01claude
andcommitted
docs: ratify project constitution v1.0.0 and propagate to speckit templates
Adds the .specify/ speckit directory with: - constitution.md v1.0.0: five principles (Framework Independence, Component Lifecycle Discipline, Test-First, YAGNI, Security by Default) plus Development Standards, Adapter Contract, and Governance sections - plan-template.md: Constitution Check stub replaced with an 11-row gate table covering all five principles - tasks-template.md: "Tests OPTIONAL" language updated to reflect the mandatory test-first requirement from Principle III - All supporting speckit templates and PowerShell setup scripts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7081ec0 commit ad41fa4

12 files changed

Lines changed: 1864 additions & 0 deletions

.specify/memory/constitution.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env pwsh
2+
3+
# Consolidated prerequisite checking script (PowerShell)
4+
#
5+
# This script provides unified prerequisite checking for Spec-Driven Development workflow.
6+
# It replaces the functionality previously spread across multiple scripts.
7+
#
8+
# Usage: ./check-prerequisites.ps1 [OPTIONS]
9+
#
10+
# OPTIONS:
11+
# -Json Output in JSON format
12+
# -RequireTasks Require tasks.md to exist (for implementation phase)
13+
# -IncludeTasks Include tasks.md in AVAILABLE_DOCS list
14+
# -PathsOnly Only output path variables (no validation)
15+
# -Help, -h Show help message
16+
17+
[CmdletBinding()]
18+
param(
19+
[switch]$Json,
20+
[switch]$RequireTasks,
21+
[switch]$IncludeTasks,
22+
[switch]$PathsOnly,
23+
[switch]$Help
24+
)
25+
26+
$ErrorActionPreference = 'Stop'
27+
28+
# Show help if requested
29+
if ($Help) {
30+
Write-Output @"
31+
Usage: check-prerequisites.ps1 [OPTIONS]
32+
33+
Consolidated prerequisite checking for Spec-Driven Development workflow.
34+
35+
OPTIONS:
36+
-Json Output in JSON format
37+
-RequireTasks Require tasks.md to exist (for implementation phase)
38+
-IncludeTasks Include tasks.md in AVAILABLE_DOCS list
39+
-PathsOnly Only output path variables (no prerequisite validation)
40+
-Help, -h Show this help message
41+
42+
EXAMPLES:
43+
# Check task prerequisites (plan.md required)
44+
.\check-prerequisites.ps1 -Json
45+
46+
# Check implementation prerequisites (plan.md + tasks.md required)
47+
.\check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks
48+
49+
# Get feature paths only (no validation)
50+
.\check-prerequisites.ps1 -PathsOnly
51+
52+
"@
53+
exit 0
54+
}
55+
56+
# Source common functions
57+
. "$PSScriptRoot/common.ps1"
58+
59+
# Get feature paths and validate branch
60+
$paths = Get-FeaturePathsEnv
61+
62+
if (-not (Test-FeatureBranch -Branch $paths.CURRENT_BRANCH -HasGit:$paths.HAS_GIT)) {
63+
exit 1
64+
}
65+
66+
# If paths-only mode, output paths and exit (support combined -Json -PathsOnly)
67+
if ($PathsOnly) {
68+
if ($Json) {
69+
[PSCustomObject]@{
70+
REPO_ROOT = $paths.REPO_ROOT
71+
BRANCH = $paths.CURRENT_BRANCH
72+
FEATURE_DIR = $paths.FEATURE_DIR
73+
FEATURE_SPEC = $paths.FEATURE_SPEC
74+
IMPL_PLAN = $paths.IMPL_PLAN
75+
TASKS = $paths.TASKS
76+
} | ConvertTo-Json -Compress
77+
} else {
78+
Write-Output "REPO_ROOT: $($paths.REPO_ROOT)"
79+
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
80+
Write-Output "FEATURE_DIR: $($paths.FEATURE_DIR)"
81+
Write-Output "FEATURE_SPEC: $($paths.FEATURE_SPEC)"
82+
Write-Output "IMPL_PLAN: $($paths.IMPL_PLAN)"
83+
Write-Output "TASKS: $($paths.TASKS)"
84+
}
85+
exit 0
86+
}
87+
88+
# Validate required directories and files
89+
if (-not (Test-Path $paths.FEATURE_DIR -PathType Container)) {
90+
Write-Output "ERROR: Feature directory not found: $($paths.FEATURE_DIR)"
91+
Write-Output "Run /speckit.specify first to create the feature structure."
92+
exit 1
93+
}
94+
95+
if (-not (Test-Path $paths.IMPL_PLAN -PathType Leaf)) {
96+
Write-Output "ERROR: plan.md not found in $($paths.FEATURE_DIR)"
97+
Write-Output "Run /speckit.plan first to create the implementation plan."
98+
exit 1
99+
}
100+
101+
# Check for tasks.md if required
102+
if ($RequireTasks -and -not (Test-Path $paths.TASKS -PathType Leaf)) {
103+
Write-Output "ERROR: tasks.md not found in $($paths.FEATURE_DIR)"
104+
Write-Output "Run /speckit.tasks first to create the task list."
105+
exit 1
106+
}
107+
108+
# Build list of available documents
109+
$docs = @()
110+
111+
# Always check these optional docs
112+
if (Test-Path $paths.RESEARCH) { $docs += 'research.md' }
113+
if (Test-Path $paths.DATA_MODEL) { $docs += 'data-model.md' }
114+
115+
# Check contracts directory (only if it exists and has files)
116+
if ((Test-Path $paths.CONTRACTS_DIR) -and (Get-ChildItem -Path $paths.CONTRACTS_DIR -ErrorAction SilentlyContinue | Select-Object -First 1)) {
117+
$docs += 'contracts/'
118+
}
119+
120+
if (Test-Path $paths.QUICKSTART) { $docs += 'quickstart.md' }
121+
122+
# Include tasks.md if requested and it exists
123+
if ($IncludeTasks -and (Test-Path $paths.TASKS)) {
124+
$docs += 'tasks.md'
125+
}
126+
127+
# Output results
128+
if ($Json) {
129+
# JSON output
130+
[PSCustomObject]@{
131+
FEATURE_DIR = $paths.FEATURE_DIR
132+
AVAILABLE_DOCS = $docs
133+
} | ConvertTo-Json -Compress
134+
} else {
135+
# Text output
136+
Write-Output "FEATURE_DIR:$($paths.FEATURE_DIR)"
137+
Write-Output "AVAILABLE_DOCS:"
138+
139+
# Show status of each potential document
140+
Test-FileExists -Path $paths.RESEARCH -Description 'research.md' | Out-Null
141+
Test-FileExists -Path $paths.DATA_MODEL -Description 'data-model.md' | Out-Null
142+
Test-DirHasFiles -Path $paths.CONTRACTS_DIR -Description 'contracts/' | Out-Null
143+
Test-FileExists -Path $paths.QUICKSTART -Description 'quickstart.md' | Out-Null
144+
145+
if ($IncludeTasks) {
146+
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Out-Null
147+
}
148+
}

0 commit comments

Comments
 (0)