Skip to content

Functional Requirements

Functional requirements with stable IDs, traceable to spec sections.

Status: Draft v0.1 · 2026-05-03 Source documents: PRD.md, SPEC.md, API.md, CLI.md, MCP.md, AI.md Scope: v1.0 (alpha → GA). Items marked (post-v1) are tracked here for traceability but are out of v1 scope per PRD.md §5.


  • Every requirement has a stable ID (FR-<area>-<n>). IDs are referenced from epics, tests, and pull requests.
  • Priority: MUST (v1 release-blocking), SHOULD (v1 target, may slip to v1.1), MAY (nice-to-have / post-v1).
  • Trace column links the requirement back to the source document section so reviewers can validate intent.
  • A requirement is satisfied iff its acceptance criteria can be exercised against any one of the three surfaces (API / CLI / MCP) — surface-specific items are tagged.

IDPriorityRequirementTrace
FR-V-1MUSTThe system SHALL operate against a single user-supplied vault directory mounted at INDX_VAULT (default /vault).SPEC §3
FR-V-2MUSTThe system SHALL treat the vault directory as the source of truth; deleting .indx/ and restarting SHALL produce a fully functional system after one reindex.PRD §9, SPEC §3
FR-V-3MUSTThe system SHALL never write to .obsidian/; it MAY read it (config only) to honor user preferences such as attachment folder and tag delimiters.PRD §6 (J6), SPEC §3
FR-V-4MUSTThe system SHALL ignore .git/, .indx/, and .obsidian/ during the indexing walk (the latter is read separately for config).SPEC §5.3
FR-V-5MUSTThe system SHALL accept only vault-relative POSIX paths through every public surface, normalizing leading /, collapsing .., and rejecting paths that escape the vault root.SPEC §6.4, API §5.1
FR-V-6SHOULDThe system SHALL support relocating .indx/ outside the vault via INDX_INDEX_DIR for users who do not want index files inside the vault.SPEC §10.2, PRD §11 (Q1)
FR-V-7MUSTPath matching SHALL be case-sensitive by default; an opt-in case_insensitive_match setting SHALL fall back to a case-folded lookup when an exact match fails.SPEC §6.4
FR-V-8MUSTThe system SHALL support exactly one vault per running container in v1; multi-vault is (post-v1).PRD §7
IDPriorityRequirementTrace
FR-N-1MUSTThe system SHALL support reading a note returning { path, kind, etag, mtime_ms, size, frontmatter, outline, body }, with an include selector to limit fields.API §6.1, CLI §3.1, MCP §3.1
FR-N-2MUSTThe system SHALL support creating a note via PUT/note write/note_write, accepting either a body (with optional fenced frontmatter) or a { frontmatter, body } pair.API §6.2, CLI §3.1, MCP §3.2
FR-N-3MUSTThe system SHALL support replacing an existing note with the same verbs as FR-N-2, honoring If-Match for optimistic concurrency.API §6.2, SPEC §6.1
FR-N-4MUSTThe system SHALL support deleting a note (hard delete) and SHALL maintain a 5-minute tombstone so subsequent reads return 410 gone (vs. 404 not_found).API §6.4
FR-N-5MUSTThe system SHALL support moving/renaming a note. By default the operation rewrites incoming wikilinks to keep the graph intact; update_links: false SHALL disable rewriting.API §6.5, SPEC §6.3, MCP §3.2
FR-N-6MUSTThe system SHALL support listing notes with filters: path_glob, tag (repeatable), frontmatter (key=value, repeatable), limit, opaque cursor.API §6.6, CLI §3.1
FR-N-7MUSTList endpoints SHALL stream NDJSON (one object per line, no envelope, no cursor) when the caller requests Accept: application/x-ndjson / --ndjson.API §11, CLI §3.1
FR-N-8MUSTThe system SHALL expose a fast existence check (note_exists / note exists / HTTP HEAD) that returns without loading the body.CLI §3.1, MCP §3.1
FR-N-9MUSTThe system SHALL expose a heading outline of a note as [{ level, text, line, block_id }].API §6.1, CLI §3.1
FR-N-10SHOULDNote creation SHALL accept an if_not_exists flag that turns the operation into an idempotent create (no error if the note already exists; the existing resource is returned).CLI §4.1, MCP §3.2

