Skip to main content

Feature Catalog

A machine-readable index mapping every Functional Requirement (FR-S089-001, FR-082-3c-005, etc.) to the file:line that implements it and the test files that cover it. Generated deterministically by npm run generate:feature-catalog and committed to the repo at docs/develop/feature-catalog.json.

Purpose

The catalog is the AI-coding-agent entry point for the codebase. When an agent (Claude Code, a sub-agent, or a future automation) is asked to extend a feature, it looks up the FR id in the catalog and gets back: spec headline, implementing files, covering tests. No grep over a 3,000-test repo required.

Source of truth lives in specs/<NNN>-*/spec.md. The catalog is a strict view over those specs + the codebase.

Shape

{
"schemaVersion": 1,
"generatedAt": "2026-04-30T...",
"counts": {
"specs": 77,
"frs": 1208,
"frsWithImpl": 676,
"frsWithoutImpl": 532
},
"specs": {
"090-extractor-enhancements": {
"specPath": "specs/090-extractor-enhancements/spec.md",
"frCount": 21,
"frs": {
"FR-S090-011": {
"id": "FR-S090-011",
"headline": "When extract_pdf_text returns no extractable text, System MUST fall through to vision-LLM OCR ...",
"specLine": 56,
"impl": [
{ "path": "apps/ml/services/vision_ocr.py", "line": 12, "text": "..." },
{ "path": "apps/ml/services/routes/extract.py", "line": 134, "text": "..." }
],
"tests": [
{ "path": "apps/ml/tests/test_extract_route_spec090.py", "line": 102, "text": "..." }
],
"hasImpl": true,
"hasTests": true
}
}
}
}
}

How the catalog is generated

scripts/generate-feature-catalog.mjs walks every specs/<NNN>-*/spec.md, extracts each FR- identifier, then runs git grep -nF for that identifier across apps/, packages/, scripts/, docs/. Matches under __tests__/, /tests/, or with .spec. / .test. in the path become tests; everything else becomes impl.

Re-run after every PR that touches an FR or its implementing code:

npm run generate:feature-catalog

Stale-check (CI):

npm run check:feature-catalog

What "without impl" means

frsWithoutImpl counts FRs that have no code reference to the FR id as a comment marker. Two cases:

  1. Implemented but undocumented — the code does the thing but doesn't reference the FR by id. The drift audit (Spec 091) flagged ~14 of these; they're real impl, just under-marked. The fix is a one-line comment on the implementing file. Find them via the catalog: frsWithoutImpl > 0 && headline matches X.
  2. Genuinely deferred — the FR is in the spec but hasn't been built yet (e.g., Spec 082 Phase 5 knowledge accumulation, Spec 088 FR-S088-004 SSE client subscription). Tracked as follow-up PRs.

When to read the catalog

  • Before starting a feature: look up the surrounding FRs to understand what already exists and what still needs to be built.
  • Before opening a PR: re-run generate:feature-catalog so the diff captures the new FR↔file mappings.
  • During code review: cross-reference an FR claim in the PR description against the catalog to confirm the implementation actually references the FR.
  • When onboarding an AI agent: the catalog plus per-module READMEs (apps/api/src/modules/<module>/README.md) are the primary navigation surface.

Per-module entry-point READMEs

The richest AI-agent context lives in module-level READMEs. Read these before editing the corresponding directory:

ModuleREADMESpecs covered
Extractorapps/api/src/modules/extractor/README.md084, 090
Extraction Reviewapps/api/src/modules/extraction-review/README.md085, 086, 087
Agents (panel deliberation)apps/api/src/modules/admin/agents/README.md082 (Phase 1–4 + 3c), 09b/c/d, 089

Per-module READMEs document: pipeline diagrams, FR → file:line mappings, audit-chain action list, "how to extend" recipes, and "what NOT to do" lists. They're the recipe an AI agent needs before writing the next change in that module.