@ai-matrx/records 0.48.0 → 0.51.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,82 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.51.0
4
+
5
+ **The seal is asked through its own door.** (lane FIX-10B, from VERIFIER-10 finding F6.)
6
+
7
+ * This package published `docSignatureIntact` and did NOT publish `docSignatureRead`, so the
8
+ one screen that shows a signature asked the half that cannot answer. `custom.doc_signature_intact`
9
+ is the INTERNAL half of that pair: SECURITY INVOKER over a doors-only table, so from a browser
10
+ every seal check answered **`42501 permission denied for table doc_signature`** — measured live
11
+ from the admin seat on 2026-09-22, on a signature this lane had just written successfully.
12
+ * `custom.doc_signature_read` is the door built for a client: SECURITY DEFINER, asking
13
+ `assert_client_may_reach` and `assert_client_may_open` on the RECORD by name, and it returns the
14
+ seal AND the verdict in one answer — it calls `doc_signature_intact` itself and merges the
15
+ result, because a seal a person can read and a verdict they cannot is how a changed agreement
16
+ keeps wearing somebody's signature.
17
+ * **`docSignatureIntact` is gone and `docSignatureRead` replaces it.** No deprecated twin: a door
18
+ that cannot answer is not a fallback. One call per signature instead of two.
19
+ * **Consumer action:** replace `client.docSignatureIntact({ signature_id })` with
20
+ `client.docSignatureRead({ signature_id })`. The answer is a superset — the same
21
+ `intact` / `verdict` / `document_unchanged` / `value_unchanged` keys, plus the seal itself
22
+ (`signer_name`, `signed_at`, `document_hash`, `document_version`, `record_id`, `field_key`).
23
+
24
+ ## 0.50.0
25
+
26
+ **The store publishes every kind a column can be, and `signature` is one of them.** (lane FIX-10B-F6, from VERIFIER-10 finding F6.)
27
+
28
+ * Under a rendered document on a plumbing company's Crews table the screen said, in its own
29
+ words: *"To ask somebody to sign one of these, Crews needs a signature column — a text
30
+ column whose format is signature. Declare one in section 1 and the button appears here."*
31
+ There is no such control in section 1, and there never was: the only list of column kinds
32
+ any screen could read was `custom.parity_field_types()`, FLD-11's fourteen-row parity
33
+ FLOOR. Every kind that is not on that floor — plain text, long text, number, a
34
+ person-aimed relation and a signature — lived as an inline literal inside
35
+ `custom._field_document_for`. So `custom.doc_sign`, granted to `authenticated` and
36
+ accepting exactly one field shape, could not be reached from the product at all.
37
+ * **`DECLARABLE_FIELD_KINDS` and `DeclarableFieldKind` — the ONE registry**, generated from
38
+ the store's new `custom.field_kinds()` by `pnpm records:generate`. It SELECTS from the
39
+ parity floor rather than restating it, so a fifteenth parity type appears in both the day
40
+ it appears in one. `ParityFieldType` is unchanged and still means what it always meant:
41
+ which of the fourteen a field IS, which is a different question from what a person may
42
+ pick.
43
+ * **`FieldTypeWord` now spans that registry**, so `{ type: "signature" }` is a declaration a
44
+ typed caller can send. `custom._field_document_for` answers to the word (and to `sign`,
45
+ `e_signature`, `esignature`), and its "there is no kind of column called X" refusal now
46
+ names every kind instead of the parity floor alone.
47
+ * Consumer action: none required. `@ai-matrx/records-ui` 0.73.0 puts the kind on the
48
+ Add-field menu.
49
+
50
+ ## 0.49.0
51
+
52
+ **A number on a dashboard can be walked into.** (lane DRILL.)
53
+
54
+ * **`custom.read_records_matching` — the store's filtered read door**, and `client.list` now
55
+ asks it for EVERY page, filtered or not, with `{}` when nobody asked a question. The grid,
56
+ the board, the record list and a dashboard drill-through are therefore one door, decided by
57
+ one ladder: a narrowed screen can never be a differently-judged screen. `list({ filter })`
58
+ and `useRecords(tableId, { filter })` are how a caller asks.
59
+ * **`RecordFilter` — the ONE name for the store's filter shape.** It is not new and never was
60
+ the aggregate's private shape: `platform.saved_view` has carried exactly this object of
61
+ Field key → value as `definition -> 'filters'` since DOOR-18, `custom.view_declare` writes
62
+ it, `custom.agg_view_admits` reads it, and `custom.record_aggregate` takes it as `p_filter`.
63
+ A scalar is an equality, `null` is UNSET (the third state of a tick box), an object is a
64
+ half-open time window. `SavedViewRow.filters` and `SavedViewSpecIn.filters` were
65
+ `Record<string, unknown>` and are this now — an `unknown` there is a digest that quietly
66
+ notifies about the wrong records.
67
+ * **One evaluator in the database, not two.** `custom.agg_sql`'s filter loop moved verbatim
68
+ into `custom.record_filter_sql` and the aggregate CALLS it, so the number a chart draws and
69
+ the rows a drill lands on are produced by the same bytes. Measured on Rincon Plumbing Co's
70
+ 41 live jobs: the chart says Cancelled 4, the door hands back RPC-1036…1039 and nothing
71
+ else; the rendered choice label and the stored key both land on the same four; a window on
72
+ `scheduled_date` answers 5 through both doors.
73
+ * **Why it is a new name and not a sixth argument** — the REALTIME-2 ruling, again: a defaulted
74
+ `p_filter` beside the live five-argument `custom.read_records` makes every existing call
75
+ ambiguous, and the only way to add it in place is to drop a live door.
76
+ * **`stableFilterKey`** — key-sorted JSON of a `RecordFilter`, so a React parent that rebuilds
77
+ `{ status: "Cancelled" }` on every render re-reads the page when the QUESTION changes rather
78
+ than on every render forever.
79
+
3
80
  ## 0.48.0
4
81
 