FR-E — Editing model (atomic writes & patches)

Section titled “FR-E — Editing model (atomic writes & patches)”
IDPriorityRequirementTrace
FR-E-1MUSTAll writes SHALL go through one engine path (core.vault.writeNote/peers); UI, API, CLI, MCP SHALL share the path.PRD §9, SPEC §6.1
FR-E-2MUSTEvery write SHALL be atomic: write-to-temp + rename. The index SHALL be updated synchronously before the write returns.SPEC §6.1
FR-E-3MUSTEvery read SHALL return a strong ETag (first 16 hex chars of xxhash64 of bytes); every write SHALL accept and honor If-Match and reject mismatches with 409 etag_mismatch.API §3, §6.2; SPEC §6.1
FR-E-4MUSTThe system SHALL implement an AST-aware patch language with the following ops: set_frontmatter, delete_frontmatter, append_body, prepend_body, insert_before_heading, insert_after_heading, replace_section, replace_block, rename_heading.SPEC §6.2, API §6.3, MCP §3.4
FR-E-5MUSTA patch request SHALL accept an ordered array of ops applied in order; failure of any op SHALL roll back the entire request (no partial writes).SPEC §6.2, API §6.3
FR-E-6MUSTPatches SHALL be applied against the parsed AST, not text positions, and re-serialized through a printer configurable per-vault (e.g., list marker, thematic break) via .indx/config.json.SPEC §6.2
FR-E-7MUSTEvery write SHALL accept an Idempotency-Key. Replays within 24 h SHALL return the original cached response with X-Indx-Idempotent-Replay: true; replays with the same key but a different body SHALL return 409 idempotency_key_reused.API §12
FR-E-8MUSTEvery write verb SHALL return the full new resource (read-after-write), including new etag, mtime_ms, and outline. The agent SHALL NOT need a follow-up GET.CLI §3.1, MCP §3.4
FR-E-9MUSTValidation of new content SHALL run before write. Parse warnings are allowed; parse errors SHALL reject the write with 422 parse_failed.SPEC §6.1, API §4.1
FR-E-10MUSTWhen INDX_READONLY=true, every write surface SHALL reject writes with 423 readonly.SPEC §9.1, API §4.1

FR-F — Vault format (Obsidian compatibility)

