Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions changes/1c5s-server-derived-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: added
facade: changed
---
[en]
A hosted board now ranks `next` by derived ranking keys (`related`, `count`, `sum`) exactly as a local replica does. The store-free `next` (`read::next_active`, `next_active_value`, `next_active_filtered`) ranks a plugin with such keys from a new `ActiveBoard::ranking` graph: the edges around the active tickets and the live neighbours they reach, closed and archived ones included, deleted ones not. A server fills it with `nxs_fold_ddb::records::with_ranking` for what `read::next_ranking_reads(cfg)` names; `read::with_ranking` reads the same graph from a store. Without the graph, `next_active` still refuses such a plugin. Everything is additive: `ActiveBoard` gains a field (it is `#[non_exhaustive]`), and `RankingReads`, `RankingGraph`, `Neighbour`, `next_ranking_reads` and `with_ranking` are new. For a server's operators: the fold's index revision goes from 2 to 3, so every hosted stream is not current and is folded again on its next run. Its records reads (`active_board`, `with_ranking`, `list_board`) answer `NotCurrent` from the moment the run clears it and `Refolding` until the refold has reached the relay position the clear named — never a half-folded board. `Folder::clear` now takes that position (`clear(table, until)`) and leaves a sentinel watermark instead of deleting it. Roll the new build out to every fold run at once: a build with older rules refolds a newer stream back. The `.adj#` adjacency entries gain attributes; the one table-contract change of this release, the `typed` index, is described with the hosted `list`.
[de]
Ein gehostetes Brett rankt `next` jetzt nach abgeleiteten Ranking-Schlüsseln (`related`, `count`, `sum`) genau so wie ein lokales Replikat. Das speicherfreie `next` (`read::next_active`, `next_active_value`, `next_active_filtered`) rankt ein Plugin mit solchen Schlüsseln aus einem neuen Graphen `ActiveBoard::ranking`: den Kanten um die aktiven Tickets und den lebenden Nachbarn, die sie erreichen, geschlossene und archivierte eingeschlossen, gelöschte nicht. Ein Server füllt ihn mit `nxs_fold_ddb::records::with_ranking` für das, was `read::next_ranking_reads(cfg)` nennt; `read::with_ranking` liest denselben Graphen aus einem Speicher. Ohne den Graphen lehnt `next_active` ein solches Plugin weiterhin ab. Alles ist additiv: `ActiveBoard` bekommt ein Feld (es ist `#[non_exhaustive]`), und `RankingReads`, `RankingGraph`, `Neighbour`, `next_ranking_reads` und `with_ranking` sind neu. Für den Betrieb eines Servers: Die Index-Revision des Folds steigt von 2 auf 3, daher ist jeder gehostete Stream nicht aktuell und wird bei seinem nächsten Lauf neu gefaltet. Seine Records-Lesezugriffe (`active_board`, `with_ranking`, `list_board`) antworten ab dem Moment, in dem der Lauf ihn leert, mit `NotCurrent` und mit `Refolding`, bis das Neufalten die Relay-Position erreicht hat, die das Leeren genannt hat — nie mit einem halb gefalteten Brett. `Folder::clear` nimmt diese Position jetzt entgegen (`clear(table, until)`) und hinterlässt ein Sentinel-Watermark, statt es zu löschen. Den neuen Build in alle Fold-Läufe zugleich ausrollen: Ein Build mit älteren Regeln faltet einen neueren Stream zurück. Die `.adj#`-Adjazenzeinträge bekommen Attribute; die eine Änderung des Tabellenvertrags in diesem Release, der Index `typed`, ist bei der gehosteten `list` beschrieben.
8 changes: 8 additions & 0 deletions changes/xkr7-typed-index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: added
facade: changed
---
[en]
A hosted board now answers `list` ranked by the plugin's `[ranking.list]`, derived ranking keys included, exactly as a local replica does. The facade names what to read for a query (`read::list_reads`, a new `ListReads`), a server reads it into a new `ListBoard` with `nxs_fold_ddb::records::list_board`, and `read::list_board_page` / `read::list_board_value` filter (type, container, label, status), rank and project it with the code `list` runs locally; `read::list_board` reads the same board from a store. The hosted list is not paged: `limit` cuts it with the total disclosed, and `paginate` or a token is refused, as for the hosted `next`. A list that is not ranked costs its page, not the stream: `ListReads::select` (a new `ListSelect`) lets the server keep and cut on the item rows before it reads any join (`read::list_select`, and `ListBoard::cut` with a new `ListCut` carries the total counted before the cut), and the server reads its point reads by `BatchGetItem`; a ranked list reads its types' whole `list` scope, which its rank is computed over. A hosted `next` whose keys read the rank `[ranking.list]` gives a type now ranks too instead of being refused: `RankingReads` gains `list_types` and `RankingGraph` gains `listed` (both `#[non_exhaustive]`, so additive). For a server's operators, the table contract changes: a third sparse index, GSI `typed` — partition key `nxf_typed` (string, `<stream>#<type>`), sort key `sk` (string), projection ALL — set by the fold on every live item row (closed and archived included, removed on delete). Add the index to the stack first, then deploy the read; a fold against a table without the index writes the attribute harmlessly. It ships in fold index revision 3 together with the self-describing adjacency entries, so one refold covers both. For an embedder of `nxs-fold-ddb`: the `Table` trait gains `get_many` (one `BatchGetItem` per 100 keys, unprocessed keys sent again), `RecordsError` gains `Refolding`, `Watermark` gains `refold_until`, `board::reindex` returns the items whose index attributes did not fit, and an item op is refused (`Folded::Refused`) when its row would pass 400 KB together with its index attributes, at one `GetItem` more per op, instead of silently dropping off an index.
[de]
Ein gehostetes Brett beantwortet `list` jetzt nach dem `[ranking.list]` des Plugins gerankt, abgeleitete Ranking-Schlüssel eingeschlossen, genau so wie ein lokales Replikat. Die Fassade nennt, was für eine Anfrage zu lesen ist (`read::list_reads`, ein neues `ListReads`), ein Server liest es mit `nxs_fold_ddb::records::list_board` in ein neues `ListBoard`, und `read::list_board_page` / `read::list_board_value` filtern (Typ, Container, Label, Status), ranken und projizieren es mit dem Code, den `list` lokal ausführt; `read::list_board` liest dasselbe Brett aus einem Speicher. Die gehostete Liste wird nicht geblättert: `limit` schneidet sie mit offengelegter Gesamtzahl, `paginate` oder ein Token wird abgelehnt, wie beim gehosteten `next`. Eine nicht gerankte Liste kostet ihre Seite, nicht den Stream: `ListReads::select` (ein neues `ListSelect`) lässt den Server anhand der Item-Zeilen behalten und schneiden, bevor er einen Join liest (`read::list_select`; `ListBoard::cut` mit einem neuen `ListCut` trägt die vor dem Schnitt gezählte Gesamtzahl), und der Server liest seine Punktzugriffe per `BatchGetItem`; eine gerankte Liste liest den ganzen `list`-Bereich ihrer Typen, über den ihr Rang berechnet wird. Ein gehostetes `next`, dessen Schlüssel den Rang lesen, den `[ranking.list]` einem Typ gibt, rankt jetzt ebenfalls, statt abgelehnt zu werden: `RankingReads` bekommt `list_types` und `RankingGraph` bekommt `listed` (beide `#[non_exhaustive]`, also additiv). Für den Betrieb eines Servers ändert sich der Tabellenvertrag: ein dritter dünn besetzter Index, GSI `typed` — Partitionsschlüssel `nxf_typed` (String, `<stream>#<type>`), Sortierschlüssel `sk` (String), Projektion ALL —, den der Fold an jeder lebenden Item-Zeile setzt (geschlossene und archivierte eingeschlossen, beim Löschen entfernt). Zuerst den Index in den Stack aufnehmen, dann das Lesen ausrollen; ein Fold gegen eine Tabelle ohne den Index schreibt das Attribut folgenlos. Er kommt mit der Fold-Index-Revision 3 zusammen mit den selbstbeschreibenden Adjazenzeinträgen, ein Neufalten deckt also beides ab. Für Einbettende von `nxs-fold-ddb`: Das Trait `Table` bekommt `get_many` (ein `BatchGetItem` je 100 Schlüssel, unverarbeitete Schlüssel werden erneut gesendet), `RecordsError` bekommt `Refolding`, `Watermark` bekommt `refold_until`, `board::reindex` gibt die Items zurück, deren Index-Attribute nicht passten, und eine Item-Op wird abgelehnt (`Folded::Refused`), wenn ihre Zeile zusammen mit ihren Index-Attributen 400 KB überschritte — zum Preis eines `GetItem` mehr je Op —, statt still aus einem Index zu fallen.
3 changes: 2 additions & 1 deletion crates/core/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ pub struct Ticket {
}

/// A present edge, `from` → `to` (a `dep` points at its blocker, a `parent` edge at the parent).
#[derive(Debug, Clone, PartialEq, Eq)]
/// Ordered by `from`, `to`, `kind`, so a set of edges has one order whatever order they came in.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Edge {
pub from: String,
pub to: String,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Status {
/// the OR-set replacement for the former single-parent `belongs_to` LWW register. The substrate is
/// n:m-capable like every other edge; single-parent is enforced at the write seam during ②a and
/// relaxed in ②c. The single current parent is projected by the `present_parent` view.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum EdgeKind {
Dep,
ContributesTo,
Expand Down
Loading
Loading