zettelkasten.tables_persist¶
zettelkasten.tables_persist ¶
Table persistence + overlay/attribution helpers for the matrix engine.
Mechanically split out of tables.py: the _reviews/<name>.tables.json
read/write substrate, org mirror upsert, table signature, overlay normalization
and corrections attribution.
set_column_synthesis ¶
set_column_synthesis(name: str, table_id: str, column_key: str, ai: str, *, graphs_dir: 'Path | None' = None) -> bool
Durably fold a column's on-demand AI narrative into synthesis[key]["ai"].
The on-demand synthesis endpoint (Synthesize / Synthesize all) generates
a one-line narrative per column; without this it lived only in transient client
state and vanished on reload. This persists it onto the stored grid the same way
the streaming build does, so it survives a reload and a cross-session peek.
A narrow load→mutate→save under the review write lock (never a whole-grid
clobber from a possibly-stale read) so a parallel Synthesize all — which
fires one request per column concurrently — can't drop a sibling column's
freshly-written narrative. Returns False when the table or column is absent.
Source code in zettelkasten/tables_persist.py
delete_table ¶
Remove a table from the review's tables artifact. Returns True if removed.
Source code in zettelkasten/tables_persist.py
rename_table ¶
Patch a persisted table's display title. Returns True if the table existed.
Only the title (a display label) changes — table_id is the immutable
artifact key that cell-note anchors and the grid signature depend on, so a
rename never re-keys the table. Cheap: no re-gather, no LLM.
Source code in zettelkasten/tables_persist.py
list_tables ¶
All persisted tables for a review (full grids), newest-first by id.
Source code in zettelkasten/tables_persist.py
list_table_meta ¶
Metadata-only listing of a review's tables — NO rows/cells.
Same order as :func:list_tables but each entry is a shallow copy with the
heavy per-cell rows payload dropped and lightweight rows_count /
columns_count added. Every other field (title, columns, row axis,
synthesis, signature, timestamps, lifecycle) is preserved so the dropdown,
merge, and axis-typed subdivision keep working without loading a single
full grid. The full grids stay available via :func:list_tables (used for
the on-demand Excel export).
Source code in zettelkasten/tables_persist.py
apply_overlay ¶
apply_overlay(table: dict[str, Any], overlay: Any, *, valid_note_ids: 'set[str] | None' = None) -> dict[str, Any]
Merge a corrections overlay onto a freshly built table — the durability merge.
Returns a DERIVED, READ-ONLY view (a deep copy): corrections whose referenced
row/col/note ids still exist are applied; the rest drop into
removed_upstream rather than crashing. NEVER persist this view — only the
stored overlay is durable. member_removes win over member_adds for the
same (row, col, note) so a resync can't resurrect an ejected note.
valid_note_ids (when supplied — :func:reconcile passes the rebuilt
corpus's note ids) gates member_add against vanished notes: a pin whose
note_id no longer exists in the corpus is dropped into removed_upstream
rather than fabricating a phantom member. When None (a bare merge over a
pre-built grid) the existence check is skipped. A member_remove whose
note_id no longer routes into its cell is ALSO surfaced in
removed_upstream (rather than silently no-op'd) so a correction that no
longer bites — e.g. against a lens column with no materialized edges — stays
visible and reconcilable instead of vanishing without trace.
Source code in zettelkasten/tables_persist.py
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 | |
corpus_note_ids ¶
corpus_note_ids(get_graph: GetGraph, *, project: str = '', graph: str = '', graphs_dir: 'Path | None' = None, localize: 'Callable[[str], str] | None' = None) -> set[str]
Every note id in the build's scope — the existence oracle for the overlay.
:func:apply_overlay consults this to tell a still-valid pin from one that
references a vanished note. Spans every scope graph (incl. _cross) and is
type-agnostic so a legitimately-existing note of any kind is never mistaken
for vanished. Best-effort: a load failure yields an empty set (no spurious
drops — the existence check simply degrades to skipped per-id).
Source code in zettelkasten/tables_persist.py
load_overlay ¶
load_overlay(name: str, table_id: str, *, project: str = '', graph: str = '', graphs_dir: 'Path | None' = None) -> 'dict[str, Any] | None'
The corrections overlay stored on the table's mirror org, or None.
Resolves the owner-scoped org for (review, table_id) the same way the
persist mirror does (:func:_mirror_owner) and returns its overlay block.
Returns None when no org exists yet (a brand-new / unmirrored table) so a
caller can fall back to the bare projection and behave exactly as before the
overlay existed. Best-effort: any registry hiccup also yields None.
Source code in zettelkasten/tables_persist.py
record_attribution ¶
record_attribution(name: str, table_id: str, *, member_uid: str, dim_id: str, decision: str, note_id: str = '', source_graph: str = '', title: str = '', project: str = '', graph: str = '', graphs_dir: 'Path | None' = None) -> bool
Durably record a blank-edge collision attribution on the table's overlay.
decision='own' CLAIMS the ambiguous member as THIS spine's — on the next
build it is no longer excluded and rejoins the grid as a normal row.
decision='foreign' marks it not-mine — it stays excluded but leaves the
needs-attribution bucket. The entry is keyed by (member_uid, dim_id) (a
re-record on the same key REPLACES the prior decision) so it survives rebuilds
and re-partitions. Returns False when no backing org exists yet (build the
table first so its definition is mirrored). Raises ValueError on a bad
decision.
Source code in zettelkasten/tables_persist.py
count_unrouted ¶
count_unrouted(get_graph: GetGraph, table: dict[str, Any], *, project: str = '', graph: str = '', graphs_dir: 'Path | None' = None, localize: 'Callable[[str], str] | None' = None) -> dict[str, Any]
A cheap drift signal: in-scope notes attached to NO cell/dimension.
Compares the corpus's current in-scope value notes against the payload's
stamped attached_note_ids. Surfaces {"unrouted", "unrouted_ids",
"attached", "stale"} — the per-view "N new notes unrouted · Resync" cue. The
in-scope universe spans every scope graph (incl. _cross); stale flags
a built signature that no longer matches the corpus.