Section titled “FR-F — Vault format (Obsidian compatibility)”
IDPriorityRequirementTrace
FR-F-1MUSTThe system SHALL parse YAML frontmatter between --- fences at the start of a file; types SHALL be inferred (string, number, bool, list, date, datetime).SPEC §4.1
FR-F-2MUSTUnknown frontmatter keys SHALL be preserved verbatim on round-trip.SPEC §4.1, PRD §11 (Q4)
FR-F-3MUSTThe system SHALL recognize and index Obsidian conventions: tags:, aliases:, cssclasses:.SPEC §4.1
FR-F-4MUSTThe system SHALL parse and resolve wikilink forms: [[Note]], [[Note|Display]], [[Note#Heading]], [[Note#^block-id]], [[#Heading]] (intra-note).SPEC §4.2
FR-F-5MUSTWikilink resolution SHALL match by filename without extension, falling back to aliases: in target frontmatter.SPEC §4.2
FR-F-6MUSTThe system SHALL parse embed forms: ![[Note]], ![[Note#Heading]], ![[Note#^id]], ![[image.png]], ![[file.pdf]].SPEC §4.3
FR-F-7MUSTThe system SHALL recognize callouts: > [!type] … with the standard type list (note, tip, warning, …). Unknown types SHALL pass through.SPEC §4.4
FR-F-8MUSTThe system SHALL parse tags #tag and #nested/tag (allowed chars: letters, digits-not-first, _, -, /); frontmatter tags: SHALL also index without leading #.SPEC §4.5
FR-F-9MUSTThe system SHALL parse block IDs (^block-id at the end of a block) and make them addressable as [[Note#^block-id]].SPEC §4.6
FR-F-10MUSTLaTeX ($inline$, $$display$$) and Mermaid fenced blocks SHALL pass through unmodified; rendering is the UI’s responsibility.SPEC §4.7
FR-F-11MUSTA round-trip “open → write-back unchanged” against an Obsidian sample vault SHALL produce byte-identical files when no edit is intended.PRD §8, SPEC §13
IDPriorityRequirementTrace
FR-S-1MUSTThe system SHALL provide lexical search via SQLite FTS5 (BM25) over title, body, and tags.SPEC §5.2
FR-S-2SHOULDThe system SHALL provide semantic search (KNN over notes_vss) when an embeddings provider is configured.SPEC §5.2
FR-S-3SHOULDThe system SHALL provide hybrid search (reciprocal rank fusion of lexical + semantic) when embeddings are configured.SPEC §5.2
FR-S-4MUSTWhen mode=hybrid or semantic is requested without embeddings configured, the system SHALL fall back to lexical, set mode_used: "lexical", and include embeddings_unavailable in warnings.API §7.1, CLI §3.4
FR-S-5MUSTSearch SHALL accept structural filters composable with any mode: frontmatter equality/range/contains, tag:, path: glob, linksTo:[[X]], linkedFrom:[[Y]].SPEC §5.2
FR-S-6MUSTSearch results SHALL include { path, title, score, snippet, tags, matched_in[] }.API §7.1
FR-S-7MUSTSearch SHALL paginate via opaque cursor; NDJSON streaming SHALL be available via Accept.API §11
FR-S-8MAYCross-vault search is (post-v1).PRD §7
IDPriorityRequirementTrace
FR-L-1MUSTThe system SHALL return inbound (backlinks) and outbound (forward) link sets for a given note, each with { src_path, dst_path, line, anchor, kind }.API §5, CLI §3.2
FR-L-2MUSTThe system SHALL list unresolved wikilinks (targets that do not match any vault file or alias).API §5, CLI §3.2
FR-L-3MUSTThe system SHALL list orphan notes (notes with no inbound and no outbound links).API §5, CLI §3.2
FR-L-4MUSTWhen a note is moved, the system SHALL by default rewrite incoming wikilinks to keep the graph consistent and report the count of rewrites.API §6.5, SPEC §6.3
FR-L-5SHOULDThe system SHALL detect tag co-occurrence and expose it via the graph queries used by the UI.PRD §7
FR-L-6MAYA force-directed visual graph view is provided in a basic form only in v1.PRD §7
IDPriorityRequirementTrace
FR-T-1MUSTThe system SHALL list all tags with counts.API §5, CLI §3.3
FR-T-2MUSTThe system SHALL list notes for a given tag, paginated.API §5, CLI §3.3
FR-T-3MUSTThe system SHALL bulk-rename a tag across the vault, returning the affected paths and supporting dry_run.CLI §3.3, MCP §3.2
IDPriorityRequirementTrace
FR-C-1MUSTThe system SHALL read and write .canvas files conforming to JSON Canvas 1.0.SPEC §4.8, API §8
FR-C-2MUSTThe system SHALL expose canvas patch ops: add_node, update_node, remove_node, add_edge, remove_edge, move_node.CLI §3.5
FR-C-3MUSTUnknown canvas fields SHALL be preserved on round-trip.SPEC §4.8
FR-C-4SHOULDThe web UI SHALL provide a read-only canvas viewer in v1; visual editing is (post-v1).PRD §7
IDPriorityRequirementTrace
FR-B-1MUSTThe system SHALL read .base YAML files (filters, formulas, properties, views).SPEC §4.9, API §8
FR-B-2MUSTThe system SHALL execute a .base query against the vault index and return matching rows; NDJSON streaming SHALL be available.API §8, CLI §3.6
FR-B-3SHOULDThe system SHALL allow creating/updating a .base YAML file directly; row data continues to live in the linked notes’ frontmatter.SPEC §4.9
FR-B-4MAYA visual .base builder UI is (post-v1).PRD §7
IDPriorityRequirementTrace
FR-W-1MUSTThe system SHALL watch the vault with chokidar, debouncing changes by 200 ms, and incrementally diff-and-upsert into the index.SPEC §5.3
FR-W-2MUSTThe system SHALL emit note.created, note.updated, note.deleted, note.moved, index.reindexed events; each event SHALL carry an Actor (ui/api/cli/mcp/fs).SPEC §7
FR-W-3MUSTThe system SHALL expose events via SSE at GET /v1/events, supporting query filters (paths, kinds) and Last-Event-ID resume.API §9
FR-W-4MUSTThe system SHALL append events to a rolling .indx/events.log (NDJSON, rotated at 10 MB) for audit.SPEC §7
FR-W-5MUSTThe system SHALL provide vault_reindex returning 202 Accepted with a job id; progress SHALL be observable via the event stream.API §10
FR-W-6MUSTThe MCP server SHALL expose resources/subscribe over vault://<glob> URIs, emitting notifications/resources/updated on change.MCP §4
IDPriorityRequirementTrace
FR-A-1MUSTThe system SHALL accept static bearer tokens of format indx_<32 hex chars> configured via INDX_TOKEN (single) or INDX_TOKENS_FILE (JSON array).SPEC §8.1, API §2
FR-A-2MUSTTokens SHALL support optional scopes: vault:read, vault:write, vault:admin, path:<glob>.SPEC §8.1, API §2
FR-A-3MUSTRequests without a valid token SHALL receive 401 unauthorized; requests with insufficient scope SHALL receive 403 forbidden.API §4.1
FR-A-4MUSTThe MCP tool catalog advertised to a session SHALL be filtered by scope (e.g., a vault:read-only session SHALL NOT see write tools).MCP §6.2
FR-A-5MUSTThe web UI SHALL use session cookies tied to the bearer token with SameSite=Strict. Cross-origin API requests SHALL require Authorization: Bearer ….SPEC §8.2
FR-A-6MAYPer-user OIDC and per-user vaults are (post-v1).PRD §5, SPEC §8.3
IDPriorityRequirementTrace
FR-API-1MUSTThe HTTP API SHALL be served under /v1/*.API §1
FR-API-2MUSTEvery response SHALL use the envelope { ok, data | error }. Errors SHALL be RFC 7807-shaped inside error and carry a stable machine code.API §3, §4
FR-API-3MUSTThe API SHALL expose every error code listed in API.md §4.1 with the documented HTTP status.API §4.1
FR-API-4MUSTThe API SHALL implement the resource map in API.md §5 for v1 (notes, search, links, tags, canvas, bases, vault admin, events, openapi).API §5
FR-API-5MUSTAll list/search endpoints SHALL paginate via opaque cursor. Offset-style pagination SHALL NOT be exposed.API §1, §11
FR-API-6MUSTThe API SHALL set ETag, Last-Modified, X-Indx-Actor, and X-Indx-Indexed-At on resource responses.API §3
FR-API-7MUSTThe API SHALL serve a complete OpenAPI 3.1 document at GET /openapi.json reflecting the running server’s exact capabilities (feature-flagged ops appear iff configured).API §14, SPEC §14
FR-API-8MUSTURL-prefix /v1 SHALL be additive-only. Breaking changes SHALL ship under /v2. Paths scheduled for removal SHALL include a Deprecation: header and a Link: …; rel="deprecation".API §15
FR-API-9MUSTThe API SHALL implement per-token sliding-window rate limiting (default 100 rps, 1000 burst), responding with 429 rate_limited and standard Retry-After / X-RateLimit-* headers.API §13, SPEC §12
FR-API-10MUSTThe API SHALL honor Accept for content negotiation: application/json (default), application/x-ndjson (streamed lists), text/event-stream (events).API §11
IDPriorityRequirementTrace
FR-CLI-1MUSTThe CLI SHALL ship as a single Node binary indx and SHALL be installable via npm and bundled in the Docker image.SPEC §1
FR-CLI-2MUSTThe CLI SHALL operate in two modes: --remote (talks to a running server, default if INDX_URL is set) and --local (operates directly against a vault directory).SPEC §1, CLI §1
FR-CLI-3MUSTStdout SHALL always be valid JSON or NDJSON; stderr carries human-readable telemetry never required for correctness. The CLI SHALL emit no ANSI / no spinners by default.CLI §1, §2.3
FR-CLI-4MUSTThe CLI SHALL implement the noun/verb surface in CLI.md §3 (note, link, tag, search, canvas, base, vault, serve, mcp, schema).CLI §3
FR-CLI-5MUSTThe CLI SHALL expose the global flags in CLI.md §2.1, including --dry-run, --yes, --idempotency-key, --if-match, --ndjson.CLI §2.1
FR-CLI-6MUSTExit codes SHALL match CLI.md §2.2 (0 success, 2 usage, 3 not found, 4 conflict, 5 unauthorized, 6 validation, 7 rate-limited, 8 upstream).CLI §2.2
FR-CLI-7MUSTThe CLI SHALL never prompt interactively. Destructive verbs require --yes and otherwise exit 4.CLI §1, §5
FR-CLI-8MUSTWrite verbs SHALL return the full new resource (read-after-write).CLI §3.1
FR-CLI-9MUSTindx schema openapi, indx schema mcp, indx schema patch-ops, indx schema events SHALL each return the live schema as JSON.CLI §3.10
IDPriorityRequirementTrace
FR-MCP-1MUSTThe MCP server SHALL be reachable over stdio (indx mcp serve --stdio) and over HTTP/SSE (POST /mcp on the main server).MCP §6
FR-MCP-2MUSTThe server SHALL identify itself as { name: "indx", version, instructions } where instructions is the agent onboarding text.MCP §2
FR-MCP-3MUSTThe tool catalog SHALL include the read, write, and admin tools listed in MCP.md §3, each with a Zod-derived JSON Schema.MCP §3
FR-MCP-4MUSTTool inputs SHALL be flat JSON objects with required fields up front; tool outputs SHALL include both a structuredContent payload and a short content[].text deterministic summary.MCP §3, §3.5
FR-MCP-5MUSTTool errors SHALL set isError: true and return a body of { code, message, details } matching the API error codes.MCP §3
FR-MCP-6MUSTWithin a 60 s window, identical (tool, input) pairs SHALL be treated as replays for safe-marked tools.MCP §3
FR-MCP-7MUSTThe server SHALL expose resources under the vault:// URI scheme (note, search, graph, tags, canvas, base) with resources/list, resources/read, and resources/subscribe.MCP §4
FR-MCP-8SHOULDThe server SHALL ship the prompt catalog in MCP.md §5 (vault_summary, daily_note, link_orphans, cleanup_unresolved_links, weekly_review).MCP §5
FR-MCP-9MUSTEvery MCP tool SHALL map 1:1 to an HTTP endpoint and a CLI verb per MCP.md §7.MCP §7
IDPriorityRequirementTrace
FR-UI-1MUSTThe UI SHALL be served from / by the same Next.js process that serves /v1/* and /mcp.SPEC §1
FR-UI-2MUSTThe UI SHALL provide: file tree, markdown editor (CodeMirror 6), search, and recent activity panel for v1.PRD §12
FR-UI-3MUSTThe activity panel SHALL annotate each change with the Actor (who: agent etc.) drawn from the events stream.PRD §6 (J4), SPEC §7
FR-UI-4MUSTEvery UI write SHALL go through @indx/core via the same code path as API/CLI/MCP — no UI-only side channels.PRD §9
FR-UI-5MUSTThe UI SHALL be responsive enough to be usable on mobile-class viewports (no native mobile app in v1).PRD §5
FR-UI-6SHOULDThe UI SHALL provide a basic graph view, a tag explorer, and a canvas viewer in v1.PRD §7, FR-C-4
FR-UI-7MAYDiff/rollback UI is (post-v1, planned for v1.1).PRD §6 (J4), §12
FR-UI-8MAYBuilt-in chat assistant in the UI is out of scope.PRD §5
IDPriorityRequirementTrace
FR-CONF-1MUSTThe system SHALL accept the environment variables in SPEC.md §9.1, with documented defaults.SPEC §9.1
FR-CONF-2MUSTA .indx/config.json SHALL persist tokens (with scopes), markdown printer settings, embeddings enable flag, and ignore globs.SPEC §9.2
FR-CONF-3MUSTGET /v1/vault/config SHALL return the effective merged config (env + file). PUT /v1/vault/config SHALL patch and persist.API §10, SPEC §9.2
FR-CONF-4MUSTEmbeddings SHALL be pluggable across vercel-ai-gateway, openai-compatible endpoints, and ollama; the product SHALL function fully without an AI key (semantic search disabled).PRD §4, SPEC §9.1
FR-CONF-5MUSTThe system SHALL make zero outbound network calls unless a configured embeddings provider is used.PRD §9, SPEC §12
IDPriorityRequirementTrace
FR-AI-1MUSTThe system SHALL expose seven AI operations — summarize, ask, toc, relate, tag, metadata, extract — reachable identically from the HTTP API (/v1/ai/*), the CLI (indx ai *), and MCP (ai_* tools).AI §1, §10
FR-AI-2MUSTThe AI runtime SHALL be opt-in: when no chat or embeddings provider is configured, AI tools SHALL NOT be advertised in /openapi.json or the MCP tool catalog and direct calls SHALL return 503 ai_unavailable.AI §3, §9
FR-AI-3MUSTThe chat-model provider SHALL be pluggable across vercel-ai-gateway, openai-compatible endpoints, and ollama, configured via INDX_AI_PROVIDER / INDX_AI_MODEL (additive to the embeddings env vars in SPEC §9.1); when INDX_AI_PROVIDER is unset it SHALL inherit INDX_EMBEDDINGS_PROVIDER.AI §3.2, SPEC §9.1
FR-AI-4MUSTAI operations SHALL accept a structured scope selector (paths | glob | tag | query | note+include_linked); resolved scopes SHALL be intersected with INDX_AI_ALLOW_GLOBS / INDX_AI_DENY_GLOBS and excluded items SHALL be reported via the globs_excluded warning.AI §4
FR-AI-5MUSTai_summarize SHALL return { summary, citations[], usage, warnings } with map-reduce summarization for scopes that exceed the prompt budget.AI §5.1
FR-AI-6MUSTai_ask SHALL retrieve via the existing search infra (defaulting to hybrid when embeddings are configured, else lexical with embeddings_unavailable warning) and SHALL answer only from retrieved evidence; un-grounded answers SHALL be returned with confidence: "low" and unsupported_question warning.AI §5.2
FR-AI-7MUSTai_toc SHALL support mode: "note" (single-note TOC) and mode: "moc" (folder/glob/tag MOC). With include_descriptions: false, mode: "note" SHALL be deterministic and SHALL NOT call the chat model.AI §5.3
FR-AI-8MUSTai_toc materialization (write: { path }) SHALL go through core.vault.writeNote honoring if_match, if_not_exists, and the standard atomic-write pipeline; the response SHALL include written: { path, etag } (read-after-write).AI §5.3, FR-E-1, FR-E-8
FR-AI-9MUSTai_relate SHALL discover candidate neighbors via the index (lexical / semantic / hybrid), optionally classify the relation via the chat model, and (with propose_links: true) return draft PatchOp[] per SPEC §6.2 without applying them.AI §5.4, SPEC §6.2
FR-AI-10MUSTEvery AI output that references vault content SHALL include a citations[] array of { path, etag, anchor?, line?, span?, snippet? }; the runtime SHALL verify each citation post-hoc, dropping invalid ones with citation_invalid and surfacing etag drift as citation_drift.AI §6
FR-AI-11MUSTWhen cite: true (default) and zero citations survive verification, the operation SHALL fail with 422 ai_grounding_failed.AI §6, §9
FR-AI-12MUSTAI ops SHALL stream when negotiated: SSE on the API (Accept: text/event-stream), NDJSON on the CLI (--stream), notifications/progress on MCP. Providers without streaming SHALL fall back to a single response with the stream_unsupported warning.AI §7
FR-AI-13MUSTAI results SHALL be cached in .indx/ai-cache.db keyed by (op, provider, model, temperature, seed, max_output_tokens, json_schema, normalized_input, content_fingerprint). Cache hits SHALL return usage zeros and an ai_cache_hit warning.AI §8.1
FR-AI-14MUSTThe standard Idempotency-Key SHALL apply to AI ops (24 h cache); same key + different body SHALL return 409 idempotency_key_reused.AI §8.2, FR-E-7
FR-AI-15SHOULDWhen INDX_AI_DAILY_COST_USD is set, total spend per UTC day across tokens SHALL be capped; over-cap SHALL return 429 ai_quota_exceeded with Retry-After set to seconds until midnight UTC.AI §8.3
FR-AI-16MUSTAI errors SHALL include the codes ai_unavailable (503), ai_provider_error (502), ai_grounding_failed (422), ai_input_too_large (422), ai_quota_exceeded (429), in addition to the existing error envelope.AI §9, API §4.1
FR-AI-17MUSTThe MCP tool catalog SHALL filter ai_* tools by configured provider and by token scope; AI tools requiring writes (ai_toc with write) SHALL NOT be advertised to a vault:read-only session.AI §10.3, FR-A-4
FR-AI-18MUSTEvery AI invocation SHALL emit an ai.invocation event (op, actor, scope_paths, provider, model, usage, cache_hit, duration_ms, ok, error_code) on the standard event bus and into .indx/events.log; the event payload SHALL NOT include prompts or model outputs.AI §12, FR-W-2, FR-W-4
FR-AI-19MUSTGET /v1/ai/status (and equivalents) SHALL return { enabled, provider, model, embeddings, cache, spend_today_usd } without ever calling the upstream provider.AI §10
FR-AI-20MUSTThe MCP server SHALL expose two read-only resource patterns when AI is enabled — ai://summary/<scope> and ai://moc/<scope> — that re-derive on subscribed scope changes via the existing event bus.AI §10.3, FR-W-6
FR-AI-21MUSTai_tag SHALL suggest tags for a scope, biased to the existing vault vocabulary (vocabulary.use_existing defaults true, drawing from tag_list), with optional candidates/forbid/namespace constraints, max_per_note and min_confidence filters, and per-suggestion is_new flags. Returned tags SHALL be normalized and validated against SPEC §4.5; invalid candidates SHALL be dropped with tag_invalid warning.AI §5.5
FR-AI-22MUSTai_tag with apply: true SHALL write through core.notes.patch per path (atomic per note), under apply_mode: "merge" (default — dedup-union with existing tags) or "replace"; inline #tag mentions in note bodies SHALL NOT be modified.AI §5.5, §5.8, FR-E-4
FR-AI-23MUSTai_metadata SHALL accept a typed fields[] schema (string | number | boolean | date | datetime | list | enum, plus pattern, min, max, enum, required, default) and constrain model output via JSON Schema; the runtime SHALL re-validate post-generation, dropping invalid values with metadata_invalid and reporting un-extractable required fields with metadata_missing.AI §5.6
FR-AI-24MUSTai_metadata apply modes (set_missing (default) | overwrite | merge) SHALL behave per AI §5.6; writes for tags/aliases/cssclasses SHALL preserve SPEC §4.1 Obsidian-conformant list shape regardless of declared type.AI §5.6, FR-F-3
FR-AI-25MUSTai_extract SHALL accept a caller-supplied JSON Schema (2020-12 subset) plus optional schema_id, generate matching data, and re-validate before returning; an invalid input schema SHALL fail with 422 ai_schema_invalid; per-record validation failures SHALL drop with extract_invalid.AI §5.7
FR-AI-26MUSTai_extract with destination: "frontmatter" SHALL require both destination_key and apply: true to write; without these the operation is read-only and returns drafts via proposed_patches.AI §5.7
FR-AI-27MUSTMulti-note apply ops (ai_tag, ai_metadata, ai_extract) SHALL: (a) write each note atomically via core.notes.patch, (b) honor per-path if_match, (c) order writes by sorted path, (d) report each path’s outcome in applied[] and failures in warnings[], (e) emit standard note.updated events plus one roll-up ai.invocation event listing applied_paths. Mid-op failure SHALL NOT roll back already-written notes.AI §5.8, FR-E-1, FR-E-2, FR-W-2
FR-AI-28MUSTPer-path if_match mismatches under apply: true SHALL surface as 409 ai_apply_conflict with details.failed[{ path, expected, actual }].AI §5.8, §9
FR-AI-29MUSTApply ops SHALL be content-idempotent: repeating an apply request after a successful run SHALL be a no-op (per apply_mode semantics) and SHALL surface apply_skipped_no_change per affected path. Combined with Idempotency-Key, repeats within 24 h SHALL return the cached response.AI §5.8, FR-E-7
FR-AI-30MUSTApply ops SHALL include the new etag of each written note in applied[] (read-after-write), so the agent does not need a follow-up note_read.AI §5.5–§5.7, FR-E-8
IDPriorityRequirementTrace
FR-OBS-1MUSTThe system SHALL emit structured JSON logs to stdout via pino. Log level SHALL be controlled by INDX_LOG_LEVEL.SPEC §11
FR-OBS-2MUSTGET /v1/health SHALL return { status, vault, indexed_notes, last_reindex_at, uptime_s }.SPEC §11, API §5
FR-OBS-3MUSTGET /v1/vault/status SHALL return counts, last reindex, and embeddings status.API §10
FR-OBS-4SHOULDThe system SHALL support exporting OpenTelemetry traces via OTEL_EXPORTER_OTLP_ENDPOINT (off by default).SPEC §11
FR-OBS-5MUSTAn audit log of vault events SHALL be written to .indx/events.log (NDJSON, rotated at 10 MB).SPEC §7
IDPriorityRequirementTrace
FR-PKG-1MUSTA single Zod tree in @indx/shared SHALL be the source of truth; TypeScript types, JSON Schema (MCP), and OpenAPI 3.1 (HTTP) SHALL be derived.PRD §9, SPEC §14
FR-PKG-2MUSTAdding a new capability SHALL be one PR that (1) adds the Zod schema, (2) implements the operation in @indx/core, (3) wires it into the API route, the CLI verb, and the MCP tool via thin adapters.SPEC §14
FR-PKG-3MUSTAll public schemas SHALL be discoverable at runtime via /openapi.json and indx schema * so a new agent can be productive in one round trip.API §14, MCP §1

Source sectionFR coverage
PRD §3 (Users)informs personas in EPICS.md
PRD §4 (Goals)FR-V-2, FR-V-3, FR-PKG-1, FR-CONF-4, FR-CONF-5
PRD §5 (Non-goals)excluded from FR; documented as MAY (post-v1) where relevant
PRD §6 (Journeys)mapped 1-to-1 to epics in EPICS.md
PRD §7 (Functional scope)FR-N, FR-S, FR-L, FR-T, FR-C, FR-B, FR-W, FR-A, FR-V-8
PRD §8 (Quality bar)covered in NFR.md
PRD §9 (Constraints)FR-V-2, FR-E-1, FR-E-3, FR-E-7, FR-CONF-5, FR-PKG-1
PRD §10 (Risks)mitigations in FR-F-2, FR-F-11, FR-W-1, FR-W-5, FR-A-2
PRD §11 (Open Q1–Q4)FR-V-6, FR-CONF-4, FR-CLI-2, FR-F-2
SPEC §1–§14mapped per row above
API §1–§15mapped per row above
CLI §1–§6mapped per row above
MCP §1–§9mapped per row above
AI §1–§16FR-AI-1 … FR-AI-30