wip: pause checkpoint — P6 feature complete, gate interrupted (not for merge)
This commit is contained in:
1 parent
cc4f104f06
commit
ce36640874
68 files changed
+4592
-374
No files matched your search
+33
-9
@@ -58,7 +58,8 @@ apply is safe — on startup the applier replays every journal line with
|
||||
- `ts`: UTC, RFC 3339. Recorded, never trusted for ordering (`seq` orders).
|
||||
- `type`: `account.added`, `entry.posted`, `entry.reversed`,
|
||||
`receipt.attached`, `receipt.linked`, `receipt.unlinked`,
|
||||
`document.filed`, `attachment.withdrawn` (plus the A/R, A/P, asset,
|
||||
`document.filed`, `attachment.withdrawn`, `attachment.text_extracted`,
|
||||
`attachment.annotated`, `entry.settles` (plus the A/R, A/P, asset,
|
||||
period, payroll, and class events listed on `JournalLine`).
|
||||
- `payload`: the full domain facts (account fields, entry with lines, blob
|
||||
sha, ...). Everything the applier needs; nothing it must look up elsewhere.
|
||||
@@ -90,9 +91,12 @@ a backup is then proven where it lands, by a drill (docs/mcp-interface.md,
|
||||
- **Immutable.** Blobs are never modified or deleted by the application. A
|
||||
retention/deletion policy is an explicit future human decision, not an
|
||||
accident of implementation.
|
||||
- **Deduplicated.** Attaching the same receipt twice stores one blob and two
|
||||
attachment rows. `attach_receipt` / `file_document` return
|
||||
`deduplicated: true` when the blob already existed.
|
||||
- **Deduplicated.** The same bytes are one blob, and since P6 one live
|
||||
attachment record: filing them again returns that record (with
|
||||
`deduplicated: true`) and links it to the new subject instead of adding
|
||||
a second row. Records an older version filed twice stay as written until
|
||||
`books.consolidate_duplicates` links the oldest to every subject the
|
||||
others had and withdraws the rest (`duplicate_of`).
|
||||
- **Limits.** 25 MiB per file, as an upload (`books.begin_upload`): the
|
||||
only way a file comes in (D-46: nothing is given inline). The name and MIME type are the client's, checked
|
||||
when the file is filed: a name of 1 to 255 bytes, no `/` or `\`, not
|
||||
@@ -111,17 +115,33 @@ a backup is then proven where it lands, by a drill (docs/mcp-interface.md,
|
||||
exists, then linked with `books.link_receipt`.
|
||||
- `category` mirrors today's Drive layout: `formation`, `legal`, `tax`,
|
||||
`accounting` (today's 01 Formation / 02 Legal / 03 Tax / 04 Accounting),
|
||||
plus `receipts`.
|
||||
plus `receipts`, and every other company record (D-20): `statement`,
|
||||
`contract`, `photo`, `warranty`, `import_source`, `notices`,
|
||||
`correspondence`, `plans`, `history`, `other`.
|
||||
- `duplicate_of` names the attachment a withdrawn duplicate stands for
|
||||
(`attachment.withdrawn`'s optional `duplicate_of`, schema v16).
|
||||
- Documents filed via `books.file_document` and receipts via
|
||||
`books.attach_receipt` share the blob store; they differ only in metadata.
|
||||
- **Receipts change by events, never by edits.** `receipt.unlinked`
|
||||
(`attachment_id`, `entry_id`, `reason`) detaches a receipt from its entry;
|
||||
`attachment.withdrawn` (`attachment_id`, `reason`) retires an unlinked
|
||||
receipt or a document (`withdrawn_at`, `withdrawn_reason` columns, schema
|
||||
v5). Linking requires an unlinked, non-withdrawn attachment, so evidence
|
||||
`attachment.withdrawn` (`attachment_id`, `reason`, `duplicate_of?`)
|
||||
retires an unlinked receipt or a document (`withdrawn_at`,
|
||||
`withdrawn_reason`, `duplicate_of` columns, schema v5 and v16). Linking requires an unlinked, non-withdrawn attachment, so evidence
|
||||
only leaves an entry through a recorded unlink. Blobs are untouched: a
|
||||
withdrawn file is still hash-verified and readable, and the journal shows
|
||||
when it was filed, linked, unlinked, and withdrawn, and why.
|
||||
- **Extraction is derived, and journaled** (P6, rules.md B-44):
|
||||
`attachment.text_extracted {attachment_id, text_sha256, extractor,
|
||||
version, pages, status}` records what reading a file found (`ok`,
|
||||
`none`, `failed`); the text itself is a content-addressed blob like any
|
||||
other, and `books.verify` checks its presence and hash, never re-derives
|
||||
it. One record per attachment, extractor and version: an extractor
|
||||
upgrade adds a record, never replaces one.
|
||||
- **Annotations and settlements are records, not edits** (P6):
|
||||
`attachment.annotated {attachment_id, fields, note?}` is what a human
|
||||
read off a file (the closed field list; a later annotation replaces the
|
||||
earlier record), and `entry.settles {entry_id, settles[]}` says which
|
||||
entries a payment pays back (the only source of "reimbursed").
|
||||
- The store is policy-free: it records attachments faithfully. Whether a
|
||||
posting *requires* a receipt is decided by the MCP layer's receipt policy
|
||||
(`docs/mcp-interface.md`), which reads the `attachments` table.
|
||||
@@ -144,7 +164,11 @@ Suspected corruption is fixed by rebuild, never by forensics.
|
||||
`integrity_check` on startup.
|
||||
- **Schema.** `meta(key, value)` — `schema_version`, `base_currency`,
|
||||
`last_applied_seq`; `accounts`, `entries`, `lines`, `blobs`, `attachments`
|
||||
as above; `events(seq, type, payload, created_at)` — the applier's output,
|
||||
as above; `extractions(attachment_id, extractor, version, text_sha256,
|
||||
pages, status, seq)` and `annotations(attachment_id, receipt_number,
|
||||
payee, amount, date, instrument, category, source, reimbursable_to,
|
||||
note, seq)` (P6; amounts TEXT, never REAL); `events(seq, type, payload,
|
||||
created_at)` — the applier's output,
|
||||
a queryable mirror of the journal stream and the feed the future GUI
|
||||
tails. (Poll `WHERE seq > ?`; later SSE over the HTTP transport. This is
|
||||
the correct answer to "watch its files to update live" — watch the event
|
||||
|
||||
Reference in new issue
Block a user