Version: 1.0
Date: July 17, 2026
Purpose: Standing constraints for every implementation session (Codex or human). These rules are binding for all code written in this repo. When a rule conflicts with an ad-hoc idea mid-session, the rule wins; change the rule file first if it must change.
Companions: prd.md, techstack.md, dataflow.md, design.md, schemas.md, implementation-phases.md (all in /docs)
- schemas.md is law for data shapes. No type, event, table, or API model may be invented, extended, or renamed in code. If a shape is missing or wrong, update schemas.md first, in its own commit, then implement.
- implementation-phases.md is law for sequence. Work only on the current phase. A phase is complete only when its exit test passes — never on "mostly working."
- prd.md is law for scope. Anything not traceable to an FR number or the demo plan (prd.md §11, Demo script) is out of scope by default. The non-goals list (prd.md §4, "Non-goals" sub-heading) is binding.
- design.md is law for UI. Component names, states, copy voice, and color roles come from design.md — do not restyle or rename mid-build.
- techstack.md is law for dependencies. No library outside techstack.md §2 without adding it to the decision log (techstack.md §8) first, with a one-line rationale.
- Backend and frontend communicate only through the SSE and REST contracts in schemas.md §2 and §5. Neither side may assume payload fields the contract doesn't declare.
- All Pydantic models live in
backend/models.py; all TypeScript types live infrontend/src/types.ts. Both mirror schemas.md field-for-field. No inline/anonymous shapes for contract data. - Every SSE payload carries its
eventdiscriminator. The per-answer ordering guarantee (schemas.md §2.2) is enforced by the emitter, not assumed by the consumer — the frontend must tolerateerrorat any point. - Responses API structured outputs are validated on receipt. Invalid intent output → fallback to
retrieve. Quizsource_anchornot in the chunks table → reject and re-prompt (max 2). Starter cardfile_pathnot in the files table → drop the card.
- The only data permitted in any live Responses API request: the user's question, retrieved passages,
DatasetProfileobjects, computed analysis results, concept names with their cited chunks, and the workspace inventory (names/counts/schemas/stats). Never: raw file bytes, full document text, raw dataset rows, embeddings, file paths outside the workspace. - All API calls go through one module (
backend/agent/llm.py). No other file may import the OpenAI SDK. This makes the boundary auditable in one place. llm.pylogs every outbound payload's field names (not values) in debug mode — the network-tab verifiability claim (dataflow.md §1) must hold.- Embeddings are computed locally (fastembed). If the embeddings fallback triggers (BM25-only), no embedding API substitution — the fallback is BM25, not cloud embeddings.
- Generated code executes only via
backend/sandbox/runner.py: isolated subprocess,timeout=15,resource.setrlimitmemory cap, workspace-root path jail, artifact-only writes, network blocked. - Generated scripts are never
exec()'d in-process and never imported. - Every executed script is retained verbatim under
.fieldnotes/artifacts/and registered in the artifacts table — the code citation chip must always resolve to the exact script that ran. - Sandbox failures clean up partial outputs and surface an honest non-recoverable
errorevent. The shipped executor does not retry failed generated scripts. - matplotlib uses the Agg backend only; charts are written as PNG artifacts, never streamed as inline base64 blobs over SSE (send the
url).
- Python: 3.12, type hints on all public functions,
ruffclean, no bareexcept, pathlib over os.path, f-strings. Modules match the techstack.md §6 layout — no new top-level packages. - TypeScript/React: strict mode and functional components. Shared UI is extracted where it improves clarity; the shipped frontend uses component-local and shared handwritten CSS rather than Tailwind.
- IDs are UUID4 strings; timestamps are ISO 8601 UTC; enums are lowercase — everywhere, per schemas.md conventions.
- No TODOs left in committed code. Either complete it, remove it, or record a material release risk in
docs/beta-known-issues.md. - Config (API key, model name, ports) via environment variables read in one
backend/config.py;.envis gitignored;.env.exampleis committed.
- Commit at every green verification milestone and at every working sub-deliverable — small, titled commits, never a day of work in one commit.
mainmust pass the documented Python, frontend, and applicable release checks from a fresh local environment.run.shis a Unix convenience launcher, not a cross-platform verification requirement..gitignoreincludes.fieldnotes/,.env, venvs,node_modules, and all artifact output.demo_course/is committed — judges need it.- Never commit secrets. If a key ever lands in history, rotate it immediately; do not just delete the file.
- Run
scripts/exit_phase0.py,scripts/exit_phase1.py, the backend test suite, and frontend tests/build when their affected areas change. - Use
scripts/run_benchmarks.pyandscripts/release_check.pyfor release-focused regression where their prerequisites are available. - Keep focused unit and integration coverage for contracts, persistence, retrieval, sandbox execution, citations, and streaming behavior.
- Use a clean workspace for demo or release-flow validation rather than relying on a warm index.
- Every implementation session begins by reading the relevant current contract and architecture documents.
- Record release or submission evidence outside the runtime documentation when it becomes available.
- Keep decisions and deviations in version-controlled documentation when they affect a public contract or release claim.
- Authentication and workspace roles are implemented on the backend (JWT/OAuth, cookie sessions, CSRF, and
owner/teacher/student/viewerenforcement inassert_workspace_access) but the role-based frontend flow (login/signup screens, teacher dashboard, classroom sharing, progress tracking UI) is in progress — seetracker.mdphase 6. - Local mode (no
FIELDNOTES_AUTH_ENABLED=1) remains the default single-user local tool with a syntheticlocal_adminowner. Do not remove or bypassassert_workspace_accessin routes when auth is enabled; gate the new UI surfaces behind the same role checks. - Keep the local-first/no-raw-file-to-API boundary (R3) intact while adding auth, teacher aggregation, or integration features: only the documented context payloads (question, retrieved passages, profiles, computed results, concept names, workspace inventory) may cross the API boundary.
- Preserve public contracts, local-first data handling, persisted citations, and grounded outputs unless the product contract is deliberately revised.
- New features require an explicit product decision; release hardening work must not silently expand scope.
- Track meaningful unresolved release risks in
docs/beta-known-issues.md.
- Every user-visible failure follows design.md §6 voice: what happened, then what happens next. No raw tracebacks in the UI; full tracebacks in the backend log.
errorSSE events always setrecoverabletruthfully — the UI shows a retry step only when one will actually occur.- Parse failures are honest in the UI (
parse_status: failedrenders in the file list) — never silently skip a file; the inventory-as-audit design (design.md §3.1) depends on it.
A phase is done when its documented verification passes, its contracts remain accurate, and its status is reconciled in tracker.md.
The beta implementation is complete when its documented verification scripts pass. Public release and submission requirements are tracked in tracker.md and release documentation.
Place this file at the repo root (and reference it from AGENTS/context config so every Codex session loads it first).