> ## Documentation Index
> Fetch the complete documentation index at: https://docs.suchi.page/llms.txt
> Use this file to discover all available pages before exploring further.

# Johnny.Decimal taxonomy

> The JD opinion, the starter tree, and the invariants that make it a schema — not a suggestion.

suchi ships with [Johnny.Decimal](https://johnnydecimal.com/) as the
default filing system — a schema-level taxonomy the app validates,
routes on, browses by number, and (in the enterprise track) hangs
retention policies off.

## The opinion

**JD is on by default.** First boot loads a generic starter tree with
zero configuration. Every document has a JD category from the moment
it's ingested. URLs, search, and default storage paths are JD-shaped
out of the box.

Flat mode exists as an explicit opt-out (`taxonomy: flat` in
settings). Crucially, flat mode is a **degenerate JD tree**, not a
second code path: the internal representation is always
`jd_areas` + `jd_categories`, flat mode just hides the tree in the UI
and collapses everything to a single inbox category. One code path,
one opinion, a respectful escape hatch.

**You can always come back to change this.** Swap presets after
ingesting docs by adding `"refile": true` to the preset-apply call —
existing docs park on the new inbox and a [refile sweep](/refile)
re-runs rules and re-renders the symlinks under the new tree. Without
`refile`, the swap is refused (with a 409) as a safety.

## The starter tree

Auto-loaded on first boot. Delete and replace freely — `suchi jd
import` (arrives post-Phase-1) will pipe a fresh YAML in.

```yaml the default theme={null}
areas:
  - start: 10, end: 19, name: Life admin
    categories:
      - { code: 11, name: Identity }
      - { code: 12, name: Health }
      - { code: 13, name: Education }
      - { code: 14, name: Vehicles }
      - { code: 15, name: Membership & subscriptions }

  - start: 20, end: 29, name: Money
    categories:
      - { code: 21, name: Banking }
      - { code: 22, name: Tax }
      - { code: 23, name: Insurance }
      - { code: 24, name: Investments }
      - { code: 25, name: Salary & benefits }

  - start: 30, end: 39, name: Home
    categories:
      - { code: 31, name: Utilities }
      - { code: 32, name: Rent & mortgage }
      - { code: 33, name: Purchases & warranties }
      - { code: 34, name: Repairs & maintenance }

  - start: 40, end: 49, name: System
    categories:
      - { code: 41, name: Templates }
      - { code: 42, name: Archive notes }
      - { code: 49, name: Inbox }    # system category — undeletable
```

## Invariants

* Every document has a `jd_category_id` — schema `NOT NULL`.
* The inbox category (`system=1`) is undeletable via API/UI.
* `settings.jd_inbox_category_id` always points at a live system
  category. If pruned, suchi repairs the pointer at boot and logs
  `jd.inbox.repaired`.
* Category codes are unique. Ranges nest exactly: category `22` must
  live inside an area `20..29`.

## Over the JSON API

Every non-browser consumer (mobile app, MCP tool, agent, SPA) that
needs to render a filing chip or drive a picker uses two surfaces:

* **`GET /api/jd/categories/`** — enumerate the tree. Query params:
  `?q=<prefix>` (case-insensitive code-or-name match, `?q=22` and
  `?q=tax` both hit `22 Tax`), `?area=<code>` (scope to one area's
  10-slot range). Each row carries `area_code` + `area_name`
  denormalized, so a picker can render the grouped tree in a single
  request. See [`api#get-apijdcategories`](/api#get-apijdcategories).
* **`GET /api/documents/{id}`** — the JD fields on the response are
  denormalized: `jd_category_id`, `jd_category_code`,
  `jd_category_name`, `jd_area_name`. Enough to render "22 Tax ·
  Money" without a second call. Set the category by PATCHing back
  with `{"jd_category_id": <n>}`.

`jd_area_code` isn't included on either surface — the category's
own `code` encodes it (22 → 20–29), and duplicating invites
divergence.

## What the inbox is for

<Note>
  Ingest paths never silently mis-file. When a classifier (or the
  importer, without a matching `--map-jd` rule) can't confidently pick
  a category, the document lands in inbox with a `needs-review` tag.
  It's browsable, searchable, and bulk-reassignable. Nothing is ever
  "unclassified" in a way that hides it.
</Note>
