Due to the introduction of image thumbnail generation in
cd89ba5dea, the scope boundaries in §10 of
the project constitution should be updated with a clarification.
11 KiB
Reaction Image Board — Project Constitution
This document is the authoritative source of project-wide principles. Every spec, plan, and task produced for this project MUST comply with it. Contradictions between a spec/plan/task and this constitution are resolved in favour of the constitution. Changes to the constitution require an explicit revision with a noted rationale.
1. Project Identity
A personal, self-hosted web application for uploading, tagging, and searching reaction images. The system is split into two independently deployable units:
- API — Python / FastAPI
- UI — Angular (standalone SPA, communicates with the API over HTTP)
There is no server-side rendering. The UI is a pure client that consumes the API. These two units may live in the same repository but are treated as separate deployable artifacts with separate dependency manifests.
2. Architecture Principles
2.1 Strict separation of concerns
The API knows nothing about the UI framework. The UI knows nothing about storage or database implementation details. All cross-unit communication is through versioned HTTP contracts (OpenAPI spec is the source of truth).
2.2 Dependency direction
UI → API → Storage (S3-compatible)
→ Database (PostgreSQL)
No layer MAY import or directly call a layer above it in this diagram.
2.3 Storage abstraction
All file I/O goes through a StorageBackend interface. The initial
implementation is S3-compatible (MinIO locally, real S3 in production).
No code outside the storage module MAY reference bucket names, S3 URLs,
or SDK-specific types directly — only the interface contract.
2.4 Auth abstraction (progressive)
Authentication is treated as a pluggable backend from day one, even though
Phase 1 ships with no auth. The API MUST route all request-identity resolution
through a single AuthProvider interface. The no-op provider (Phase 1) returns
a static anonymous identity. Adding username/password or OIDC in a later phase
MUST be a new provider implementation, not a rewrite of business logic.
Phase 1 implements: no-auth (localhost only). Planned phases: username/password, then OIDC. The constitution acknowledges all three; the spec governs which is built.
2.5 Database abstraction
PostgreSQL is the Phase 1 database. All DB access MUST go through a repository layer (one repository class per domain aggregate). Raw SQL or an ORM is acceptable, but no query logic MAY live outside a repository. This makes the planned PostgreSQL → SQLite refactor a repository-layer change only.
2.6 No speculative abstraction
Interfaces are introduced only when a second implementation either exists or is explicitly planned in the constitution (StorageBackend, AuthProvider, repositories). Everything else is concrete until a real second implementation is needed. "Just in case" generics, base classes, or plugin systems are prohibited.
3. API Design
3.1 Versioning
All API routes MUST be prefixed /api/v1/. Breaking changes require a new
version prefix. Adding fields to responses is non-breaking; removing or
renaming is breaking.
3.2 OpenAPI as contract
The FastAPI-generated OpenAPI schema is the binding contract between API and UI. The UI MUST NOT depend on behaviour not described in the schema.
3.3 Error shape
All error responses MUST use a consistent envelope:
{ "detail": "<human-readable message>", "code": "<machine-readable code>" }
The code field is required. Naked HTTP status codes with empty bodies are
prohibited.
3.4 Pagination
Any endpoint that returns a list MUST support cursor- or offset-based pagination from the start. No endpoint MAY return an unbounded list.
4. Data Model Principles
4.1 Tags are lowercase, normalised strings
Tags are stored and matched case-insensitively. The API MUST normalise tag input to lowercase and strip whitespace before persistence. The UI may display tags in any case but MUST send them lowercase.
4.2 Images are immutable after upload
Once an image is stored, its file content is never replaced. The stored object MUST NOT be mutated.
4.3 Deduplication by content hash
Every image MUST be hashed (SHA-256) on upload before storage. If a hash already exists in the database, the upload is rejected or the existing record is returned — the spec will decide the UX — but no duplicate object is written to storage. The hash is stored on the image record and may also serve as the storage object key.
4.4 Tag search is AND logic in v1
A search for tags [cat, funny] returns only images that have both tags.
OR/NOT logic is explicitly out of scope until the constitution is revised.
5. Testing Discipline
5.1 TDD is non-negotiable
No production code MAY be written before a failing test exists for it. This applies to both API and UI. Tasks MUST include a "write failing test" step before any implementation step.
5.2 Test pyramid
- Unit tests — pure logic, repository mocks, no I/O
- Integration tests — API routes tested against a real (test) database and a real (test) S3-compatible bucket (e.g. MinIO in Docker)
- E2E tests — Angular + API, minimal set covering the core happy paths
Unit and integration tests are required. E2E tests are best-effort in v1.
5.3 Tests live next to the code they test
API tests in api/tests/, UI tests colocated with their components. No
separate top-level tests/ directory that mirrors the source tree.
5.4 CI must pass before any task is considered done
"Done" means: all tests pass, linter passes, type checker passes. A task MUST NOT be marked complete while CI is failing.
6. Tech Stack Constraints
| Concern | Choice | Rationale |
|---|---|---|
| API language | Python 3.12+ | Primary language, type hints required |
| API framework | FastAPI | Async, OpenAPI-native |
| ORM / query | SQLAlchemy 2.x (async) + asyncpg driver | Repository layer owns all queries |
| DB migrations | Alembic | Schema changes tracked in version control |
| Object storage | S3-compatible via boto3 / aiobotocore |
Swap MinIO ↔ S3 via env config |
| UI framework | Angular (latest stable) | Job-relevant, learning goal |
| UI language | TypeScript strict mode | No any, no implicit types |
| Containerisation | Docker + Docker Compose | Local dev must start with one command |
7. Developer Experience
7.1 One-command local start
docker compose up MUST start the full stack: PostgreSQL, MinIO, API, UI dev
server. No manual environment setup beyond copying .env.example.
7.2 Environment configuration
All environment-specific values (DB URL, S3 endpoint, bucket name, ports) MUST
come from environment variables. Hardcoded hostnames, credentials, or ports in
source code are prohibited. .env.example is committed; .env is gitignored.
7.3 Linting and formatting are not optional
API: ruff (lint + format). UI: eslint + prettier. Both are enforced in
CI. PRs/commits with lint failures MUST NOT be considered complete tasks.
8. Scope Boundaries (v1)
The following are explicitly out of scope for v1 and MUST NOT be designed for, abstracted toward, or mentioned in specs unless the constitution is revised:
- Multi-user support
- Public sharing or embeds
- Collections or albums beyond tag-based grouping
- Image editing or transformation beyond thumbnail generation
- OR/NOT tag logic
- Mobile-native app
- Username/password auth (planned Phase 2)
- OIDC auth (planned Phase 3)
9. Governance
This constitution supersedes all other project practices, conventions, and prior documentation. Where a conflict exists between a spec, plan, or task and this constitution, the constitution prevails.
Amendment procedure: Any change to this document MUST be accompanied by:
- An explicit rationale entry in the Revision Log (§10).
- A version bump following semantic versioning (see below).
- A review of all active specs and plans for compliance with the change.
Versioning policy:
- MAJOR — backward-incompatible governance change: a principle removed, redefined in a way that invalidates prior work, or a hard scope boundary moved inward.
- MINOR — new principle or section added, or materially expanded guidance that requires new compliance work.
- PATCH — clarifications, wording improvements, typo fixes, non-semantic refinements that do not change what is required.
Compliance review: Every new spec MUST include a "Constitution Check" gate (verified by the plan command) before Phase 0 research begins, and again after Phase 1 design is complete.
10. Revision Log
| Version | Date | Change |
|---|---|---|
| 1.0.0 | 2026-05-01 | Initial constitution |
| 1.1.0 | 2026-05-01 | asyncpg driver explicit; SHA-256 deduplication added to data model; deduplication removed from out-of-scope |
| 1.1.0 | 2026-05-02 | Adopted into Spec Kit memory; fixed duplicate §4.3 → §4.4; strengthened "should" language to MUST/MUST NOT; added §9 Governance |
| 1.1.1 | 2026-05-03 | Clarify that the only acceptable form of image transformation or editing is thumbnail generation |
Version: 1.1.1 | Ratified: 2026-05-01 | Last Amended: 2026-05-03