@ai-matrx/records 0.24.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,102 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.27.0
4
+
5
+ **"Give me a board of deals by stage" — the rules that judge every move become
6
+ something a screen can ask about before the finger lets go.**
7
+
8
+ A pipeline is not a new kind of thing and this release adds none. It is a Choice
9
+ Field a Table names as its stage field, plus transition Rules of the ONE Rule
10
+ object — so a move is an ordinary record write the store judges, History records
11
+ because it IS a write, and every surface already knows how to read.
12
+
13
+ `src/pipeline.ts`: `Pipeline`, `PipelineStage`, `PipelineRule`, `PipelineColumn`,
14
+ `PipelineVerdict`, `PipelineMissingField`, `PipelineDeclared`, `PipelineSpec`.
15
+
16
+ Six client methods, each one door: `pipelineRead`, `pipelineBoard`,
17
+ `pipelineTransitionRefusal`, `pipelineMove`, `pipelineDeclare`, `tableStageField`.
18
+
19
+ THIS PACKAGE ENFORCES NOTHING, and that is the point. `pipelineTransitionRefusal`
20
+ asks the store the same question the write will ask — the same Rules, through the
21
+ same evaluator, on the document the move WOULD produce — and hands back the store's
22
+ own sentence plus, when a stage demands columns that are empty, exactly which ones.
23
+ A board that decided in the browser would be a second opinion with nothing keeping
24
+ it equal to the first, and it would be the comfortable one, because it is the one
25
+ nobody can be refused by.
26
+
27
+ `mayDrag(level)` is the one thing this package DOES decide, and it is about the
28
+ person, not the rules: a viewer's card carries no drag handle at all rather than
29
+ one that looks live and snaps back every time.
30
+
31
+ ## 0.26.0
32
+
33
+ **"Text me on a new lead; email me a Monday summary" — the second half of that
34
+ sentence becomes something the store can be told.**
35
+
36
+ A cadence was two words, `immediate` and `digest`, and `schedule` was free text
37
+ nothing read. `SubscriptionCadence` is now `instant | hourly | daily | weekly` —
38
+ the words `custom.agg_subscription_cadences()` answers and the digest runner acts
39
+ on. The two old words still work everywhere: the store normalises them, so a Rule
40
+ written last week and one written by the picker today mean the same thing.
41
+
42
+ `MySubscription` gained the three answers a notifications list actually needs and
43
+ could not ask for: `quiet_hours`, `last_sent_at` and `next_digest_at`. The last is
44
+ computed by the SAME store functions the runner uses, so "next Monday 08:00" on the
45
+ screen and the moment it fires cannot drift; it is null for a muted subscription,
46
+ because saying "next Monday" beside a switch that is off is a screen that lies.
47
+
48
+ Two doors that did not exist:
49
+
50
+ * `subscriptionDeclare({ table_id, spec })` — WRITE one. The store refuses a cadence
51
+ no runner acts on, a channel this platform does not send on and quiet hours nobody
52
+ can parse, each by name, then declares the Rule through `rule_declare` (REC-72: a
53
+ Rule written as a record is invisible to every reader that goes looking for one).
54
+ * `subscriptionPreview({ rule_id })` — the summary it would send RIGHT NOW, with what
55
+ entered, left and changed NAMED rather than counted, plus the in-view total and a
56
+ click-through to the filtered grid. It assembles only: nothing is delivered,
57
+ nothing is recorded, and the watermark does not move.
58
+
59
+ `QuietHours`, `DigestPreview` and `SubscriptionSpec` are new exported types.
60
+ `store.generated.ts` regenerated from live — 520 doors, `check:store-registry` OK.
61
+ ## 0.26.0 — 2026-09-20
62
+
63
+ **The queue was filing every refusal as a hiccup, and a real browser found it.**
64
+
65
+ `isDecided` listed SQLSTATEs. `RecordsClient` hands back its own WORD —
66
+ `invalid_argument`, `door`, `refused_by_rule` — with the sqlstate beside it, so
67
+ no refusal ever matched: a capture the store had refused BY NAME went back in the
68
+ queue and would have been sent again forever, which is the exact silent hole the
69
+ queue's fourth decision exists to prevent. The unit suite passed throughout
70
+ because it fed the check SQLSTATEs — it was testing the assumption, not the
71
+ client. Measured in a headless Chromium at 390 px against the main database.
72
+
73
+ It is now the other way round, and from the package's own list rather than a
74
+ guess: `stale_read`, `timed_out` and `store_limit` are worth repeating (which is
75
+ what `results.ts` says in as many words), a failure with NO code at all is the
76
+ network and waits, and everything the door named is a decision that stays.
77
+ `isDecided` takes the error, not a code, so either spelling works.
78
+
79
+
80
+ ## 0.25.0 — 2026-09-20
81
+
82
+ **A crew out in the field, and a phone with no bars (PRODUCTS row 15, P13).**
83
+
84
+ * `captureSheetDeclare`, `capturePublish`, `captureSheets`, `captureOpen` and
85
+ `captureSubmit` — the five SIGNED-IN doors of a crew capture sheet. A capture
86
+ sheet is not a second form store: it is the ONE Form object saying
87
+ `audience = 'crew'`, opened by `capture_opened_at` rather than `published_at`,
88
+ and the database carries a CHECK that refuses a row which is both. `captureOpen`
89
+ answers `null` for a sheet below `viewer` AND for a sheet that is not there —
90
+ the same answer the Table gives — and carries an honest `may_capture` with a
91
+ sentence, so a phone never discovers a refusal after the walk to the bin.
92
+ * `openCaptureQueue` — the offline queue itself, headless, in IndexedDB, with the
93
+ network calls injected. The capture is on disk before the network is asked; the
94
+ idempotency key is minted when the capture is TAKEN, not when it is sent; items
95
+ go up oldest first, one at a time; a file's bytes are uploaded once and the
96
+ reference written back, so a failed submit never re-sends the photograph; and a
97
+ REFUSAL is not a retry — it stays in the queue carrying the door's own sentence
98
+ until a person fixes it or throws it away. `sync()` answers with per-item
99
+ outcomes, never a boolean.
3
100
 
4
101
 
5
102
  ## 0.24.0 — 2026-09-20