289 lines
10 KiB
Markdown
289 lines
10 KiB
Markdown
<!--
|
|
SYNC IMPACT REPORT
|
|
==================
|
|
Version change: [TEMPLATE — no prior version] → 1.1.0
|
|
Ratified: 2026-05-01 | Last amended: 2026-05-02
|
|
|
|
Principles introduced (first population from docs/CONSTITUTION.md):
|
|
- §2 Architecture Principles (6 sub-principles)
|
|
- §3 API Design (4 sub-principles)
|
|
- §4 Data Model Principles (4 items — fixed duplicate §4.3 → §4.4)
|
|
- §5 Testing Discipline (4 sub-principles)
|
|
|
|
Sections introduced:
|
|
- §1 Project Identity
|
|
- §6 Tech Stack Constraints
|
|
- §7 Developer Experience
|
|
- §8 Scope Boundaries (v1)
|
|
- §9 Governance (new — derived from preamble + standard governance rules)
|
|
- §10 Revision Log
|
|
|
|
Templates updated:
|
|
✅ .specify/memory/constitution.md — this file
|
|
✅ .specify/templates/tasks-template.md — removed "Tests are OPTIONAL" language
|
|
that contradicted §5.1 TDD mandate
|
|
⚠ .specify/templates/plan-template.md — Constitution Check section references
|
|
"[Gates determined based on constitution file]";
|
|
no structural change needed, gates are
|
|
resolved at plan-generation time
|
|
⚠ .specify/templates/spec-template.md — no conflicts found; no update needed
|
|
|
|
Deferred TODOs:
|
|
- None. All placeholders resolved.
|
|
-->
|
|
|
|
# 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:
|
|
|
|
```json
|
|
{ "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
|
|
- 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:
|
|
1. An explicit rationale entry in the Revision Log (§10).
|
|
2. A version bump following semantic versioning (see below).
|
|
3. 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 |
|
|
|
|
---
|
|
|
|
**Version**: 1.1.0 | **Ratified**: 2026-05-01 | **Last Amended**: 2026-05-02
|