{
  "source": {
    "name": "clovis",
    "version": "1.3.0",
    "description": "Local-first bookkeeping CLI, MCP server, and SQLite ledger engine",
    "license": "AGPL-3.0-or-later",
    "node": ">=26.3.0",
    "bins": {
      "clovis": "dist/cli/main.js",
      "clovis-mcp": "dist/mcp/main.js"
    },
    "exports": {
      ".": {
        "types": "./dist/index.d.ts",
        "import": "./dist/index.js"
      },
      "./core": {
        "types": "./dist/core/index.d.ts",
        "import": "./dist/core/index.js"
      },
      "./app": {
        "types": "./dist/app/index.d.ts",
        "import": "./dist/app/index.js"
      },
      "./mcp": {
        "types": "./dist/mcp/index.d.ts",
        "import": "./dist/mcp/index.js"
      }
    }
  },
  "generatedBy": "apps/site/scripts/generate-clovis-docs.mjs",
  "sourceFiles": [
    "src/core/schema.ts",
    "docs/sqlite-schema.md"
  ],
  "schemaVersion": 4,
  "defaultBookId": "book_default",
  "model": "Clovis stores accounting facts in finalized journal lines. Balances are calculated by summing those facts.",
  "families": [
    {
      "name": "Identity",
      "summary": "Database identity, versioning, and book containers.",
      "tables": [
        "meta",
        "migration_history",
        "books"
      ]
    },
    {
      "name": "Accounting core",
      "summary": "The durable ledger facts that make balances and reports possible.",
      "tables": [
        "assets",
        "accounts",
        "journals",
        "journal_lines"
      ]
    },
    {
      "name": "Workflow memory",
      "summary": "Imports, reconciliation plans, tags, categorization rules, and audit context.",
      "tables": [
        "sources",
        "annotations",
        "rules",
        "statement_plans",
        "statement_plan_rows"
      ]
    },
    {
      "name": "Planning and control",
      "summary": "Budgets, goals, schedules, and closed accounting periods.",
      "tables": [
        "targets",
        "recurrences",
        "period_closes"
      ]
    },
    {
      "name": "Valuation and investments",
      "summary": "Asset conversion rates and investment cost-basis memory.",
      "tables": [
        "prices",
        "lots"
      ]
    },
    {
      "name": "Other",
      "summary": "Supporting schema objects.",
      "tables": [
        "ledger_operations",
        "ledger_operation_rows"
      ]
    }
  ],
  "tables": [
    {
      "name": "meta",
      "columns": [
        {
          "name": "key",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "key TEXT PRIMARY KEY",
          "description": "Metadata name. Primary key."
        },
        {
          "name": "value",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "value TEXT NOT NULL",
          "description": "Metadata value, stored as text."
        }
      ],
      "constraints": [],
      "family": "Identity",
      "summary": "Think of meta as a label stuck to the database file.",
      "connects": []
    },
    {
      "name": "migration_history",
      "columns": [
        {
          "name": "version",
          "type": "INTEGER",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "version INTEGER PRIMARY KEY",
          "description": "Schema version that was applied. Primary key."
        },
        {
          "name": "name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "name TEXT NOT NULL",
          "description": "Short migration name."
        },
        {
          "name": "applied_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "applied_at TEXT NOT NULL",
          "description": "Timestamp when the migration ran."
        }
      ],
      "constraints": [],
      "family": "Identity",
      "summary": "Think of migration_history as the upgrade receipt.",
      "connects": []
    },
    {
      "name": "books",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Book ID."
        },
        {
          "name": "name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": true,
          "references": null,
          "default": "",
          "check": "",
          "raw": "name TEXT NOT NULL UNIQUE",
          "description": "Human name. Unique."
        },
        {
          "name": "type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "type IN ('actual', 'scenario')",
          "raw": "type TEXT NOT NULL CHECK(type IN ('actual', 'scenario'))",
          "description": "Either actual or scenario."
        },
        {
          "name": "parent_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": {
            "table": "books",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "parent_id TEXT REFERENCES books(id)",
          "description": "Optional parent book."
        },
        {
          "name": "created_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_at TEXT NOT NULL",
          "description": "Creation timestamp."
        },
        {
          "name": "closed_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "closed_at TEXT",
          "description": "Scenario close/discard timestamp."
        }
      ],
      "constraints": [],
      "family": "Identity",
      "summary": "Think of a book as a ledger container.",
      "connects": [
        "accounts",
        "sources",
        "journals",
        "prices",
        "annotations",
        "rules",
        "targets",
        "recurrences",
        "period_closes",
        "lots",
        "statement_plans"
      ]
    },
    {
      "name": "assets",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Asset ID."
        },
        {
          "name": "symbol",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": true,
          "references": null,
          "default": "",
          "check": "",
          "raw": "symbol TEXT NOT NULL UNIQUE",
          "description": "Unique uppercase symbol, like CAD or MSFT."
        },
        {
          "name": "type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "type IN ('currency', 'commodity', 'custom', 'security')",
          "raw": "type TEXT NOT NULL CHECK(type IN ('currency', 'commodity', 'custom', 'security'))",
          "description": "currency, commodity, custom, or security."
        },
        {
          "name": "scale",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "scale >= 0 AND scale <= 18 AND scale = CAST(scale AS INTEGER)",
          "raw": "scale INTEGER NOT NULL CHECK(scale >= 0 AND scale <= 18 AND scale = CAST(scale AS INTEGER))",
          "description": "Decimal places used for integer storage."
        },
        {
          "name": "name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "name TEXT NOT NULL DEFAULT ''",
          "description": "Human name."
        }
      ],
      "constraints": [],
      "family": "Accounting core",
      "summary": "Assets are the units used by transactions.",
      "connects": [
        "journal_lines.asset_id",
        "prices.asset_id",
        "prices.quote_asset_id",
        "targets.asset_id",
        "recurrences.asset_id",
        "lots.asset_id",
        "lots.cost_asset_id"
      ]
    },
    {
      "name": "accounts",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Account ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "name TEXT NOT NULL",
          "description": "Account name, unique inside the book."
        },
        {
          "name": "type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "type IN ('asset', 'liability', 'equity', 'income', 'expense')",
          "raw": "type TEXT NOT NULL CHECK(type IN ('asset', 'liability', 'equity', 'income', 'expense'))",
          "description": "asset, liability, equity, income, or expense."
        },
        {
          "name": "parent_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "parent_id TEXT",
          "description": "Optional parent account."
        },
        {
          "name": "default_asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "default_asset_id TEXT REFERENCES assets(id)",
          "description": "Optional default asset/currency for tools that need an asset."
        },
        {
          "name": "code",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "code TEXT NOT NULL DEFAULT ''",
          "description": "Optional chart-of-accounts code."
        },
        {
          "name": "color_hex",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "#888888",
          "check": "",
          "raw": "color_hex TEXT NOT NULL DEFAULT '#888888'",
          "description": "Display color."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "active",
          "check": "",
          "raw": "status TEXT NOT NULL DEFAULT 'active'",
          "description": "Lifecycle marker, default active."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "UNIQUE(book_id, name)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(parent_id, book_id) REFERENCES accounts(id, book_id)"
      ],
      "family": "Accounting core",
      "summary": "Accounts are the buckets.",
      "connects": [
        "journal_lines.account_id",
        "targets.account_id",
        "recurrences.from_account_id",
        "recurrences.to_account_id",
        "rules.account_id",
        "lots.account_id"
      ]
    },
    {
      "name": "sources",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Source ID. Import batches usually use batch_...."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "books",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL REFERENCES books(id)",
          "description": "Owning book."
        },
        {
          "name": "type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "type TEXT NOT NULL",
          "description": "Source type, often import."
        },
        {
          "name": "label",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "label TEXT NOT NULL DEFAULT ''",
          "description": "Human label."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "open",
          "check": "",
          "raw": "status TEXT NOT NULL DEFAULT 'open'",
          "description": "Workflow status."
        },
        {
          "name": "created_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_at TEXT NOT NULL",
          "description": "Creation timestamp."
        },
        {
          "name": "metadata_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "metadata_json TEXT NOT NULL DEFAULT '{}'",
          "description": "JSON metadata string."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)"
      ],
      "family": "Workflow memory",
      "summary": "Sources remember where transactions came from.",
      "connects": [
        "journals.source_id"
      ]
    },
    {
      "name": "journals",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Transaction ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "source_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "source_id TEXT",
          "description": "Optional import/source batch."
        },
        {
          "name": "date",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "date TEXT NOT NULL",
          "description": "Economic date, YYYY-MM-DD."
        },
        {
          "name": "posted_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "posted_at TEXT NOT NULL",
          "description": "Time the row was inserted."
        },
        {
          "name": "finalized_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "finalized_at TEXT",
          "description": "Time the journal became part of the public ledger. Null means draft."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "status IN ('posted', 'pending', 'planned', 'void')",
          "raw": "status TEXT NOT NULL CHECK(status IN ('posted', 'pending', 'planned', 'void'))",
          "description": "posted, pending, planned, or void."
        },
        {
          "name": "description",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "description TEXT NOT NULL DEFAULT ''",
          "description": "Payee/memo text."
        },
        {
          "name": "external_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "external_id TEXT",
          "description": "Optional external source row ID."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(source_id, book_id) REFERENCES sources(id, book_id)"
      ],
      "family": "Accounting core",
      "summary": "journals are transaction headers.",
      "connects": [
        "journal_lines.journal_id",
        "sources.id",
        "lots.opened_journal_id",
        "lots.closed_journal_id"
      ]
    },
    {
      "name": "journal_lines",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Line ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "journal_id TEXT NOT NULL",
          "description": "Parent transaction."
        },
        {
          "name": "line_no",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "line_no INTEGER NOT NULL",
          "description": "1-based order within the transaction."
        },
        {
          "name": "account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "account_id TEXT NOT NULL",
          "description": "Account that changed."
        },
        {
          "name": "asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Unit/currency of the quantity."
        },
        {
          "name": "quantity",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "quantity INTEGER NOT NULL",
          "description": "Signed integer atomic units."
        },
        {
          "name": "memo",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "memo TEXT NOT NULL DEFAULT ''",
          "description": "Optional line memo."
        }
      ],
      "constraints": [
        "UNIQUE(journal_id, line_no)",
        "FOREIGN KEY(journal_id, book_id) REFERENCES journals(id, book_id) ON DELETE CASCADE",
        "FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)"
      ],
      "family": "Accounting core",
      "summary": "journal_lines are the actual accounting facts.",
      "connects": [
        "journals",
        "accounts",
        "assets"
      ]
    },
    {
      "name": "prices",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Price ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "books",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL REFERENCES books(id)",
          "description": "Owning book."
        },
        {
          "name": "asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Asset being priced."
        },
        {
          "name": "quote_asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "quote_asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Asset used as the quote."
        },
        {
          "name": "rate_value",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "rate_value > 0",
          "raw": "rate_value INTEGER NOT NULL CHECK(rate_value > 0)",
          "description": "Positive integer rate coefficient."
        },
        {
          "name": "rate_scale",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "rate_scale >= 0 AND rate_scale <= 18 AND rate_scale = CAST(rate_scale AS INTEGER)",
          "raw": "rate_scale INTEGER NOT NULL CHECK(rate_scale >= 0 AND rate_scale <= 18 AND rate_scale = CAST(rate_scale AS INTEGER))",
          "description": "Decimal places for rate_value."
        },
        {
          "name": "time",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "time TEXT NOT NULL",
          "description": "Effective date/time."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)"
      ],
      "family": "Valuation and investments",
      "summary": "Prices tell Clovis how to convert one asset into another.",
      "connects": []
    },
    {
      "name": "annotations",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Annotation ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "books",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL REFERENCES books(id)",
          "description": "Owning book."
        },
        {
          "name": "entity_type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "entity_type TEXT NOT NULL",
          "description": "What kind of thing is tagged: account, tx, book, etc."
        },
        {
          "name": "entity_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "entity_id TEXT NOT NULL",
          "description": "ID of the thing being tagged."
        },
        {
          "name": "key",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "key TEXT NOT NULL",
          "description": "Tag key."
        },
        {
          "name": "value",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "value TEXT NOT NULL",
          "description": "Tag value."
        }
      ],
      "constraints": [],
      "family": "Workflow memory",
      "summary": "Annotations are sticky notes.",
      "connects": [
        "many entity types by convention"
      ]
    },
    {
      "name": "rules",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Rule ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "type TEXT NOT NULL",
          "description": "Rule kind, commonly match."
        },
        {
          "name": "account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "account_id TEXT",
          "description": "Target account."
        },
        {
          "name": "pattern",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "pattern TEXT NOT NULL",
          "description": "Text pattern to match."
        },
        {
          "name": "priority",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "100",
          "check": "",
          "raw": "priority INTEGER NOT NULL DEFAULT 100",
          "description": "Evaluation order."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "active",
          "check": "",
          "raw": "status TEXT NOT NULL DEFAULT 'active'",
          "description": "active or soft-deleted state."
        },
        {
          "name": "created_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_at TEXT NOT NULL",
          "description": "Creation timestamp."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)"
      ],
      "family": "Workflow memory",
      "summary": "Rules help categorize transactions.",
      "connects": [
        "accounts"
      ]
    },
    {
      "name": "targets",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Target ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "type IN ('budget', 'goal')",
          "raw": "type TEXT NOT NULL CHECK(type IN ('budget', 'goal'))",
          "description": "budget or goal."
        },
        {
          "name": "account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "account_id TEXT NOT NULL",
          "description": "Account being planned for."
        },
        {
          "name": "asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Unit/currency."
        },
        {
          "name": "quantity",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "(type = 'budget' AND quantity >= 0) OR (type = 'goal' AND quantity > 0)",
          "raw": "quantity INTEGER NOT NULL CHECK((type = 'budget' AND quantity >= 0) OR (type = 'goal' AND quantity > 0))",
          "description": "Budget or goal amount in atomic units."
        },
        {
          "name": "period",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "period IS NULL OR period IN ('monthly', 'yearly')",
          "raw": "period TEXT CHECK(period IS NULL OR period IN ('monthly', 'yearly'))",
          "description": "monthly, yearly, or null. Mostly for budgets."
        },
        {
          "name": "year",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "year INTEGER",
          "description": "Optional budget year."
        },
        {
          "name": "month",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "month IS NULL OR (month >= 1 AND month <= 12)",
          "raw": "month INTEGER CHECK(month IS NULL OR (month >= 1 AND month <= 12))",
          "description": "Optional budget month."
        },
        {
          "name": "rollover_rule",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "rollover_rule TEXT NOT NULL DEFAULT ''",
          "description": "Budget rollover marker."
        },
        {
          "name": "name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "name TEXT NOT NULL DEFAULT ''",
          "description": "Goal name."
        },
        {
          "name": "target_date",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "target_date TEXT",
          "description": "Goal date."
        },
        {
          "name": "priority",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "1",
          "check": "",
          "raw": "priority INTEGER NOT NULL DEFAULT 1",
          "description": "Goal ordering."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "active",
          "check": "",
          "raw": "status TEXT NOT NULL DEFAULT 'active'",
          "description": "Lifecycle marker."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)"
      ],
      "family": "Planning and control",
      "summary": "Targets store budgets and goals.",
      "connects": [
        "accounts",
        "assets"
      ]
    },
    {
      "name": "recurrences",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Recurrence ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "next_date",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "next_date TEXT NOT NULL",
          "description": "Next scheduled date."
        },
        {
          "name": "quantity",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "quantity > 0",
          "raw": "quantity INTEGER NOT NULL CHECK(quantity > 0)",
          "description": "Positive atomic amount."
        },
        {
          "name": "from_account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "from_account_id TEXT NOT NULL",
          "description": "Source account."
        },
        {
          "name": "to_account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "to_account_id TEXT NOT NULL",
          "description": "Destination account."
        },
        {
          "name": "description",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "description TEXT NOT NULL DEFAULT ''",
          "description": "Transaction description."
        },
        {
          "name": "frequency",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "frequency IN ('daily', 'weekly', 'monthly', 'yearly')",
          "raw": "frequency TEXT NOT NULL CHECK(frequency IN ('daily', 'weekly', 'monthly', 'yearly'))",
          "description": "daily, weekly, monthly, or yearly."
        },
        {
          "name": "end_date",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "end_date TEXT",
          "description": "Optional end date."
        },
        {
          "name": "asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Unit/currency."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "active",
          "check": "status IN ('active', 'paused', 'deleted')",
          "raw": "status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'paused', 'deleted'))",
          "description": "active, paused, or deleted."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(from_account_id, book_id) REFERENCES accounts(id, book_id)",
        "FOREIGN KEY(to_account_id, book_id) REFERENCES accounts(id, book_id)"
      ],
      "family": "Planning and control",
      "summary": "Recurrences are scheduled transaction templates.",
      "connects": [
        "accounts",
        "assets"
      ]
    },
    {
      "name": "period_closes",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Period close ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "books",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL REFERENCES books(id)",
          "description": "Owning book."
        },
        {
          "name": "name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "name TEXT NOT NULL",
          "description": "Human checkpoint name."
        },
        {
          "name": "as_of",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "as_of TEXT NOT NULL",
          "description": "Closed-through date."
        },
        {
          "name": "description",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "description TEXT",
          "description": "Optional note."
        },
        {
          "name": "created_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_at TEXT NOT NULL",
          "description": "Creation timestamp."
        },
        {
          "name": "reopened_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "reopened_at TEXT",
          "description": "Reopen timestamp, null while still closed."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)"
      ],
      "family": "Planning and control",
      "summary": "Period closes are locks on old accounting periods.",
      "connects": [
        "books"
      ]
    },
    {
      "name": "lots",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Lot ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "account_id TEXT NOT NULL",
          "description": "Holding account."
        },
        {
          "name": "asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Held asset/security."
        },
        {
          "name": "quantity",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "quantity > 0",
          "raw": "quantity INTEGER NOT NULL CHECK(quantity > 0)",
          "description": "Positive held quantity."
        },
        {
          "name": "cost_asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "cost_asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Asset used to measure cost."
        },
        {
          "name": "cost_quantity",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "cost_quantity > 0",
          "raw": "cost_quantity INTEGER NOT NULL CHECK(cost_quantity > 0)",
          "description": "Positive cost amount."
        },
        {
          "name": "opened_journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "opened_journal_id TEXT NOT NULL",
          "description": "Transaction that opened the lot."
        },
        {
          "name": "closed_journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "closed_journal_id TEXT",
          "description": "Transaction that closed the lot, if any."
        },
        {
          "name": "opened_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "opened_at TEXT NOT NULL",
          "description": "Open date."
        },
        {
          "name": "closed_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "closed_at TEXT",
          "description": "Close date."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "open",
          "check": "status IN ('open', 'closed')",
          "raw": "status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'closed'))",
          "description": "open or closed."
        },
        {
          "name": "metadata_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "metadata_json TEXT NOT NULL DEFAULT '{}'",
          "description": "JSON metadata string."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)",
        "FOREIGN KEY(opened_journal_id, book_id) REFERENCES journals(id, book_id)",
        "FOREIGN KEY(closed_journal_id, book_id) REFERENCES journals(id, book_id)"
      ],
      "family": "Valuation and investments",
      "summary": "Lots track investment cost basis.",
      "connects": [
        "accounts",
        "assets",
        "journals"
      ]
    },
    {
      "name": "statement_plans",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Statement plan ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "account_id TEXT NOT NULL",
          "description": "Statement account being reconciled."
        },
        {
          "name": "asset_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": {
            "table": "assets",
            "columns": [
              "id"
            ]
          },
          "default": "",
          "check": "",
          "raw": "asset_id TEXT NOT NULL REFERENCES assets(id)",
          "description": "Currency/unit for the plan."
        },
        {
          "name": "source_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "source_id TEXT",
          "description": "Import batch/source created when the plan is applied."
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "planned",
          "check": "status IN ('planned', 'applied', 'discarded')",
          "raw": "status TEXT NOT NULL DEFAULT 'planned' CHECK(status IN ('planned', 'applied', 'discarded'))",
          "description": "planned, applied, or discarded."
        },
        {
          "name": "statement_kind",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "statement_kind TEXT NOT NULL DEFAULT ''",
          "description": "File/workflow kind, such as bank, card, QFX, or CSV."
        },
        {
          "name": "file_name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "file_name TEXT NOT NULL DEFAULT ''",
          "description": "Source filename, not the full local path."
        },
        {
          "name": "file_sha256",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "file_sha256 TEXT NOT NULL DEFAULT ''",
          "description": "Hash of the source file content."
        },
        {
          "name": "expected_balance",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "expected_balance INTEGER",
          "description": "Optional outside balance supplied by the statement."
        },
        {
          "name": "planned_balance",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "planned_balance INTEGER NOT NULL",
          "description": "Ledger balance after applying the plan."
        },
        {
          "name": "applied_balance",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "applied_balance INTEGER",
          "description": "Actual balance recorded after apply."
        },
        {
          "name": "created_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_at TEXT NOT NULL",
          "description": "Plan creation timestamp."
        },
        {
          "name": "applied_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "applied_at TEXT",
          "description": "Apply timestamp, null until applied."
        },
        {
          "name": "discarded_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "discarded_at TEXT",
          "description": "Discard timestamp, null unless discarded."
        },
        {
          "name": "metadata_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "metadata_json TEXT NOT NULL DEFAULT '{}'",
          "description": "JSON options used to build the plan."
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)",
        "FOREIGN KEY(source_id, book_id) REFERENCES sources(id, book_id)"
      ],
      "family": "Workflow memory",
      "summary": "Think of a statement plan as a locked worksheet.",
      "connects": [
        "accounts",
        "assets",
        "sources",
        "statement_plan_rows"
      ]
    },
    {
      "name": "statement_plan_rows",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": "Plan row ID."
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": "Owning book."
        },
        {
          "name": "plan_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "plan_id TEXT NOT NULL",
          "description": "Parent statement plan."
        },
        {
          "name": "row_index",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "row_index INTEGER NOT NULL",
          "description": "Source-row order inside the plan."
        },
        {
          "name": "date",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "date TEXT NOT NULL",
          "description": "Statement row date."
        },
        {
          "name": "quantity",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "quantity INTEGER NOT NULL",
          "description": "Signed atomic quantity from the statement account's view."
        },
        {
          "name": "description",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "description TEXT NOT NULL DEFAULT ''",
          "description": "Statement row description."
        },
        {
          "name": "external_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "external_id TEXT",
          "description": "Stable statement ID such as QFX/OFX FITID, if present."
        },
        {
          "name": "row_hash",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "row_hash TEXT NOT NULL",
          "description": "Hash of the normalized source row."
        },
        {
          "name": "action",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "action IN ('matched', 'pending_to_commit', 'new_posted', 'new_pending', 'stale_pending_to_void', 'ambiguous', 'ignored')",
          "raw": "action TEXT NOT NULL CHECK(action IN ('matched', 'pending_to_commit', 'new_posted', 'new_pending', 'stale_pending_to_void', 'ambiguous', 'ignored'))",
          "description": "Planned action."
        },
        {
          "name": "matched_journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "matched_journal_id TEXT",
          "description": "Existing journal used by matched, pending_to_commit, or stale-pending actions."
        },
        {
          "name": "created_journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_journal_id TEXT",
          "description": "Journal created when the row is applied."
        },
        {
          "name": "counterpart_account_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "counterpart_account_id TEXT",
          "description": "Other side of a new transaction, if needed."
        },
        {
          "name": "reason",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "reason TEXT NOT NULL DEFAULT ''",
          "description": "Human-readable reason for the action."
        },
        {
          "name": "metadata_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "metadata_json TEXT NOT NULL DEFAULT '{}'",
          "description": "Source amount, tags, and parser context."
        }
      ],
      "constraints": [
        "UNIQUE(plan_id, row_index)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(plan_id, book_id) REFERENCES statement_plans(id, book_id) ON DELETE CASCADE",
        "FOREIGN KEY(matched_journal_id, book_id) REFERENCES journals(id, book_id)",
        "FOREIGN KEY(created_journal_id, book_id) REFERENCES journals(id, book_id)",
        "FOREIGN KEY(counterpart_account_id, book_id) REFERENCES accounts(id, book_id)"
      ],
      "family": "Workflow memory",
      "summary": "Think of statement plan rows as the individual marks on the worksheet.",
      "connects": [
        "statement_plans",
        "journals",
        "accounts"
      ]
    },
    {
      "name": "ledger_operations",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": ""
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "tool_name",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "tool_name TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "operation_type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "operation_type TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "status",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "applied",
          "check": "status IN ('applied', 'reversed')",
          "raw": "status TEXT NOT NULL DEFAULT 'applied' CHECK(status IN ('applied', 'reversed'))",
          "description": ""
        },
        {
          "name": "created_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "created_at TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "reversed_at",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "reversed_at TEXT",
          "description": ""
        },
        {
          "name": "reversed_by_operation_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "reversed_by_operation_id TEXT",
          "description": ""
        },
        {
          "name": "reverses_operation_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "reverses_operation_id TEXT",
          "description": ""
        },
        {
          "name": "input_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "input_json TEXT NOT NULL DEFAULT '{}'",
          "description": ""
        },
        {
          "name": "preview_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "preview_json TEXT NOT NULL DEFAULT '{}'",
          "description": ""
        },
        {
          "name": "result_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "result_json TEXT NOT NULL DEFAULT '{}'",
          "description": ""
        },
        {
          "name": "metadata_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "metadata_json TEXT NOT NULL DEFAULT '{}'",
          "description": ""
        }
      ],
      "constraints": [
        "UNIQUE(id, book_id)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(reversed_by_operation_id, book_id) REFERENCES ledger_operations(id, book_id)",
        "FOREIGN KEY(reverses_operation_id, book_id) REFERENCES ledger_operations(id, book_id)"
      ],
      "family": "Other",
      "summary": "",
      "connects": []
    },
    {
      "name": "ledger_operation_rows",
      "columns": [
        {
          "name": "id",
          "type": "TEXT",
          "primaryKey": true,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "id TEXT PRIMARY KEY",
          "description": ""
        },
        {
          "name": "book_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "book_id TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "operation_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "operation_id TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "row_index",
          "type": "INTEGER",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "row_index INTEGER NOT NULL",
          "description": ""
        },
        {
          "name": "entity_type",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "entity_type TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "entity_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "entity_id TEXT NOT NULL",
          "description": ""
        },
        {
          "name": "action",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "",
          "check": "action IN ('insert', 'update', 'delete', 'correction', 'reverse')",
          "raw": "action TEXT NOT NULL CHECK(action IN ('insert', 'update', 'delete', 'correction', 'reverse'))",
          "description": ""
        },
        {
          "name": "before_hash",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "before_hash TEXT",
          "description": ""
        },
        {
          "name": "after_hash",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "after_hash TEXT",
          "description": ""
        },
        {
          "name": "before_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "before_json TEXT",
          "description": ""
        },
        {
          "name": "after_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "after_json TEXT",
          "description": ""
        },
        {
          "name": "correction_journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "correction_journal_id TEXT",
          "description": ""
        },
        {
          "name": "reverse_journal_id",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": false,
          "unique": false,
          "references": null,
          "default": "",
          "check": "",
          "raw": "reverse_journal_id TEXT",
          "description": ""
        },
        {
          "name": "metadata_json",
          "type": "TEXT",
          "primaryKey": false,
          "notNull": true,
          "unique": false,
          "references": null,
          "default": "{}",
          "check": "",
          "raw": "metadata_json TEXT NOT NULL DEFAULT '{}'",
          "description": ""
        }
      ],
      "constraints": [
        "UNIQUE(operation_id, row_index)",
        "FOREIGN KEY(book_id) REFERENCES books(id)",
        "FOREIGN KEY(operation_id, book_id) REFERENCES ledger_operations(id, book_id) ON DELETE CASCADE",
        "FOREIGN KEY(correction_journal_id, book_id) REFERENCES journals(id, book_id)",
        "FOREIGN KEY(reverse_journal_id, book_id) REFERENCES journals(id, book_id)"
      ],
      "family": "Other",
      "summary": "",
      "connects": []
    }
  ],
  "relationships": [
    {
      "id": "accounts_parent_id_book_id_to_accounts_id_book_id",
      "fromTable": "accounts",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "parent_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "parent",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "accounts.parent_id, book_id references accounts.id, book_id"
    },
    {
      "id": "journal_lines_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "journal_lines",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "account",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "journal_lines.account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "lots_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "lots",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "account",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "lots.account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "recurrences_from_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "recurrences",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "from_account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "from account",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "recurrences.from_account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "recurrences_to_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "recurrences",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "to_account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "to account",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "recurrences.to_account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "rules_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "rules",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "account",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "rules.account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "statement_plan_rows_counterpart_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "statement_plan_rows",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "counterpart_account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "counterpart account",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plan_rows.counterpart_account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "statement_plans_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "statement_plans",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "account",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plans.account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "targets_account_id_book_id_to_accounts_id_book_id",
      "fromTable": "targets",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "account_id",
        "book_id"
      ],
      "toTable": "accounts",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "account",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "targets.account_id, book_id references accounts.id, book_id"
    },
    {
      "id": "accounts_default_asset_id_to_assets_id",
      "fromTable": "accounts",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "default_asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "default asset",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "accounts.default_asset_id references assets.id"
    },
    {
      "id": "journal_lines_asset_id_to_assets_id",
      "fromTable": "journal_lines",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "journal_lines.asset_id references assets.id"
    },
    {
      "id": "lots_asset_id_to_assets_id",
      "fromTable": "lots",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "lots.asset_id references assets.id"
    },
    {
      "id": "lots_cost_asset_id_to_assets_id",
      "fromTable": "lots",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "cost_asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "cost asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "lots.cost_asset_id references assets.id"
    },
    {
      "id": "prices_asset_id_to_assets_id",
      "fromTable": "prices",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "prices.asset_id references assets.id"
    },
    {
      "id": "prices_quote_asset_id_to_assets_id",
      "fromTable": "prices",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "quote_asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "quote asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "prices.quote_asset_id references assets.id"
    },
    {
      "id": "recurrences_asset_id_to_assets_id",
      "fromTable": "recurrences",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "recurrences.asset_id references assets.id"
    },
    {
      "id": "statement_plans_asset_id_to_assets_id",
      "fromTable": "statement_plans",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "statement_plans.asset_id references assets.id"
    },
    {
      "id": "targets_asset_id_to_assets_id",
      "fromTable": "targets",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "asset_id"
      ],
      "toTable": "assets",
      "toFamily": "Accounting core",
      "toColumns": [
        "id"
      ],
      "role": "asset",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "targets.asset_id references assets.id"
    },
    {
      "id": "accounts_book_id_to_books_id",
      "fromTable": "accounts",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "accounts.book_id references books.id"
    },
    {
      "id": "annotations_book_id_to_books_id",
      "fromTable": "annotations",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "annotations.book_id references books.id"
    },
    {
      "id": "books_parent_id_to_books_id",
      "fromTable": "books",
      "fromFamily": "Identity",
      "fromColumns": [
        "parent_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "parent",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "books.parent_id references books.id"
    },
    {
      "id": "journals_book_id_to_books_id",
      "fromTable": "journals",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "journals.book_id references books.id"
    },
    {
      "id": "ledger_operation_rows_book_id_to_books_id",
      "fromTable": "ledger_operation_rows",
      "fromFamily": "Other",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "ledger_operation_rows.book_id references books.id"
    },
    {
      "id": "ledger_operations_book_id_to_books_id",
      "fromTable": "ledger_operations",
      "fromFamily": "Other",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "ledger_operations.book_id references books.id"
    },
    {
      "id": "lots_book_id_to_books_id",
      "fromTable": "lots",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "lots.book_id references books.id"
    },
    {
      "id": "period_closes_book_id_to_books_id",
      "fromTable": "period_closes",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "period_closes.book_id references books.id"
    },
    {
      "id": "prices_book_id_to_books_id",
      "fromTable": "prices",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "prices.book_id references books.id"
    },
    {
      "id": "recurrences_book_id_to_books_id",
      "fromTable": "recurrences",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "recurrences.book_id references books.id"
    },
    {
      "id": "rules_book_id_to_books_id",
      "fromTable": "rules",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "rules.book_id references books.id"
    },
    {
      "id": "sources_book_id_to_books_id",
      "fromTable": "sources",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "column",
      "summary": "sources.book_id references books.id"
    },
    {
      "id": "statement_plan_rows_book_id_to_books_id",
      "fromTable": "statement_plan_rows",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plan_rows.book_id references books.id"
    },
    {
      "id": "statement_plans_book_id_to_books_id",
      "fromTable": "statement_plans",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plans.book_id references books.id"
    },
    {
      "id": "targets_book_id_to_books_id",
      "fromTable": "targets",
      "fromFamily": "Planning and control",
      "fromColumns": [
        "book_id"
      ],
      "toTable": "books",
      "toFamily": "Identity",
      "toColumns": [
        "id"
      ],
      "role": "book",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": false,
      "onDelete": "",
      "source": "constraint",
      "summary": "targets.book_id references books.id"
    },
    {
      "id": "journal_lines_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "journal_lines",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "journal",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "cascade",
      "source": "constraint",
      "summary": "journal_lines.journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "ledger_operation_rows_correction_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "ledger_operation_rows",
      "fromFamily": "Other",
      "fromColumns": [
        "correction_journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "correction journal",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "ledger_operation_rows.correction_journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "ledger_operation_rows_reverse_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "ledger_operation_rows",
      "fromFamily": "Other",
      "fromColumns": [
        "reverse_journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "reverse journal",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "ledger_operation_rows.reverse_journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "lots_closed_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "lots",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "closed_journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "closed journal",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "lots.closed_journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "lots_opened_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "lots",
      "fromFamily": "Valuation and investments",
      "fromColumns": [
        "opened_journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "opened journal",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "lots.opened_journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "statement_plan_rows_created_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "statement_plan_rows",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "created_journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "created journal",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plan_rows.created_journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "statement_plan_rows_matched_journal_id_book_id_to_journals_id_book_id",
      "fromTable": "statement_plan_rows",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "matched_journal_id",
        "book_id"
      ],
      "toTable": "journals",
      "toFamily": "Accounting core",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "matched journal",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plan_rows.matched_journal_id, book_id references journals.id, book_id"
    },
    {
      "id": "ledger_operation_rows_operation_id_book_id_to_ledger_operations_id_book_id",
      "fromTable": "ledger_operation_rows",
      "fromFamily": "Other",
      "fromColumns": [
        "operation_id",
        "book_id"
      ],
      "toTable": "ledger_operations",
      "toFamily": "Other",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "operation",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "cascade",
      "source": "constraint",
      "summary": "ledger_operation_rows.operation_id, book_id references ledger_operations.id, book_id"
    },
    {
      "id": "ledger_operations_reversed_by_operation_id_book_id_to_ledger_operations_id_book_id",
      "fromTable": "ledger_operations",
      "fromFamily": "Other",
      "fromColumns": [
        "reversed_by_operation_id",
        "book_id"
      ],
      "toTable": "ledger_operations",
      "toFamily": "Other",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "reversed by operation",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "ledger_operations.reversed_by_operation_id, book_id references ledger_operations.id, book_id"
    },
    {
      "id": "ledger_operations_reverses_operation_id_book_id_to_ledger_operations_id_book_id",
      "fromTable": "ledger_operations",
      "fromFamily": "Other",
      "fromColumns": [
        "reverses_operation_id",
        "book_id"
      ],
      "toTable": "ledger_operations",
      "toFamily": "Other",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "reverses operation",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "ledger_operations.reverses_operation_id, book_id references ledger_operations.id, book_id"
    },
    {
      "id": "journals_source_id_book_id_to_sources_id_book_id",
      "fromTable": "journals",
      "fromFamily": "Accounting core",
      "fromColumns": [
        "source_id",
        "book_id"
      ],
      "toTable": "sources",
      "toFamily": "Workflow memory",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "source",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "journals.source_id, book_id references sources.id, book_id"
    },
    {
      "id": "statement_plans_source_id_book_id_to_sources_id_book_id",
      "fromTable": "statement_plans",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "source_id",
        "book_id"
      ],
      "toTable": "sources",
      "toFamily": "Workflow memory",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "source",
      "cardinality": "1 -> many",
      "required": false,
      "optional": true,
      "composite": true,
      "onDelete": "",
      "source": "constraint",
      "summary": "statement_plans.source_id, book_id references sources.id, book_id"
    },
    {
      "id": "statement_plan_rows_plan_id_book_id_to_statement_plans_id_book_id",
      "fromTable": "statement_plan_rows",
      "fromFamily": "Workflow memory",
      "fromColumns": [
        "plan_id",
        "book_id"
      ],
      "toTable": "statement_plans",
      "toFamily": "Workflow memory",
      "toColumns": [
        "id",
        "book_id"
      ],
      "role": "plan",
      "cardinality": "1 -> many",
      "required": true,
      "optional": false,
      "composite": true,
      "onDelete": "cascade",
      "source": "constraint",
      "summary": "statement_plan_rows.plan_id, book_id references statement_plans.id, book_id"
    }
  ],
  "indexes": [
    {
      "name": "idx_journals_book_date",
      "table": "journals",
      "unique": false,
      "columns": "book_id, date, id",
      "where": ""
    },
    {
      "name": "idx_journals_status",
      "table": "journals",
      "unique": false,
      "columns": "status, date",
      "where": ""
    },
    {
      "name": "idx_lines_journal",
      "table": "journal_lines",
      "unique": false,
      "columns": "journal_id",
      "where": ""
    },
    {
      "name": "idx_lines_account_asset",
      "table": "journal_lines",
      "unique": false,
      "columns": "account_id, asset_id",
      "where": ""
    },
    {
      "name": "idx_prices_pair_time",
      "table": "prices",
      "unique": false,
      "columns": "book_id, asset_id, quote_asset_id, time",
      "where": ""
    },
    {
      "name": "idx_annotations_entity",
      "table": "annotations",
      "unique": false,
      "columns": "entity_type, entity_id, key",
      "where": ""
    },
    {
      "name": "idx_rules_type_status",
      "table": "rules",
      "unique": false,
      "columns": "type, status, priority",
      "where": ""
    },
    {
      "name": "idx_targets_budget",
      "table": "targets",
      "unique": true,
      "columns": "book_id, type, account_id, asset_id, period, coalesce(year, -1), coalesce(month, -1)",
      "where": "type = 'budget'"
    },
    {
      "name": "idx_targets_goal",
      "table": "targets",
      "unique": true,
      "columns": "book_id, type, account_id, asset_id",
      "where": "type = 'goal'"
    },
    {
      "name": "idx_period_closes_book_as_of",
      "table": "period_closes",
      "unique": false,
      "columns": "book_id, as_of",
      "where": ""
    },
    {
      "name": "idx_statement_plans_account_status",
      "table": "statement_plans",
      "unique": false,
      "columns": "book_id, account_id, status, created_at",
      "where": ""
    },
    {
      "name": "idx_statement_plan_rows_plan_action",
      "table": "statement_plan_rows",
      "unique": false,
      "columns": "plan_id, action, row_index",
      "where": ""
    },
    {
      "name": "idx_statement_plan_rows_hash",
      "table": "statement_plan_rows",
      "unique": false,
      "columns": "book_id, row_hash",
      "where": ""
    },
    {
      "name": "idx_ledger_operations_type_status",
      "table": "ledger_operations",
      "unique": false,
      "columns": "book_id, operation_type, status, created_at",
      "where": ""
    },
    {
      "name": "idx_ledger_operation_rows_operation",
      "table": "ledger_operation_rows",
      "unique": false,
      "columns": "operation_id, row_index",
      "where": ""
    }
  ],
  "triggers": [
    {
      "name": "trg_statement_plans_no_identity_update",
      "summary": "Statement plan identity fields cannot be edited after creation.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_statement_plans_no_identity_update BEFORE UPDATE OF book_id, account_id, asset_id, statement_kind, file_name, file_sha256, expected_balance, planned_balance, metadata_json, created_at ON statement_plans\nBEGIN\n  SELECT RAISE(ABORT, 'statement plan identity is immutable');END;"
    },
    {
      "name": "trg_statement_plans_status_transition",
      "summary": "Statement plans can only move from planned to applied or discarded.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_statement_plans_status_transition BEFORE UPDATE OF status ON statement_plans\nWHEN OLD.status != NEW.status\nBEGIN\n  SELECT CASE\n    WHEN OLD.status != 'planned'\n    THEN RAISE(ABORT, 'statement plan status is final')END;"
    },
    {
      "name": "trg_statement_plans_no_delete",
      "summary": "Statement plans are audit records and cannot be deleted.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_statement_plans_no_delete BEFORE DELETE ON statement_plans\nBEGIN\n  SELECT RAISE(ABORT, 'statement plans are audit records');END;"
    },
    {
      "name": "trg_statement_plan_rows_no_semantic_update",
      "summary": "Statement plan row decisions are immutable.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_statement_plan_rows_no_semantic_update BEFORE UPDATE OF book_id, plan_id, row_index, date, quantity, description, external_id, row_hash, action, matched_journal_id, counterpart_account_id, reason, metadata_json ON statement_plan_rows\nBEGIN\n  SELECT RAISE(ABORT, 'statement plan rows are immutable');END;"
    },
    {
      "name": "trg_statement_plan_rows_created_once",
      "summary": "A plan row can receive a created journal exactly once.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_statement_plan_rows_created_once BEFORE UPDATE OF created_journal_id ON statement_plan_rows\nWHEN OLD.created_journal_id IS NOT NULL OR NEW.created_journal_id IS NULL\nBEGIN\n  SELECT RAISE(ABORT, 'created_journal_id can only be set once');END;"
    },
    {
      "name": "trg_statement_plan_rows_no_delete",
      "summary": "Statement plan rows are audit records and cannot be deleted.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_statement_plan_rows_no_delete BEFORE DELETE ON statement_plan_rows\nBEGIN\n  SELECT RAISE(ABORT, 'statement plan rows are audit records');END;"
    },
    {
      "name": "trg_ledger_operations_no_identity_update",
      "summary": "ledger operations no identity update",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_ledger_operations_no_identity_update BEFORE UPDATE OF book_id, tool_name, operation_type, created_at, reverses_operation_id, input_json, preview_json, result_json, metadata_json ON ledger_operations\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operations are audit records');END;"
    },
    {
      "name": "trg_ledger_operations_status_transition",
      "summary": "ledger operations status transition",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_ledger_operations_status_transition BEFORE UPDATE OF status ON ledger_operations\nWHEN OLD.status != NEW.status\nBEGIN\n  SELECT CASE\n    WHEN OLD.status != 'applied' OR NEW.status != 'reversed'\n    THEN RAISE(ABORT, 'invalid ledger operation status transition')END;"
    },
    {
      "name": "trg_ledger_operations_no_delete",
      "summary": "ledger operations no delete",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_ledger_operations_no_delete BEFORE DELETE ON ledger_operations\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operations are audit records');END;"
    },
    {
      "name": "trg_ledger_operation_rows_no_update",
      "summary": "ledger operation rows no update",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_ledger_operation_rows_no_update BEFORE UPDATE ON ledger_operation_rows\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operation rows are immutable');END;"
    },
    {
      "name": "trg_ledger_operation_rows_no_delete",
      "summary": "ledger operation rows no delete",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_ledger_operation_rows_no_delete BEFORE DELETE ON ledger_operation_rows\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operation rows are audit records');END;"
    },
    {
      "name": "trg_journals_no_finalized_insert",
      "summary": "Journals must be inserted as drafts, then finalized.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_journals_no_finalized_insert BEFORE INSERT ON journals\nWHEN NEW.finalized_at IS NOT NULL\nBEGIN\n  SELECT RAISE(ABORT, 'insert journal as draft, then finalize');END;"
    },
    {
      "name": "trg_journals_finalize_requires_lines",
      "summary": "Finalization requires lines, per-asset balance, and an open period.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_journals_finalize_requires_lines BEFORE UPDATE OF finalized_at ON journals\nWHEN OLD.finalized_at IS NULL AND NEW.finalized_at IS NOT NULL\nBEGIN\n  SELECT CASE\n    WHEN NOT EXISTS (\n      SELECT 1 FROM journal_lines\n      WHERE book_id = NEW.book_id AND journal_id = NEW.id\n    )\n    THEN RAISE(ABORT, 'finalized journal must have lines')END;"
    },
    {
      "name": "trg_journals_reopen_guard",
      "summary": "Finalized journals cannot be reopened when period or lot rules forbid it.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_journals_reopen_guard BEFORE UPDATE OF finalized_at ON journals\nWHEN OLD.finalized_at IS NOT NULL AND NEW.finalized_at IS NULL\nBEGIN\n  SELECT CASE\n    WHEN EXISTS (\n      SELECT 1 FROM period_closes\n      WHERE book_id = OLD.book_id\n        AND reopened_at IS NULL\n        AND as_of >= OLD.date\n      LIMIT 1\n    )\n    THEN RAISE(ABORT, 'cannot reopen journal in a closed period')END;"
    },
    {
      "name": "trg_lines_no_insert_finalized",
      "summary": "Finalized journals cannot receive new lines.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_lines_no_insert_finalized BEFORE INSERT ON journal_lines\nWHEN EXISTS (\n  SELECT 1 FROM journals\n  WHERE book_id = NEW.book_id\n    AND id = NEW.journal_id\n    AND finalized_at IS NOT NULL\n)\nBEGIN\n  SELECT RAISE(ABORT, 'cannot insert lines on finalized journal');END;"
    },
    {
      "name": "trg_lines_no_update_finalized",
      "summary": "Finalized journal lines cannot be edited.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_lines_no_update_finalized BEFORE UPDATE ON journal_lines\nWHEN EXISTS (\n  SELECT 1 FROM journals\n  WHERE book_id = OLD.book_id\n    AND id = OLD.journal_id\n    AND finalized_at IS NOT NULL\n)\nBEGIN\n  SELECT RAISE(ABORT, 'cannot update lines on finalized journal');END;"
    },
    {
      "name": "trg_lines_no_delete_finalized",
      "summary": "Finalized journal lines cannot be deleted.",
      "raw": "CREATE TRIGGER IF NOT EXISTS trg_lines_no_delete_finalized BEFORE DELETE ON journal_lines\nWHEN EXISTS (\n  SELECT 1 FROM journals\n  WHERE book_id = OLD.book_id\n    AND id = OLD.journal_id\n    AND finalized_at IS NOT NULL\n)\nBEGIN\n  SELECT RAISE(ABORT, 'cannot delete lines on finalized journal');END;"
    }
  ],
  "ddl": "\nCREATE TABLE IF NOT EXISTS meta (\n  key TEXT PRIMARY KEY,\n  value TEXT NOT NULL\n);\nCREATE TABLE IF NOT EXISTS migration_history (\n  version INTEGER PRIMARY KEY,\n  name TEXT NOT NULL,\n  applied_at TEXT NOT NULL\n);\nCREATE TABLE IF NOT EXISTS books (\n  id TEXT PRIMARY KEY,\n  name TEXT NOT NULL UNIQUE,\n  type TEXT NOT NULL CHECK(type IN ('actual', 'scenario')),\n  parent_id TEXT REFERENCES books(id),\n  created_at TEXT NOT NULL,\n  closed_at TEXT\n);\nCREATE TABLE IF NOT EXISTS assets (\n  id TEXT PRIMARY KEY,\n  symbol TEXT NOT NULL UNIQUE,\n  type TEXT NOT NULL CHECK(type IN ('currency', 'commodity', 'custom', 'security')),\n  scale INTEGER NOT NULL CHECK(scale >= 0 AND scale <= 18 AND scale = CAST(scale AS INTEGER)),\n  name TEXT NOT NULL DEFAULT ''\n);\nCREATE TABLE IF NOT EXISTS accounts (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  name TEXT NOT NULL,\n  type TEXT NOT NULL CHECK(type IN ('asset', 'liability', 'equity', 'income', 'expense')),\n  parent_id TEXT,\n  default_asset_id TEXT REFERENCES assets(id),\n  code TEXT NOT NULL DEFAULT '',\n  color_hex TEXT NOT NULL DEFAULT '#888888',\n  status TEXT NOT NULL DEFAULT 'active',\n  UNIQUE(id, book_id),\n  UNIQUE(book_id, name),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(parent_id, book_id) REFERENCES accounts(id, book_id)\n);\nCREATE TABLE IF NOT EXISTS sources (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL REFERENCES books(id),\n  type TEXT NOT NULL,\n  label TEXT NOT NULL DEFAULT '',\n  status TEXT NOT NULL DEFAULT 'open',\n  created_at TEXT NOT NULL,\n  metadata_json TEXT NOT NULL DEFAULT '{}',\n  UNIQUE(id, book_id)\n);\nCREATE TABLE IF NOT EXISTS journals (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  source_id TEXT,\n  date TEXT NOT NULL,\n  posted_at TEXT NOT NULL,\n  finalized_at TEXT,\n  status TEXT NOT NULL CHECK(status IN ('posted', 'pending', 'planned', 'void')),\n  description TEXT NOT NULL DEFAULT '',\n  external_id TEXT,\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(source_id, book_id) REFERENCES sources(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_journals_book_date ON journals(book_id, date, id);\nCREATE INDEX IF NOT EXISTS idx_journals_status ON journals(status, date);\nCREATE TABLE IF NOT EXISTS journal_lines (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  journal_id TEXT NOT NULL,\n  line_no INTEGER NOT NULL,\n  account_id TEXT NOT NULL,\n  asset_id TEXT NOT NULL REFERENCES assets(id),\n  quantity INTEGER NOT NULL,\n  memo TEXT NOT NULL DEFAULT '',\n  UNIQUE(journal_id, line_no),\n  FOREIGN KEY(journal_id, book_id) REFERENCES journals(id, book_id) ON DELETE CASCADE,\n  FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_lines_journal ON journal_lines(journal_id);\nCREATE INDEX IF NOT EXISTS idx_lines_account_asset ON journal_lines(account_id, asset_id);\nCREATE TABLE IF NOT EXISTS prices (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL REFERENCES books(id),\n  asset_id TEXT NOT NULL REFERENCES assets(id),\n  quote_asset_id TEXT NOT NULL REFERENCES assets(id),\n  rate_value INTEGER NOT NULL CHECK(rate_value > 0),\n  rate_scale INTEGER NOT NULL CHECK(rate_scale >= 0 AND rate_scale <= 18 AND rate_scale = CAST(rate_scale AS INTEGER)),\n  time TEXT NOT NULL,\n  UNIQUE(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_prices_pair_time ON prices(book_id, asset_id, quote_asset_id, time);\nCREATE TABLE IF NOT EXISTS annotations (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL REFERENCES books(id),\n  entity_type TEXT NOT NULL,\n  entity_id TEXT NOT NULL,\n  key TEXT NOT NULL,\n  value TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_annotations_entity ON annotations(entity_type, entity_id, key);\nCREATE TABLE IF NOT EXISTS rules (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  type TEXT NOT NULL,\n  account_id TEXT,\n  pattern TEXT NOT NULL,\n  priority INTEGER NOT NULL DEFAULT 100,\n  status TEXT NOT NULL DEFAULT 'active',\n  created_at TEXT NOT NULL,\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_rules_type_status ON rules(type, status, priority);\nCREATE TABLE IF NOT EXISTS targets (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  type TEXT NOT NULL CHECK(type IN ('budget', 'goal')),\n  account_id TEXT NOT NULL,\n  asset_id TEXT NOT NULL REFERENCES assets(id),\n  quantity INTEGER NOT NULL CHECK((type = 'budget' AND quantity >= 0) OR (type = 'goal' AND quantity > 0)),\n  period TEXT CHECK(period IS NULL OR period IN ('monthly', 'yearly')),\n  year INTEGER,\n  month INTEGER CHECK(month IS NULL OR (month >= 1 AND month <= 12)),\n  rollover_rule TEXT NOT NULL DEFAULT '',\n  name TEXT NOT NULL DEFAULT '',\n  target_date TEXT,\n  priority INTEGER NOT NULL DEFAULT 1,\n  status TEXT NOT NULL DEFAULT 'active',\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS idx_targets_budget ON targets(book_id, type, account_id, asset_id, period, coalesce(year, -1), coalesce(month, -1))\n  WHERE type = 'budget';\nCREATE UNIQUE INDEX IF NOT EXISTS idx_targets_goal ON targets(book_id, type, account_id, asset_id)\n  WHERE type = 'goal';\nCREATE TABLE IF NOT EXISTS recurrences (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  next_date TEXT NOT NULL,\n  quantity INTEGER NOT NULL CHECK(quantity > 0),\n  from_account_id TEXT NOT NULL,\n  to_account_id TEXT NOT NULL,\n  description TEXT NOT NULL DEFAULT '',\n  frequency TEXT NOT NULL CHECK(frequency IN ('daily', 'weekly', 'monthly', 'yearly')),\n  end_date TEXT,\n  asset_id TEXT NOT NULL REFERENCES assets(id),\n  status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'paused', 'deleted')),\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(from_account_id, book_id) REFERENCES accounts(id, book_id),\n  FOREIGN KEY(to_account_id, book_id) REFERENCES accounts(id, book_id)\n);\nCREATE TABLE IF NOT EXISTS period_closes (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL REFERENCES books(id),\n  name TEXT NOT NULL,\n  as_of TEXT NOT NULL,\n  description TEXT,\n  created_at TEXT NOT NULL,\n  reopened_at TEXT,\n  UNIQUE(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_period_closes_book_as_of ON period_closes(book_id, as_of);\nCREATE TABLE IF NOT EXISTS lots (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  account_id TEXT NOT NULL,\n  asset_id TEXT NOT NULL REFERENCES assets(id),\n  quantity INTEGER NOT NULL CHECK(quantity > 0),\n  cost_asset_id TEXT NOT NULL REFERENCES assets(id),\n  cost_quantity INTEGER NOT NULL CHECK(cost_quantity > 0),\n  opened_journal_id TEXT NOT NULL,\n  closed_journal_id TEXT,\n  opened_at TEXT NOT NULL,\n  closed_at TEXT,\n  status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'closed')),\n  metadata_json TEXT NOT NULL DEFAULT '{}',\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id),\n  FOREIGN KEY(opened_journal_id, book_id) REFERENCES journals(id, book_id),\n  FOREIGN KEY(closed_journal_id, book_id) REFERENCES journals(id, book_id)\n);\nCREATE TABLE IF NOT EXISTS statement_plans (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  account_id TEXT NOT NULL,\n  asset_id TEXT NOT NULL REFERENCES assets(id),\n  source_id TEXT,\n  status TEXT NOT NULL DEFAULT 'planned' CHECK(status IN ('planned', 'applied', 'discarded')),\n  statement_kind TEXT NOT NULL DEFAULT '',\n  file_name TEXT NOT NULL DEFAULT '',\n  file_sha256 TEXT NOT NULL DEFAULT '',\n  expected_balance INTEGER,\n  planned_balance INTEGER NOT NULL,\n  applied_balance INTEGER,\n  created_at TEXT NOT NULL,\n  applied_at TEXT,\n  discarded_at TEXT,\n  metadata_json TEXT NOT NULL DEFAULT '{}',\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(account_id, book_id) REFERENCES accounts(id, book_id),\n  FOREIGN KEY(source_id, book_id) REFERENCES sources(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_statement_plans_account_status ON statement_plans(book_id, account_id, status, created_at);\nCREATE TABLE IF NOT EXISTS statement_plan_rows (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  plan_id TEXT NOT NULL,\n  row_index INTEGER NOT NULL,\n  date TEXT NOT NULL,\n  quantity INTEGER NOT NULL,\n  description TEXT NOT NULL DEFAULT '',\n  external_id TEXT,\n  row_hash TEXT NOT NULL,\n  action TEXT NOT NULL CHECK(action IN ('matched', 'pending_to_commit', 'new_posted', 'new_pending', 'stale_pending_to_void', 'ambiguous', 'ignored')),\n  matched_journal_id TEXT,\n  created_journal_id TEXT,\n  counterpart_account_id TEXT,\n  reason TEXT NOT NULL DEFAULT '',\n  metadata_json TEXT NOT NULL DEFAULT '{}',\n  UNIQUE(plan_id, row_index),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(plan_id, book_id) REFERENCES statement_plans(id, book_id) ON DELETE CASCADE,\n  FOREIGN KEY(matched_journal_id, book_id) REFERENCES journals(id, book_id),\n  FOREIGN KEY(created_journal_id, book_id) REFERENCES journals(id, book_id),\n  FOREIGN KEY(counterpart_account_id, book_id) REFERENCES accounts(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_statement_plan_rows_plan_action ON statement_plan_rows(plan_id, action, row_index);\nCREATE INDEX IF NOT EXISTS idx_statement_plan_rows_hash ON statement_plan_rows(book_id, row_hash);\nCREATE TABLE IF NOT EXISTS ledger_operations (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  tool_name TEXT NOT NULL,\n  operation_type TEXT NOT NULL,\n  status TEXT NOT NULL DEFAULT 'applied' CHECK(status IN ('applied', 'reversed')),\n  created_at TEXT NOT NULL,\n  reversed_at TEXT,\n  reversed_by_operation_id TEXT,\n  reverses_operation_id TEXT,\n  input_json TEXT NOT NULL DEFAULT '{}',\n  preview_json TEXT NOT NULL DEFAULT '{}',\n  result_json TEXT NOT NULL DEFAULT '{}',\n  metadata_json TEXT NOT NULL DEFAULT '{}',\n  UNIQUE(id, book_id),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(reversed_by_operation_id, book_id) REFERENCES ledger_operations(id, book_id),\n  FOREIGN KEY(reverses_operation_id, book_id) REFERENCES ledger_operations(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_ledger_operations_type_status ON ledger_operations(book_id, operation_type, status, created_at);\nCREATE TABLE IF NOT EXISTS ledger_operation_rows (\n  id TEXT PRIMARY KEY,\n  book_id TEXT NOT NULL,\n  operation_id TEXT NOT NULL,\n  row_index INTEGER NOT NULL,\n  entity_type TEXT NOT NULL,\n  entity_id TEXT NOT NULL,\n  action TEXT NOT NULL CHECK(action IN ('insert', 'update', 'delete', 'correction', 'reverse')),\n  before_hash TEXT,\n  after_hash TEXT,\n  before_json TEXT,\n  after_json TEXT,\n  correction_journal_id TEXT,\n  reverse_journal_id TEXT,\n  metadata_json TEXT NOT NULL DEFAULT '{}',\n  UNIQUE(operation_id, row_index),\n  FOREIGN KEY(book_id) REFERENCES books(id),\n  FOREIGN KEY(operation_id, book_id) REFERENCES ledger_operations(id, book_id) ON DELETE CASCADE,\n  FOREIGN KEY(correction_journal_id, book_id) REFERENCES journals(id, book_id),\n  FOREIGN KEY(reverse_journal_id, book_id) REFERENCES journals(id, book_id)\n);\nCREATE INDEX IF NOT EXISTS idx_ledger_operation_rows_operation ON ledger_operation_rows(operation_id, row_index);\nCREATE TRIGGER IF NOT EXISTS trg_statement_plans_no_identity_update\nBEFORE UPDATE OF book_id, account_id, asset_id, statement_kind, file_name, file_sha256, expected_balance, planned_balance, metadata_json, created_at ON statement_plans\nBEGIN\n  SELECT RAISE(ABORT, 'statement plan identity is immutable');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_statement_plans_status_transition\nBEFORE UPDATE OF status ON statement_plans\nWHEN OLD.status != NEW.status\nBEGIN\n  SELECT CASE\n    WHEN OLD.status != 'planned'\n    THEN RAISE(ABORT, 'statement plan status is final')\n  END;\n  SELECT CASE\n    WHEN NEW.status NOT IN ('applied', 'discarded')\n    THEN RAISE(ABORT, 'invalid statement plan status transition')\n  END;\n  SELECT CASE\n    WHEN NEW.status = 'applied' AND NEW.applied_at IS NULL\n    THEN RAISE(ABORT, 'applied statement plan requires applied_at')\n  END;\n  SELECT CASE\n    WHEN NEW.status = 'discarded' AND NEW.discarded_at IS NULL\n    THEN RAISE(ABORT, 'discarded statement plan requires discarded_at')\n  END;\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_statement_plans_no_delete\nBEFORE DELETE ON statement_plans\nBEGIN\n  SELECT RAISE(ABORT, 'statement plans are audit records');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_statement_plan_rows_no_semantic_update\nBEFORE UPDATE OF book_id, plan_id, row_index, date, quantity, description, external_id, row_hash, action, matched_journal_id, counterpart_account_id, reason, metadata_json ON statement_plan_rows\nBEGIN\n  SELECT RAISE(ABORT, 'statement plan rows are immutable');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_statement_plan_rows_created_once\nBEFORE UPDATE OF created_journal_id ON statement_plan_rows\nWHEN OLD.created_journal_id IS NOT NULL OR NEW.created_journal_id IS NULL\nBEGIN\n  SELECT RAISE(ABORT, 'created_journal_id can only be set once');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_statement_plan_rows_no_delete\nBEFORE DELETE ON statement_plan_rows\nBEGIN\n  SELECT RAISE(ABORT, 'statement plan rows are audit records');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_ledger_operations_no_identity_update\nBEFORE UPDATE OF book_id, tool_name, operation_type, created_at, reverses_operation_id, input_json, preview_json, result_json, metadata_json ON ledger_operations\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operations are audit records');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_ledger_operations_status_transition\nBEFORE UPDATE OF status ON ledger_operations\nWHEN OLD.status != NEW.status\nBEGIN\n  SELECT CASE\n    WHEN OLD.status != 'applied' OR NEW.status != 'reversed'\n    THEN RAISE(ABORT, 'invalid ledger operation status transition')\n  END;\n  SELECT CASE\n    WHEN NEW.reversed_at IS NULL OR NEW.reversed_by_operation_id IS NULL\n    THEN RAISE(ABORT, 'reversed ledger operation requires reversal metadata')\n  END;\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_ledger_operations_no_delete\nBEFORE DELETE ON ledger_operations\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operations are audit records');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_ledger_operation_rows_no_update\nBEFORE UPDATE ON ledger_operation_rows\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operation rows are immutable');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_ledger_operation_rows_no_delete\nBEFORE DELETE ON ledger_operation_rows\nBEGIN\n  SELECT RAISE(ABORT, 'ledger operation rows are audit records');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_journals_no_finalized_insert\nBEFORE INSERT ON journals\nWHEN NEW.finalized_at IS NOT NULL\nBEGIN\n  SELECT RAISE(ABORT, 'insert journal as draft, then finalize');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_journals_finalize_requires_lines\nBEFORE UPDATE OF finalized_at ON journals\nWHEN OLD.finalized_at IS NULL AND NEW.finalized_at IS NOT NULL\nBEGIN\n  SELECT CASE\n    WHEN NOT EXISTS (\n      SELECT 1 FROM journal_lines\n      WHERE book_id = NEW.book_id AND journal_id = NEW.id\n    )\n    THEN RAISE(ABORT, 'finalized journal must have lines')\n  END;\n  SELECT CASE\n    WHEN EXISTS (\n      SELECT 1\n      FROM (\n        SELECT asset_id, sum(quantity) AS total\n        FROM journal_lines\n        WHERE book_id = NEW.book_id AND journal_id = NEW.id\n        GROUP BY asset_id\n        HAVING total != 0\n      )\n    )\n    THEN RAISE(ABORT, 'finalized journal must balance per asset')\n  END;\n  SELECT CASE\n    WHEN EXISTS (\n      SELECT 1 FROM period_closes\n      WHERE book_id = NEW.book_id\n        AND reopened_at IS NULL\n        AND as_of >= NEW.date\n      LIMIT 1\n    )\n    THEN RAISE(ABORT, 'journal date is in a closed period')\n  END;\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_journals_reopen_guard\nBEFORE UPDATE OF finalized_at ON journals\nWHEN OLD.finalized_at IS NOT NULL AND NEW.finalized_at IS NULL\nBEGIN\n  SELECT CASE\n    WHEN EXISTS (\n      SELECT 1 FROM period_closes\n      WHERE book_id = OLD.book_id\n        AND reopened_at IS NULL\n        AND as_of >= OLD.date\n      LIMIT 1\n    )\n    THEN RAISE(ABORT, 'cannot reopen journal in a closed period')\n  END;\n  SELECT CASE\n    WHEN EXISTS (\n      SELECT 1 FROM lots\n      WHERE book_id = OLD.book_id\n        AND (opened_journal_id = OLD.id OR closed_journal_id = OLD.id)\n      LIMIT 1\n    )\n    THEN RAISE(ABORT, 'cannot reopen journal linked to investment lots')\n  END;\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_lines_no_insert_finalized\nBEFORE INSERT ON journal_lines\nWHEN EXISTS (\n  SELECT 1 FROM journals\n  WHERE book_id = NEW.book_id\n    AND id = NEW.journal_id\n    AND finalized_at IS NOT NULL\n)\nBEGIN\n  SELECT RAISE(ABORT, 'cannot insert lines on finalized journal');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_lines_no_update_finalized\nBEFORE UPDATE ON journal_lines\nWHEN EXISTS (\n  SELECT 1 FROM journals\n  WHERE book_id = OLD.book_id\n    AND id = OLD.journal_id\n    AND finalized_at IS NOT NULL\n)\nBEGIN\n  SELECT RAISE(ABORT, 'cannot update lines on finalized journal');\nEND;\nCREATE TRIGGER IF NOT EXISTS trg_lines_no_delete_finalized\nBEFORE DELETE ON journal_lines\nWHEN EXISTS (\n  SELECT 1 FROM journals\n  WHERE book_id = OLD.book_id\n    AND id = OLD.journal_id\n    AND finalized_at IS NOT NULL\n)\nBEGIN\n  SELECT RAISE(ABORT, 'cannot delete lines on finalized journal');\nEND;\n"
}