5
82
  **A portal principal carries the link the office copies for them.** (lane TAILS-5.)
@@ -32,8 +32,13 @@ var core_exports = {};
32
32
  __export(core_exports, {
33
33
  ABSENCE_REASONS: () => ABSENCE_REASONS,
34
34
  ACTOR_VOCABULARY: () => ACTOR_VOCABULARY,
35
+ ARCHIVE_LANES: () => ARCHIVE_LANES,
36
+ ARCHIVE_LANE_LABEL: () => ARCHIVE_LANE_LABEL,
37
+ ARCHIVE_LANE_MEANS: () => ARCHIVE_LANE_MEANS,
35
38
  COMPUTE_ON: () => COMPUTE_ON,
36
39
  CONTEXT_POLICIES: () => CONTEXT_POLICIES,
40
+ DECLARABLE_FIELD_KINDS: () => DECLARABLE_FIELD_KINDS,
41
+ DEFAULT_ARCHIVE_LANE: () => DEFAULT_ARCHIVE_LANE,
37
42
  DEFAULT_DOCUMENT_MAX_BYTES: () => DEFAULT_DOCUMENT_MAX_BYTES,
38
43
  DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
39
44
  DEFAULT_VALUE_MAX_BYTES: () => DEFAULT_VALUE_MAX_BYTES,
@@ -73,6 +78,8 @@ __export(core_exports, {
73
78
  TABLE_TYPES: () => TABLE_TYPES,
74
79
  VALUE_ALTERNATE_KEYS: () => VALUE_ALTERNATE_KEYS,
75
80
  VALUE_ENVELOPE_KEYS: () => VALUE_ENVELOPE_KEYS,
81
+ archivedByLine: () => archivedByLine,
82
+ archivedWhen: () => archivedWhen,
76
83
  asPermissionLevel: () => asPermissionLevel,
77
84
  asWriteConflict: () => asWriteConflict,
78
85
  atLeast: () => atLeast,
@@ -88,6 +95,7 @@ __export(core_exports, {
88
95
  doorAbsent: () => doorAbsent,
89
96
  doorCensus: () => doorCensus,
90
97
  doorExists: () => doorExists,
98
+ emptyArchiveLine: () => emptyArchiveLine,
91
99
  err: () => err,
92
100
  exportCsv: () => exportCsv,
93
101
  exportXlsx: () => exportXlsx,
@@ -99,6 +107,7 @@ __export(core_exports, {
99
107
  importXlsx: () => importXlsx,
100
108
  indexedDbStore: () => indexedDbStore,
101
109
  intersectIds: () => intersectIds,
110
+ isArchiveLane: () => isArchiveLane,
102
111
  isDecided: () => isDecided,
103
112
  isOwnOp: () => isOwnOp,
104
113
  isRecordsErr: () => isRecordsErr,
@@ -123,7 +132,9 @@ __export(core_exports, {
123
132
  predictWriteRefusals: () => predictWriteRefusals,
124
133
  publicFormPath: () => publicFormPath,
125
134
  rememberedOpCount: () => rememberedOpCount,
135
+ sameDocument: () => sameDocument,
126
136
  scopeChip: () => scopeChip,
137
+ stableFilterKey: () => stableFilterKey,
127
138
  staleWriteDetail: () => staleWriteDetail,
128
139
  supabaseDataSource: () => supabaseDataSource,
129
140
  toAoa: () => toAoa
@@ -186,6 +197,46 @@ var OVERRIDE_POLICIES = [
186
197
  var FRESHNESS = ["live", "cached", "snapshot"];
187
198
  var TABLE_DISPLAYS = ["list", "grid", "board", "calendar", "timeline"];
188
199
 
200
+ // src/archive.ts
201
+ var ARCHIVE_LANES = ["mine", "org"];
202
+ var DEFAULT_ARCHIVE_LANE = "org";
203
+ var ARCHIVE_LANE_LABEL = {
204
+ mine: "Archived by me",
205
+ org: "Archived by anyone"
206
+ };
207
+ var ARCHIVE_LANE_MEANS = {
208
+ mine: "The records you archived yourself.",
209
+ org: "Everything anybody in this organization archived, including you."
210
+ };
211
+ function isArchiveLane(word) {
212
+ return typeof word === "string" && ARCHIVE_LANES.includes(word);
213
+ }
214
+ function archivedWhen(at, locale = "en-GB") {
215
+ const when = new Date(at);
216
+ if (Number.isNaN(when.getTime())) return "a moment nobody recorded";
217
+ return new Intl.DateTimeFormat(locale, {
218
+ day: "numeric",
219
+ month: "long",
220
+ year: "numeric",
221
+ hour: "2-digit",
222
+ minute: "2-digit"
223
+ }).format(when);
224
+ }
225
+ function archivedByLine(row, locale = "en-GB") {
226
+ const when = archivedWhen(row.archivedAt, locale);
227
+ const who = (row.archivedByName ?? "").trim();
228
+ if (who === "") {
229
+ return `Archived on ${when}. The store does not know who archived it \u2014 whoever did is no longer in this organization, or it happened before this table kept that.`;
230
+ }
231
+ return `Archived by ${who} on ${when}.`;
232
+ }
233
+ function emptyArchiveLine(lane) {
234
+ return lane === "mine" ? "You have not archived anything in this table. Switch to \u201CArchived by anyone\u201D to see what the rest of the organization has." : "Nothing in this table has been archived. Records you remove from the grid arrive here, and nothing is ever destroyed.";
235
+ }
236
+ function sameDocument(a, b) {
237
+ return JSON.stringify(a) === JSON.stringify(b);
238
+ }
239
+
189
240
  // src/level.ts
190
241
  var PERMISSION_LEVELS = [
191
242
  "viewer",
@@ -484,6 +535,20 @@ function portalPath(slug) {
484
535
  function measureKey(measure) {
485
536
  return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
486
537
  }
538
+ function stableFilterKey(filter) {
539
+ if (!filter) return "{}";
540
+ const keys = Object.keys(filter).sort();
541
+ if (keys.length === 0) return "{}";
542
+ const parts = keys.map((key) => {
543
+ const value = filter[key];
544
+ if (value !== null && typeof value === "object") {
545
+ const window2 = value;
546
+ return `${JSON.stringify(key)}:{"from":${JSON.stringify(window2.from ?? null)},"to":${JSON.stringify(window2.to ?? null)}}`;
547
+ }
548
+ return `${JSON.stringify(key)}:${JSON.stringify(value ?? null)}`;
549
+ });
550
+ return `{${parts.join(",")}}`;
551
+ }
487
552
 
488
553
  // src/capture-queue.ts
489
554
  var DB_NAME = "matrx-capture";
@@ -835,6 +900,27 @@ var PARITY_FIELD_TYPES = [
835
900
  { parity_type: "select", behavior: "list", made_of: "one choice from a Table shown as a list (FLD-5/FLD-6)" },
836
901
  { parity_type: "url", behavior: "text", made_of: "text whose format is url, with the pattern Rule that makes the format enforceable" }
837
902
  ];
903
+ var DECLARABLE_FIELD_KINDS = [
904
+ { kind: "attachment", behavior: "relation", parity: true, made_of: "REC-31: a File record reached through a relation, target the kernel `File` Table" },
905
+ { kind: "checkbox", behavior: "boolean", parity: true, made_of: "a ticked or unticked answer, stored as a REAL boolean \u2014 and a record that never answered holds neither, so filters and group-by see three states, not two" },
906
+ { kind: "currency", behavior: "range", parity: true, made_of: "FLD-N-1: a number whose UNIT is the currency and whose format is currency \u2014 on the Field, never in presentation" },
907
+ { kind: "datetime", behavior: "range", parity: true, made_of: "a range of kind date or datetime \u2014 the dated modifier says the VALUES are dated, which is a different question" },
908
+ { kind: "email", behavior: "text", parity: true, made_of: "text whose format is email" },
909
+ { kind: "formula", behavior: "formula", parity: true, made_of: "FLD-9: a declared computation, on read or on write, evaluated by custom.rule_eval" },
910
+ { kind: "long_text", behavior: "text", parity: false, made_of: "behaviour text with format long and config.multiline, for several lines rather than one" },
911
+ { kind: "lookup", behavior: "formula", parity: true, made_of: "a Value read THROUGH a relation \u2014 config.via names the relation field, config.pick the Value on the far side" },
912
+ { kind: "member", behavior: "relation", parity: true, made_of: "a person: a relation whose target is the kernel `Person` Table" },
913
+ { kind: "multi_select", behavior: "list", parity: true, made_of: "many choices from the same Table \u2014 the multi modifier, never a second behaviour" },
914
+ { kind: "number", behavior: "range", parity: false, made_of: "a plain number: behaviour range with no date kind and neither the currency nor the percent format" },
915
+ { kind: "percent", behavior: "range", parity: true, made_of: "a number whose unit is % and whose format is percent" },
916
+ { kind: "phone", behavior: "text", parity: true, made_of: "text whose format is phone" },
917
+ { kind: "relation", behavior: "relation", parity: false, made_of: "REL / FLD-11: a relation a person aims themselves \u2014 relation_target names a Table of this organization, and it is a BEHAVIOUR, never a parity type of its own" },
918
+ { kind: "rollup", behavior: "formula", parity: true, made_of: "an aggregate ALONG a relation \u2014 config.via, config.of, config.agg; far-side ids are taken DISTINCT, so nothing is counted twice" },
919
+ { kind: "select", behavior: "list", parity: true, made_of: "one choice from a Table shown as a list (FLD-5/FLD-6)" },
920
+ { kind: "signature", behavior: "text", parity: false, made_of: "VAL-10: behaviour text whose format is signature \u2014 the one shape custom.doc_sign accepts, sealed by custom.doc_signature_write and checked by custom.doc_signature_intact" },
921
+ { kind: "text", behavior: "text", parity: false, made_of: "plain text: behaviour text with no format \u2014 it carries no parity type, which is exactly what custom.parity_type answers for it" },
922
+ { kind: "url", behavior: "text", parity: true, made_of: "text whose format is url, with the pattern Rule that makes the format enforceable" }
923
+ ];
838
924
  var RULE_NODE_KINDS = [
839
925
  { node: "actor_at_least", evaluated_by: "PIPELINES" },
840
926
  { node: "add", evaluated_by: "W1-RULE" },
@@ -1070,6 +1156,7 @@ var STORE_DOORS = [
1070
1156
  { name: "field_dependants", args: "p_organization_id uuid, p_field_id uuid", returns: "TABLE(kind text, dependant_id uuid, label text, how text)", security: "definer" },
1071
1157
  { name: "field_history", args: "p_organization_id uuid, p_table_id uuid, p_field_key text, p_limit integer DEFAULT 100, p_offset integer DEFAULT 0, p_record_id uuid DEFAULT NULL::uuid", returns: "TABLE(record_id uuid, record_title text, version integer, occurred_at timestamp with time zone, operation_label text, actor jsonb, before jsonb, after jsonb)", security: "definer" },
1072
1158
  { name: "field_kernel_id", args: "", returns: "uuid", security: "invoker" },
1159
+ { name: "field_kinds", args: "", returns: "TABLE(kind text, behavior text, parity boolean, made_of text)", security: "invoker" },
1073
1160
  { name: "field_options", args: "p_organization_id uuid, p_field_id uuid", returns: "SETOF custom.record", security: "definer" },
1074
1161
  { name: "field_retire", args: "p_organization_id uuid, p_field_id uuid", returns: "boolean", security: "definer" },
1075
1162
  { name: "field_rules", args: "p_field_data jsonb", returns: "TABLE(kind text, spec jsonb)", security: "invoker" },
@@ -1205,6 +1292,7 @@ var STORE_DOORS = [
1205
1292
  { name: "portal_field_map", args: "p_organization_id uuid, p_table_id uuid", returns: "TABLE(field_id uuid, field_key text, field_type text, points_at uuid)", security: "definer" },
1206
1293
  { name: "portal_invitation", args: "p_slug text, p_email text", returns: "jsonb", security: "definer" },
1207
1294
  { name: "portal_invite", args: "p_organization_id uuid, p_portal_id uuid, p_client_record_id uuid, p_email text, p_user_id uuid DEFAULT NULL::uuid", returns: "jsonb", security: "definer" },
1295
+ { name: "portal_invite_accept", args: "p_token text", returns: "jsonb", security: "definer" },
1208
1296
  { name: "portal_me", args: "", returns: "jsonb", security: "definer" },
1209
1297
  { name: "portal_preview", args: "p_organization_id uuid, p_portal_id uuid, p_principal_id uuid, p_table_id uuid", returns: "TABLE(record_id uuid, title text)", security: "definer" },
1210
1298
  { name: "portal_principal_bind", args: "p_organization_id uuid, p_principal_id uuid, p_user_id uuid", returns: "jsonb", security: "definer" },
@@ -1257,14 +1345,18 @@ var STORE_DOORS = [
1257
1345
  { name: "read_paths_outside_the_door", args: "", returns: "TABLE(object_name text, why text)", security: "invoker" },
1258
1346
  { name: "read_record", args: "p_organization_id uuid, p_record_id uuid, p_by_id boolean DEFAULT false", returns: "jsonb", security: "definer" },
1259
1347
  { name: "read_records", args: "p_organization_id uuid, p_table_id uuid, p_by_id boolean DEFAULT false, p_limit integer DEFAULT 200, p_offset integer DEFAULT 0", returns: "TABLE(id uuid, document jsonb, level permission_level)", security: "definer" },
1348
+ { name: "read_records_archived", args: "p_organization_id uuid, p_table_id uuid, p_lane text DEFAULT 'org'::text, p_by_id boolean DEFAULT false, p_limit integer DEFAULT 200, p_offset integer DEFAULT 0", returns: "TABLE(id uuid, document jsonb, level permission_level, archived_at timestamp with time zone, archived_by uuid, archived_by_name text)", security: "definer" },
1260
1349
  { name: "read_records_by_ids", args: "p_organization_id uuid, p_table_id uuid, p_record_ids uuid[], p_by_id boolean DEFAULT false", returns: "TABLE(id uuid, document jsonb, level permission_level)", security: "definer" },
1350
+ { name: "read_records_matching", args: "p_organization_id uuid, p_table_id uuid, p_filter jsonb DEFAULT '{}'::jsonb, p_by_id boolean DEFAULT false, p_limit integer DEFAULT 200, p_offset integer DEFAULT 0", returns: "TABLE(id uuid, document jsonb, level permission_level)", security: "definer" },
1261
1351
  { name: "realtime_topic_admits", args: "p_topic text", returns: "boolean", security: "definer" },
1262
1352
  { name: "record_aggregate", args: "p_organization_id uuid, p_table_id uuid, p_group_by jsonb DEFAULT '[]'::jsonb, p_measures jsonb DEFAULT '[]'::jsonb, p_bucket jsonb DEFAULT NULL::jsonb, p_filter jsonb DEFAULT '{}'::jsonb, p_limit integer DEFAULT 200, p_required text DEFAULT 'viewer'::text", returns: "TABLE(groups jsonb, measures jsonb, row_count bigint)", security: "definer" },
1263
1353
  { name: "record_applicability", args: "p_organization_id uuid, p_record_id uuid", returns: "TABLE(rule_id uuid, rule_name text, rule_version integer, applies boolean, answer jsonb)", security: "invoker" },
1354
+ { name: "record_archiver", args: "p_organization_id uuid, p_record_id uuid", returns: "uuid", security: "definer" },
1264
1355
  { name: "record_as_of", args: "p_organization_id uuid, p_record_id uuid, p_at timestamp with time zone", returns: "TABLE(state jsonb, replayed boolean)", security: "definer" },
1265
1356
  { name: "record_card", args: "p_viewer uuid, p_organization_id uuid, p_record_id uuid", returns: "jsonb", security: "definer" },
1266
1357
  { name: "record_carrying_edges", args: "p_id uuid, p_data_class text, p_data jsonb, p_deleted_at timestamp with time zone", returns: "TABLE(container_id uuid, item_id uuid, edge_role text)", security: "invoker" },
1267
1358
  { name: "record_delete", args: "p_organization_id uuid, p_record_id uuid", returns: "timestamp with time zone", security: "definer" },
1359
+ { name: "record_filter_sql", args: "p_filter jsonb", returns: "text", security: "invoker" },
1268
1360
  { name: "record_history", args: "p_organization_id uuid, p_record_id uuid, p_limit integer DEFAULT 200, p_offset integer DEFAULT 0", returns: "TABLE(version integer, occurred_at timestamp with time zone, operation text, operation_label text, actor jsonb, changes jsonb, migration_id uuid, undoable boolean)", security: "definer" },
1269
1361
  { name: "record_platform_keys", args: "", returns: "text[]", security: "invoker" },
1270
1362
  { name: "record_relation_edges", args: "p_organization_id uuid, p_id uuid, p_table_id uuid, p_data_class text, p_data jsonb, p_deleted_at timestamp with time zone", returns: "TABLE(target_id uuid, edge_role text, field_id uuid, ord integer)", security: "invoker" },
@@ -1353,6 +1445,7 @@ var STORE_DOORS = [
1353
1445
  { name: "subscription_mute", args: "p_organization_id uuid, p_rule_id uuid, p_muted boolean DEFAULT true", returns: "boolean", security: "definer" },
1354
1446
  { name: "subscription_preview", args: "p_organization_id uuid, p_rule_id uuid", returns: "jsonb", security: "definer" },
1355
1447
  { name: "subscriptions", args: "p_organization_id uuid, p_table_id uuid DEFAULT NULL::uuid", returns: "TABLE(rule_id uuid, name text, table_id uuid, saved_view_id uuid, cadence text, schedule text, quiet_hours jsonb, channel text, recipient_user_id uuid, event_key text, muted boolean, mine boolean, i_may_mute boolean, last_sent_at timestamp with time zone, next_digest_at timestamp with time zone)", security: "definer" },
1448
+ { name: "table_archive", args: "p_organization_id uuid, p_table_id uuid, p_chunk integer DEFAULT 50, p_include_table boolean DEFAULT true", returns: "jsonb", security: "definer" },
1356
1449
  { name: "table_capacity", args: "p_organization_id uuid, p_table_id uuid", returns: "jsonb", security: "definer" },
1357
1450
  { name: "table_carries_its_rows", args: "p_user_id uuid, p_table_id uuid, p_required permission_level DEFAULT 'viewer'::permission_level", returns: "boolean", security: "definer" },
1358
1451
  { name: "table_column_source", args: "p_organization_id uuid, p_table_id uuid", returns: "text", security: "invoker" },
@@ -1485,18 +1578,18 @@ var STORE_KNOBS = [
1485
1578
  { key: "world_publish_enabled", value: "false", type: "boolean" }
1486
1579
  ];
1487
1580
  var STORE_REFUSAL_CODES = [
1488
- { code: "02000", raised_by: "_checklist_instantiate,_table_share_invite_payload,checklist_declare,checklist_run,checklist_step_complete,checklist_step_refusal,checklist_template_shape,conversation_scope_bind,dashboard_delete,dashboard_run,doc_letterhead,doc_render_body,doc_render_document,doc_render_read,doc_sign,doc_signature_intact,doc_signature_read,doc_template_delete,doc_template_read,doc_template_save,enrich_pin,entity_field_retire,entity_field_update,entity_record_read,entity_value_write,external_history_event,external_rows,external_stub_upsert,external_writes_set,io_restore,migrate_delete,migrate_extract_parent,migrate_merge,migrate_reclass,migrate_rename,migrate_reparent,migrate_retype,migrate_split,migrate_undo,organization_clear,portal_card,portal_declare,portal_invite,portal_preview,portal_principal_bind,portal_revoke,read_record,record_delete,record_restore,record_restore_preview,record_table,record_update,relation_uncarry,share_access,share_grant,share_lane_set,share_people,share_revoke,sign_request_cancel,sign_request_create,sign_request_remind,sign_request_unchanged,table_share_outside_accept,table_share_outside_invite,table_share_outside_resend,table_share_outside_revoke,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_assign,work_record_states,work_set_state" },
1581
+ { code: "02000", raised_by: "_checklist_instantiate,_portal_invite_payload,_share_write_person,_table_share_invite_payload,checklist_declare,checklist_run,checklist_step_complete,checklist_step_refusal,checklist_template_shape,conversation_scope_bind,dashboard_delete,dashboard_run,doc_letterhead,doc_render_body,doc_render_document,doc_render_read,doc_sign,doc_signature_intact,doc_signature_read,doc_template_delete,doc_template_read,doc_template_save,enrich_pin,entity_field_retire,entity_field_update,entity_record_read,entity_value_write,external_history_event,external_rows,external_stub_upsert,external_writes_set,io_restore,migrate_delete,migrate_extract_parent,migrate_merge,migrate_reclass,migrate_rename,migrate_reparent,migrate_retype,migrate_split,migrate_undo,organization_clear,portal_card,portal_declare,portal_invite,portal_invite_accept,portal_preview,portal_principal_bind,portal_revoke,read_record,record_delete,record_restore,record_restore_preview,record_table,record_update,relation_uncarry,share_access,share_grant,share_lane_set,share_people,share_revoke,sign_request_cancel,sign_request_create,sign_request_remind,sign_request_unchanged,table_archive,table_share_outside_accept,table_share_outside_invite,table_share_outside_resend,table_share_outside_revoke,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_assign,work_record_states,work_set_state" },
1489
1582
  { code: "0A000", raised_by: "checklist_step_complete,external_rows,external_source_declare,external_write_through,promote_field,promoted_index_expr,promoted_query_sql,promoted_read,rule_eval,work_assign" },
1490
1583
  { code: "22003", raised_by: "context_resolve" },
1491
- { code: "22004", raised_by: "_entity_custom_fields_guard,_value_envelope,actor_word,agg_assert_key,agg_sql,anon_capture,anon_token_issue,anon_write,assert_client_may_reach,booking_declare,capture_submit,checklist_declare,context_resolve,dashboard_block_normalize,dashboard_declare,dashboard_window_sql,doc_render_write,doc_sign,doc_signature_write,doc_template_save,enrich_land,enrich_normalize,external_history_event,external_rows,external_source_declare,external_stub_upsert,external_write_through,external_writes_set,field_history,form_declare,form_submit,home_add,io_comment_write,io_import_begin,io_infer_column,io_outbox_drain,io_restore,migrate_choice_keys,migrate_purge,migrate_reclass,migrate_split,migrate_undo,organization_clear,organization_contents,pipeline_declare,pipeline_gate_preview,portal_declare,portal_invite,portal_principal_bind,provenance_retention_days,provenance_write,query_across_homes,query_by_coordinates,query_rollup,record_delete,record_reparent,record_resolve,record_restore,record_update,record_write,record_write_many,relation_carry,relation_own,rule_declare,share_grant,sign_request_create,sign_request_sign,subscription_declare,table_declare,table_share_outside_invite,value_restore,view_declare,work_approval_decide,work_approval_request,work_template_declare" },
1584
+ { code: "22004", raised_by: "_entity_custom_fields_guard,_share_write_person,_value_envelope,actor_word,agg_assert_key,agg_sql,anon_capture,anon_token_issue,anon_write,assert_client_may_reach,booking_declare,capture_submit,checklist_declare,context_resolve,dashboard_block_normalize,dashboard_declare,dashboard_window_sql,doc_render_write,doc_sign,doc_signature_write,doc_template_save,enrich_land,enrich_normalize,external_history_event,external_rows,external_source_declare,external_stub_upsert,external_write_through,external_writes_set,field_history,form_declare,form_submit,home_add,io_comment_write,io_import_begin,io_infer_column,io_outbox_drain,io_restore,migrate_choice_keys,migrate_purge,migrate_reclass,migrate_split,migrate_undo,organization_clear,organization_contents,pipeline_declare,pipeline_gate_preview,portal_declare,portal_invite,portal_principal_bind,provenance_retention_days,provenance_write,query_across_homes,query_by_coordinates,query_rollup,record_delete,record_reparent,record_resolve,record_restore,record_update,record_write,record_write_many,relation_carry,relation_own,rule_declare,share_grant,sign_request_create,sign_request_sign,subscription_declare,table_archive,table_declare,table_share_outside_invite,value_restore,view_declare,work_approval_decide,work_approval_request,work_template_declare" },
1492
1585
  { code: "22007", raised_by: "dashboard_window_sql" },
1493
1586
  { code: "22012", raised_by: "rule_eval" },
1494
- { code: "22023", raised_by: "_booking_availability,_entity_custom_fields_guard,_take_op_id,_value_envelope,_work_shape_guard,actor_word,agg_assert_key,agg_sql,anon_token_issue,booking_declare,containment_parent,dashboard_block_normalize,dashboard_window_sql,doc_render_body,doc_render_write,doc_signature_write,enrich_declare,enrich_land,enrich_normalize,enrich_pin,entity_value_write,external_history_event,external_source_declare,external_stub_upsert,external_write_through,field_history,field_update,io_export,io_import_begin,io_import_finish,io_import_plan,io_import_report,io_proposal_accept,migrate_merge,migrate_reclass,my_levels,organization_clear,page_size,pin_agent_cells,pipeline_move,portal_declare,provenance_write,query_by_coordinates,query_rollup,record_update,record_write_many,relation_carry,rule_eval,rule_members,share_grant,share_lane_set,sign_request_create,sign_request_sign,subscription_declare,table_rules,visibility_as_of,work_approval_request,work_list,work_slot_hold" },
1495
- { code: "23503", raised_by: "_booking_availability,_checklist_instantiate,_containment_guard,_display_spec_for,_field_document_for,_pipeline_stage_key,_record_rule_uses,agg_digest_assemble,anon_publish,anon_rate_take,anon_token_issue,anon_write,assert_organization_wall,booking_cancel,booking_confirm,booking_declare,booking_hold,booking_reschedule,capture_publish,checklist_declare,checklist_steps_table,comment_write,dashboard_declare,delete_rule,doc_sign,doc_template_save,enrich_declare,enrich_due,enrich_land,form_declare,form_submit,home_add,inbound_declare,inbound_mail_land,inbound_set,io_comment_write,io_import_begin,io_import_finish,io_import_report,io_import_rows,io_proposal_accept,migrate_demote,migrate_promote,pipeline_board,pipeline_declare,pipeline_move,pipeline_read,pipeline_transition_refusal,promote_field,promote_table,record_applicability,record_reparent,relation_carry,relation_own,resolve_first_match,rule_applies,rule_declare,rule_eval,rule_members,rule_run,sign_request_create,subscription_mute,subscription_preview,view_declare,work_set_state,work_take_assignment,work_template_instantiate" },
1496
- { code: "23505", raised_by: "_unique_rule_holds,doc_sign,doc_signature_write,entity_field_declare,field_declare,home_add,inbound_declare,io_proposal_accept,relation_carry,relation_own,share_grant,sign_request_cancel,sign_request_create,table_share_outside_invite,table_share_outside_resend,work_approval_decide,work_slot_hold" },
1497
- { code: "23514", raised_by: "_checklist_instantiate,_checklist_step_guard,_containment_guard,_dated_values_guard,_derived_fields,_display_spec_for,_entity_custom_fields_guard,_field_class_guard,_field_document_for,_field_shape_guard,_field_type_parity_guard,_merge_field_shape_guard,_merge_field_temporal_guard,_promoted_field_cap_guard,_record_rule_uses,_resolve_choice_words,_rule_shape_guard,_rule_topology_guard,_store_relation_edge_names_its_field,_table_columns_word_guard,_table_shape_guard,_undeclared_key_guard,_value_envelope,_with_display,_work_shape_guard,_workdoors_approval_guard,assert_columns_are_defined,assert_entity_is_organization_scoped,booking_declare,capture_publish,capture_sheet_declare,checklist_declare,checklist_step_complete,doc_sign,doc_signature_write,doc_template_save,entity_field_declare,entity_field_retire,entity_field_update,entity_records_find,entity_table,field_declare,field_retire,field_update,home_add,io_import_rows,migrate_merge,relation_carry,relation_own,sign_request_create,sign_request_sign,table_declare,validate_value_envelope,validate_values,work_set_state,work_slots_declare,work_take_assignment,work_template_instantiate" },
1587
+ { code: "22023", raised_by: "_booking_availability,_entity_custom_fields_guard,_take_op_id,_value_envelope,_work_shape_guard,actor_word,agg_assert_key,agg_sql,anon_token_issue,booking_declare,containment_parent,dashboard_block_normalize,dashboard_window_sql,doc_render_body,doc_render_write,doc_signature_write,enrich_declare,enrich_land,enrich_normalize,enrich_pin,entity_value_write,external_history_event,external_source_declare,external_stub_upsert,external_write_through,field_history,field_update,io_export,io_import_begin,io_import_finish,io_import_plan,io_import_report,io_proposal_accept,migrate_merge,migrate_reclass,my_levels,organization_clear,page_size,pin_agent_cells,pipeline_move,portal_declare,provenance_write,query_by_coordinates,query_rollup,read_records_archived,record_update,record_write_many,relation_carry,rule_eval,rule_members,share_grant,share_lane_set,sign_request_create,sign_request_sign,subscription_declare,table_rules,visibility_as_of,work_approval_request,work_list,work_slot_hold" },
1588
+ { code: "23503", raised_by: "_booking_availability,_checklist_instantiate,_containment_guard,_display_spec_for,_field_document_for,_field_type_parity_guard,_pipeline_stage_key,_record_rule_uses,agg_digest_assemble,anon_publish,anon_rate_take,anon_token_issue,anon_write,assert_organization_wall,booking_cancel,booking_confirm,booking_declare,booking_hold,booking_reschedule,capture_publish,checklist_declare,checklist_steps_table,comment_write,dashboard_declare,delete_rule,doc_sign,doc_template_save,enrich_declare,enrich_due,enrich_land,form_declare,form_submit,home_add,inbound_declare,inbound_mail_land,inbound_set,io_comment_write,io_import_begin,io_import_finish,io_import_report,io_import_rows,io_proposal_accept,migrate_demote,migrate_promote,pipeline_board,pipeline_declare,pipeline_move,pipeline_read,pipeline_transition_refusal,promote_field,promote_table,record_applicability,record_reparent,relation_carry,relation_own,resolve_first_match,rule_applies,rule_declare,rule_eval,rule_members,rule_run,sign_request_create,subscription_mute,subscription_preview,view_declare,work_set_state,work_take_assignment,work_template_instantiate" },
1589
+ { code: "23505", raised_by: "_share_write_person,_unique_rule_holds,doc_sign,doc_signature_write,entity_field_declare,field_declare,home_add,inbound_declare,io_proposal_accept,relation_carry,relation_own,sign_request_cancel,sign_request_create,table_share_outside_resend,work_approval_decide,work_slot_hold" },
1590
+ { code: "23514", raised_by: "_checklist_instantiate,_checklist_step_guard,_containment_guard,_dated_values_guard,_derived_fields,_display_spec_for,_entity_custom_fields_guard,_field_class_guard,_field_document_for,_field_shape_guard,_field_type_parity_guard,_merge_field_shape_guard,_merge_field_temporal_guard,_promoted_field_cap_guard,_record_rule_uses,_resolve_choice_words,_rule_shape_guard,_rule_topology_guard,_store_relation_edge_names_its_field,_table_columns_word_guard,_table_shape_guard,_undeclared_key_guard,_value_envelope,_with_display,_work_shape_guard,_workdoors_approval_guard,assert_columns_are_defined,assert_entity_is_organization_scoped,booking_declare,capture_publish,capture_sheet_declare,checklist_declare,checklist_step_complete,doc_sign,doc_signature_write,doc_template_save,entity_field_declare,entity_field_retire,entity_field_update,entity_records_find,entity_table,field_declare,field_retire,field_update,home_add,io_comment_write,io_import_rows,migrate_merge,relation_carry,relation_own,sign_request_create,sign_request_sign,table_declare,validate_value_envelope,validate_values,work_set_state,work_slots_declare,work_take_assignment,work_template_instantiate" },
1498
1591
  { code: "40001", raised_by: "has_visibility_at" },
1499
- { code: "42501", raised_by: "_doc_render_immutable,_doc_signature_immutable,_field_definition_write,_field_write_door,_rule_definition_write,_store_door,anon_capture,anon_inbound_land,anon_publish,anon_token_issue,anon_token_verify,anon_write,assert_client_may_change,assert_client_may_open,assert_client_may_reach,assert_may_know_table,assert_organization_admin,assert_store_door,booking_confirm,booking_declare,booking_hold,capture_submit,checklist_run,comment_write,conversation_scope_bind,conversation_scope_unbind,enrich_declare,enrich_due,enrich_land,enrich_normalize,entity_field_declare,entity_field_retire,entity_field_update,entity_value_write,external_write_through,form_declare,form_submit,inbound_mail_land,io_comment_resolve,io_comment_write,io_import_begin,io_import_finish,io_restore,migrate_purge,migrate_reclass,my_levels,organization_clear,portal_declare,portal_invitation,portal_invite,portal_principal_bind,promote_table,query_visibility_parity,read_record,read_records,read_records_by_ids,record_table,share_grant,subscription_mute,subscription_preview,table_share_outside_accept,table_share_outside_invite,table_share_outside_resend,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_person" },
1592
+ { code: "42501", raised_by: "_doc_render_immutable,_doc_signature_immutable,_field_definition_write,_field_write_door,_rule_definition_write,_store_door,agent_table_claim,anon_capture,anon_inbound_land,anon_publish,anon_token_issue,anon_token_verify,anon_write,assert_client_may_change,assert_client_may_open,assert_client_may_reach,assert_may_know_table,assert_organization_admin,assert_store_door,booking_confirm,booking_declare,booking_hold,capture_submit,checklist_run,comment_write,conversation_scope_bind,conversation_scope_unbind,doc_sign,enrich_declare,enrich_due,enrich_land,enrich_normalize,entity_field_declare,entity_field_retire,entity_field_update,entity_value_write,external_write_through,form_declare,form_submit,inbound_mail_land,io_comment_resolve,io_comment_write,io_import_begin,io_import_finish,io_restore,migrate_purge,migrate_reclass,my_levels,organization_clear,portal_declare,portal_invitation,portal_invite,portal_invite_accept,portal_principal_bind,promote_table,query_visibility_parity,read_record,read_records,read_records_archived,read_records_by_ids,read_records_matching,record_table,share_grant,subscription_mute,subscription_preview,table_share_outside_accept,table_share_outside_invite,table_share_outside_resend,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_person" },
1500
1593
  { code: "42703", raised_by: "entity_table,portal_declare" },
1501
1594
  { code: "53400", raised_by: "anon_rate_take,delete_cascade_closure,enrich_due,enrich_land,promote_field" },
1502
1595
  { code: "54001", raised_by: "record_delete" },
@@ -2392,6 +2485,7 @@ var ENTITY_TOKENS = [
2392
2485
  "template_scope_types",
2393
2486
  "templates",
2394
2487
  "test_handset_inbox",
2488
+ "test_handset_verification",
2395
2489
  "thread",
2396
2490
  "tool",
2397
2491
  "tool_binding",
@@ -2592,6 +2686,23 @@ var DOORS = {
2592
2686
  // live Postgres function makes every existing call ambiguous — `is not unique`. Same
2593
2687
  // ladder, same level, same masking, same page ceiling.
2594
2688
  readRecordsByIds: "read_records_by_ids",
2689
+ // THE SAME READ DOOR, ADDRESSED BY A QUESTION. `custom.read_records_matching`
2690
+ // takes the store's one filter shape (`RecordFilter` — the object
2691
+ // `platform.saved_view` carries as `filters` and `custom.record_aggregate`
2692
+ // takes as `p_filter`) and narrows the page INSIDE its own query, in the same
2693
+ // WHERE as Visibility. A separate NAME for the same reason as above. It is
2694
+ // what makes a number on a dashboard walkable: the filter is evaluated by
2695
+ // `custom.record_filter_sql`, the one body `custom.agg_sql` also calls, so the
2696
+ // rows it opens are the rows the number counted.
2697
+ readRecordsMatching: "read_records_matching",
2698
+ // THE SAME READ DOOR, ADDRESSED BY ABSENCE. Every door above answers LIVE rows
2699
+ // only — each of them hard-codes `and r.deleted_at is null` — so until this one
2700
+ // existed no screen could list an archived record however it was written, and
2701
+ // `record_restore` below had nothing that could reach it (VERIFIER-10 F4).
2702
+ // `custom.read_records_archived` is that body with the sign of one comparison
2703
+ // flipped, plus the two facts only an archive needs: who archived it and when.
2704
+ // A separate NAME for the same reason as its two siblings above.
2705
+ readRecordsArchived: "read_records_archived",
2595
2706
  // DOOR-8, the two clocks. `p_recorded_at` is what the store SAID then;
2596
2707
  // `p_world_on` is what was TRUE of the world on that date, read out of dated
2597
2708
  // values. A client that answered either one itself would be guessing.
@@ -2886,7 +2997,14 @@ var DOORS = {
2886
2997
  docTokens: "doc_tokens",
2887
2998
  docUnresolvedTokens: "doc_unresolved_tokens",
2888
2999
  docSign: "doc_sign",
2889
- docSignatureIntact: "doc_signature_intact",
3000
+ // THE SEAL IS ASKED THROUGH ITS OWN DOOR. `custom.doc_signature_intact` is the INTERNAL
3001
+ // half of this pair — SECURITY INVOKER over a doors-only table, so a browser that calls it
3002
+ // gets `42501 permission denied for table doc_signature`, which is what every SignBlock
3003
+ // seal check got until 2026-09-22 (FIX-10B). `custom.doc_signature_read` is the definer
3004
+ // door built for a client: it asks may_reach and may_open on the RECORD by name and
3005
+ // returns the seal AND the verdict in one answer, because a seal a person can read and a
3006
+ // verdict they cannot is how a changed agreement keeps wearing somebody's signature.
3007
+ docSignatureRead: "doc_signature_read",
2890
3008
  // THE PLURAL ACTS. Until 2026-09-20 this package read `custom.doc_template`,
2891
3009
  // `custom.doc_render` and `custom.doc_signature` with a direct PostgREST
2892
3010
  // `.from()`, and all three hold NO client SELECT — so every list in product #5
@@ -3291,12 +3409,41 @@ function createRecordsClient(config) {
3291
3409
  })
3292
3410
  );
3293
3411
  },
3294
- async list({ table_id, limit = 50, offset = 0 }) {
3412
+ async listArchived({ table_id, lane = DEFAULT_ARCHIVE_LANE, limit = 50, offset = 0 }) {
3295
3413
  const door = await callDoor(
3296
- DOORS.readRecords,
3414
+ DOORS.readRecordsArchived,
3415
+ {
3416
+ p_organization_id: org,
3417
+ p_table_id: table_id,
3418
+ p_lane: lane,
3419
+ p_limit: limit,
3420
+ p_offset: offset
3421
+ },
3422
+ "listArchived"
3423
+ );
3424
+ if (!door.ok) return err(door.error);
3425
+ const rows = (door.data ?? []).map((row) => {
3426
+ const { document, hidden } = unfold(row.document);
3427
+ return {
3428
+ id: row.id,
3429
+ document,
3430
+ level: row.level,
3431
+ hidden,
3432
+ archivedAt: row.archived_at,
3433
+ archivedBy: row.archived_by ?? null,
3434
+ archivedByName: row.archived_by_name ?? null
3435
+ };
3436
+ });
3437
+ const total = rows.length < limit ? offset + rows.length : null;
3438
+ return ok({ rows, total });
3439
+ },
3440
+ async list({ table_id, filter, limit = 50, offset = 0 }) {
3441
+ const door = await callDoor(
3442
+ DOORS.readRecordsMatching,
3297
3443
  {
3298
3444
  p_organization_id: org,
3299
3445
  p_table_id: table_id,
3446
+ p_filter: filter ?? {},
3300
3447
  p_limit: limit,
3301
3448
  p_offset: offset
3302
3449
  },
@@ -4437,11 +4584,11 @@ function createRecordsClient(config) {
4437
4584
  "commentResolve"
4438
4585
  );
4439
4586
  },
4440
- async docSignatureIntact({ signature_id }) {
4587
+ async docSignatureRead({ signature_id }) {
4441
4588
  return callDoor(
4442
- DOORS.docSignatureIntact,
4589
+ DOORS.docSignatureRead,
4443
4590
  { p_organization_id: org, p_signature_id: signature_id },
4444
- "docSignatureIntact"
4591
+ "docSignatureRead"
4445
4592
  );
4446
4593
  },
4447
4594
  // ── the anonymous lane ───────────────────────────────────────────────