Requirement & Relationship Schema
traceo intermediate 6 min read
ELI5
A requirement is a sticky note pinned on a five-shelf cabinet (L1-Strategic → L4-Verification). Relationships are coloured strings tied between sticky notes — every string has a known opposite colour, so pulling on one end automatically labels the other. Traceo stores both ends so a query from either side is a single index lookup.
Technical Deep Dive
Core Tables (migration 001_initial_schema.sql)
| Table | Purpose |
|---|---|
tenants | Top-level billing/isolation boundary |
workspaces | Per-tenant project scope (RLS pivot) |
users | Members, with role for RBAC |
requirements | The atomic unit; stores type, classification, priority, status |
relationships | Edges between requirements |
requirement_versions | Append-only history per requirement |
ingestion_jobs, job_issues | Engine state |
audit_logs | Append-only audit trail |
Requirement Enums
| Enum | Values |
|---|---|
requirement_type | business, functional, non_functional, system, implementation, verification |
requirement_classification | L1-Strategic, L2-Functional, L2-Performance, L3-System, L4-Implementation, L4-Verification |
requirement_priority | P0-critical, P1-high, P2-medium, P3-low |
requirement_status | draft → candidate → approved → active → implemented → verified, plus deprecated |
Relationship Types & Reverses
relationship_type is a single enum holding 15 values. The MCP service services/traceability.py keeps a static reverse map so that adding A implements B also writes B implemented_by A:
| Forward | Reverse |
|---|---|
implements | implemented_by |
satisfies | validates |
derives_from | derived_by |
verifies | verified_by |
depends_on | affected_by |
affects | affected_by |
supports | realized_by |
Asymmetric values (decomposes_into, conflicts_with, relates_to, constrains, constrained_by) have no reverse mapping and are stored only in the direction the caller supplied.
Class Diagram
classDiagram class Requirement { +UUID id +UUID workspace_id +text requirement_id +requirement_type type +requirement_classification classification +requirement_priority priority +requirement_status status +vector(1536) embedding } class Relationship { +UUID id +UUID source_id +UUID target_id +relationship_type type } class RequirementVersion { +UUID id +UUID requirement_id +int version +jsonb snapshot } Requirement "1" --> "*" Relationship : source Requirement "1" --> "*" Relationship : target Requirement "1" --> "*" RequirementVersionKey Terms
- Reverse map →
_REVERSE_RELATIONSHIPSinservices/traceability.py; converts a forward type to its symmetric counterpart. - Classification → fixed 5-layer hierarchy used for traceability matrix rendering.
- Workspace → the RLS pivot column carried on every domain row.
Q&A
Q: If I add A satisfies B, what edge does Traceo also write?
A: B validates A — the reverse of satisfies is validates per the static map.
Q: Which relationship types have no reverse?
A: decomposes_into, conflicts_with, relates_to, constrains, constrained_by — they fall through _reverse_relationship_type returning None, so only the supplied direction is persisted.
Q: Where is per-requirement history stored?
A: requirement_versions — an append-only table with a JSONB snapshot per version, distinct from audit_logs.
Examples
Calling add_link(source="REQ-1", target="REQ-2", type="implements") writes two rows in relationships: (REQ-1, REQ-2, implements) and (REQ-2, REQ-1, implemented_by). A subsequent trace_requirement("REQ-2") finds the inbound implemented_by edge without needing a reverse-direction index.
neighbors on the map
- Unit Schema Types authoring a new prompt unit
- Chain & ChainStep Schema designing a new chain
- Operations & Versions Schema writing a new sync query