Non-negotiables
These nine rules define the platform’s safety and correctness boundaries. Every change to the codebase is evaluated against them.The system prompt is not a database
User-specific knowledge belongs in the knowledge graph, not hardcoded into LLM system prompts.
The system prompt provides instructions; the retrieval pipeline provides context.
Learning is gated: candidate, eval, approval, apply
No extracted fact becomes active truth without passing through evaluation. Facts enter as
candidate, are scored and evaluated, and only transition to active after approval and patch
application.Evidence is untrusted text
All evidence retrieved from the knowledge graph is treated as untrusted. An injection warning is
appended when evidence is included in LLM context to prevent prompt injection via stored
content.
Roles are server-derived
The API derives user roles from authenticated sessions. Client role claims are never trusted.
The role hierarchy is:
viewer < operator < admin < owner.Realtime path is read-only for truth tables
The realtime plane (API + brain-core) may read facts, entities, chunks, and sources but must
never write to them. This is enforced by import restrictions on
@reflection/db/queries/admin.Facts are append-only
Facts use temporal validity (
valid_from, valid_to) and supersession (supersedes_fact_id)
instead of destructive updates. Old facts are never deleted — they are superseded.No process.env outside designated files
Environment variable access is restricted to
packages/shared/src/env.ts (backend) and
apps/web/src/lib/env.ts (frontend). All other code imports from @reflection/shared/env.No deep imports
Code must import from package entrypoints (
@reflection/db/queries/read), never from internal
paths (@reflection/db/src/queries/facts.ts).Ingestion lifecycle authority
The ingestion pipeline has its own set of invariants that govern how source processing state is tracked:source_ingestionsis the only runtime authority for ingestion lifecycle state. No other table drives lifecycle branching.- Lifecycle semantics are defined once in
@reflection/schemas(ingestion-lifecycle.ts). Workers, DB query modules, and API status mapping all consume this shared kernel. ingest_runsis telemetry-only — it exists for observability and compatibility but must not drive lifecycle branching decisions.
Voice tool role policy
Voice tools — the server-tool callbacks that the voice provider calls during a conversation — follow strict role-based access:- Mutation-capable tools require
operatorrole or above (operator,admin,owner). - Viewer and visitor sessions are retrieval-only by design.
- Tool-requested learning intents flow through the async ingestion/eval gate. The realtime path does not directly mutate truth tables, even when a tool requests it.
Complexity budget
The project operates under a “minimum viable abstraction” posture. These hard rules prevent unnecessary complexity from entering the codebase:Hard rules
Hard rules
- No new infrastructure component without a measured bottleneck.
- No generic abstraction until at least 3 concrete call sites exist.
- No second implementation path for the same responsibility.
- No LLM entity extraction in the realtime path.
- No cross-layer shortcuts around boundaries.
- No placeholder comments (
TODO,FIXME,HACK) in source files. - Max cyclomatic complexity of 20 per function (enforced by ESLint).
PR rejection triggers
PR rejection triggers
A pull request is rejected if it:
- Adds indirection without materially reducing duplication or coupling.
- Introduces speculative extension points for hypothetical future use.
- Expands scope beyond the current milestone gate.
- Violates any non-negotiable invariant.
Definition of done
Every change must meet these criteria before merging:- Failing tests written first for changed behavior (test-driven development).
pnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass for the touched scope.- No focused or skipped tests (
test.only,test.skip) in committed code. - No placeholder comments (
TODO,FIXME,HACK).
Release blockers
Any of the following conditions blocks a release:- A boundary violation against any non-negotiable invariant.
- A failing safety or invariant regression test.
- An undocumented complexity increase that violates the complexity budget.
The enforcement matrix — which invariants are checked by ESLint, the architecture guard, and
file-scan tests — is documented in the repository’s
AGENTS.md. Each invariant is covered by at
least two independent enforcement mechanisms to prevent single-point bypass.Related pages
- Two-plane architecture — the execution model that invariants protect.
- Boundary matrix — per-plane allowed and forbidden operations.
- Knowledge graph — how the temporal fact model and eval gate work in practice.

