@ai-matrx/records 0.11.0 → 0.17.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,159 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.17.0
4
+
5
+ **A list is a door, never a grant on the thing behind it.**
6
+
7
+ `docTemplates`, `docRenders` and `docSignatures` read `custom.doc_template` (a
8
+ view), `custom.doc_render` and `custom.doc_signature` (tables) with a direct
9
+ PostgREST `.from()`. **None of those three holds a client SELECT**, and none ever
10
+ should — so every plural act of document generation returned 403, and the
11
+ try-everything page photographed *"permission denied for view doc_template"* on
12
+ 2026-09-20. A person could save a template they could never list and render a
13
+ document they could never find again.
14
+
15
+ All three now call doors that narrow inside the definer, after the ladder has
16
+ spoken: `custom.doc_templates` and `custom.doc_renders` / `custom.doc_signatures`
17
+ (viewer on the Table, viewer on the record). Two acts that had no door at all
18
+ arrive with them:
19
+
20
+ - **`docTemplateDelete({ template_id })`** — retire one template. Editor on the
21
+ Table it renders, because a template is that Table's wording. Soft only:
22
+ documents already rendered keep their frozen bytes and their seals, so
23
+ retiring a template can never invalidate a signature (VAL-10).
24
+ - **`subscriptionCadences()`** — the cadences a subscription may be written
25
+ with, through the door a PERSON may call. `aggSubscriptionCadences` is the
26
+ notifier's own and holds no client grant, so every screen that asked for it
27
+ was refused and fell back to a word typed into the screen; this one delegates
28
+ to the same array, so a picker can never offer a cadence the digest runner
29
+ does not honour.
30
+
31
+ `DocTemplateRow` drops `organization_id`, which the door does not answer and no
32
+ caller read — the client already holds the organization. The door names its key
33
+ `template_id` / `render_id` / `signature_id`; the package keeps `id`, mapped once
34
+ here rather than renamed in four screens.
35
+
36
+ `store.generated.ts` regenerated from the live store: **387 → 401 doors**.
37
+
38
+ **Consumer action:** none for `docTemplates` / `docRenders` / `docSignatures` —
39
+ same names, same row shapes, they simply stop being refused. The five doors they
40
+ need are live on the main database (`doorstwo_the_document_and_cadence_doors.sql`
41
+ + its grant file, applied 2026-09-20 13:43Z), so this version requires no
42
+ migration of its own.
43
+
44
+ ## 0.16.0
45
+
46
+ **A column is declared, and a client type can finally say so.**
47
+
48
+ `NewFieldDeclaration` now names what `custom.field_declare` has actually taken
49
+ since 2026-09-20: `type` (the one word every agent, script and import reaches
50
+ for — the store resolves the thirteen parity types, the five behaviours and the
51
+ plain synonyms, and refuses a word that means nothing by name), `config` (a long
52
+ text's `multiline`, a list's existing options table — the door always read it and
53
+ no client could send it), `relation_target` and `on_target_delete` (a column that
54
+ points at another of this organization's own tables, which no client type could
55
+ express at all), and `format` (the one shape `custom.doc_sign` accepts).
56
+
57
+ `FieldPatch` gains the door's BEHAVIOUR arm — `parity_type`, `plain`, `type`,
58
+ `multi` — plus `relation_target`, `on_target_delete`, `promoted`, `unique` and
59
+ `depends_on`. It named only the settings, so the one caller that needed to move a
60
+ column back onto the behaviour it is written against had no door and reached for
61
+ `custom.record_update` instead, writing the document *under* the guards rather
62
+ than through them. **`multi` is read only inside that arm: send the behaviour
63
+ word with it or nothing happens.**
64
+
65
+ **The columns come back in the order they were declared.** `custom.applicable_fields`
66
+ answers a SET — its order is the plan's and it moves between calls — so every
67
+ consumer of `client.fields()` was drawing columns in an arbitrary order. A table
68
+ declared Title, Day rate, Due came back Day rate, Title, Due. The Field's own
69
+ `sort` is applied once, here, so every screen, export, form and agent inherits it.
70
+
71
+ **22007 is mapped.** The live-door census caught `custom.dashboard_window_sql`
72
+ raising it; it reads as `invalid_argument`, like 42703 before it.
73
+
74
+ **The live-door suite declared its own fixture the way the store no longer
75
+ allows** — `custom.record_write` into the field kernel — so eighteen of its
76
+ thirty-three clauses were red on `main` against the live store. It uses
77
+ `custom.field_declare`. 33/33.
78
+ ## 0.15.0
79
+
80
+ **A client portal has an owner's side, and this package can reach it.**
81
+ `custom.portals`, `custom.portal_card`, `custom.portal_declare`,
82
+ `custom.portal_invite`, `custom.portal_revoke` and `custom.portal_preview` have
83
+ been live on the database and no client could call them, so the six doors that
84
+ decide who signs in from outside an organization — and what they see — existed
85
+ with nothing able to ask them. `store.generated.ts` was regenerated from the
86
+ live store (372 → 387 doors) and the six are now on `client`:
87
+
88
+ - `client.portals()` — every portal over this organization, with its client
89
+ Table, how many Tables it exposes, how many people are invited and how many
90
+ have actually signed in. The store filters the list on whether you may open
91
+ the client Table, so it never reveals a Table.
92
+ - `client.portalCard({ portal_id })` — one portal in full: each exposed Table
93
+ with the relation Field that says whose a record is and the fields a client
94
+ sees and may change, every person with `signed_in` and `is_active` kept
95
+ APART, and `external_lane_open`.
96
+ - `client.portalDeclare(...)` — create or RE-STATE one. Re-stating replaces
97
+ what the portal exposes and leaves the people alone.
98
+ - `client.portalInvite({ portal_id, client_record_id, email })` — and it takes
99
+ no `user_id` on purpose. From a screen that value is always null: an
100
+ invitation confers NOTHING until the person follows their own sign-in link,
101
+ which is what the door's own `say` sentence states. A client that passed an
102
+ id would be granting access on somebody else's behalf.
103
+ - `client.portalRevoke({ portal_id, principal_id })` — the answer's `say` names
104
+ the consequence in the past tense.
105
+ - `client.portalPreview({ portal_id, principal_id, table_id })` — "view as this
106
+ client". The door asks `custom.visible_set` for HER user id, the same call
107
+ `custom.read_records` makes when she opens the page, so it cannot disagree
108
+ with what she sees. It REFUSES (`02000`, mapped to `not_found`) when she has
109
+ not signed in, rather than answering an empty list.
110
+
111
+ New module `portal`, exported from the root: `PortalSummary`,
112
+ `PortalTableExposure`, `PortalPrincipal`, `PortalCard`, `PortalInvitation`,
113
+ `PortalRevocation`, `PortalPreviewRow`, and `portalPath(slug)` — the one place
114
+ `/portal/c/<slug>` is written down.
115
+
116
+ **Consumer action:** none required; every addition is new surface. A host that
117
+ already builds a portal address by hand should use `portalPath` so there is one
118
+ spelling of it.
119
+
120
+ ## 0.13.0
121
+
122
+ **A choice value is the choice's own word, and this package says what that means.**
123
+ A list Field's cell used to hold the option RECORD'S ID, so every read, filter,
124
+ group-by, export and agent got back a uuid — the seventh independent pass failed
125
+ acceptance test T8 on exactly that. The store now holds the option's own stable
126
+ key, the read doors hand back the LABEL and name the key beside it in
127
+ `_choices`, and a caller may write the label, the key or (so nothing that worked
128
+ yesterday breaks) the option's id.
129
+
130
+ New module `choice`, exported from the root: `optionKey`, `optionLabel`,
131
+ `isTheChosen`, `choiceValuesOf`, `choicesOf`, `holdsARetiredChoice`,
132
+ `choiceSlug`, `looksLikeAnId`, and the `ChoiceOption` type. `isTheChosen` is the
133
+ one a control wants: it recognises a choice by its key, its label or its option
134
+ id, which is what lets one picker read a document from any of the three eras.
135
+
136
+ **Consumer action:** a control that compared a cell to `option.id` must use
137
+ `isTheChosen` and write `optionKey(option)` — comparing against the id alone now
138
+ shows nothing selected, because the read door hands back the label.
139
+
140
+ ## 0.12.0
141
+
142
+ **A person can see what they are being told about, and switch one off.**
143
+ `custom.subscriptions` and `custom.subscription_mute` have been live on the
144
+ database since 20 September and no client could call them, so a form's "tell me
145
+ when somebody answers" could be turned ON by an agent and never turned off by
146
+ the person it was telling. `client.subscriptions({ table_id })` answers mine
147
+ plus anyone's over a Table I hold admin on — narrowed to Tables I can already
148
+ open, so the list never reveals one — and `client.subscriptionMute({ rule_id,
149
+ muted })` is the switch. `MySubscription` carries the two things a screen needs
150
+ and the notifier does not: `muted`, and `i_may_mute`.
151
+
152
+ `aggSubscriptions` is unchanged and is still the NOTIFIER's reader, which no
153
+ client can call. Muting is honoured inside that one reader, so a screen can
154
+ never say a subscription is off while it keeps firing.
155
+
156
+
3
157
  ## 0.11.0
4
158
 
5
159
  **The publish gate stops red-lighting on other lanes' migrations.** `the