@ai-matrx/records 0.37.0 → 0.40.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,81 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.40.0
4
+
5
+ **The parity floor this package types is asserted against the one the store ships.**
6
+
7
+ `checkbox` landed as the fourteenth parity type and `store.generated.ts` picked it
8
+ up — but nothing made that agreement a test. Everything that spelled the floor out
9
+ by hand went stale in the same hour: three of the store's own refusal hints, the
10
+ `FieldTypeWord` doc comment, `UseCaseField.parityType`'s doc comment and the
11
+ use-case README's list, all still saying thirteen. A person who asked for a
12
+ checkbox could be told by the store that checkboxes do not exist.
13
+
14
+ * `src/__tests__/real-doors.test.ts` gains three cases, in the one suite that runs
15
+ against the live store: `PARITY_FIELD_TYPES` is exactly
16
+ `custom.parity_field_types()` name for name (a diff NAMES the missing type, it
17
+ never prints a count); every parity type and every behaviour the store ships is
18
+ a legal `parityType` word in `RESOLVABLE_FIELD_TYPE_WORDS`; and `checkbox` is
19
+ present with behaviour `boolean`, with a compile-time assertion that the
20
+ `ParityFieldType` union still admits it. Shown RED: with the `checkbox` row
21
+ removed from `store.generated.ts` all three fail and the first one says
22
+ "expected 'attachment, currency, …' to be 'attachment, checkbox, …'".
23
+ * The four places that wrote the count or the list out by hand now point at the
24
+ derived set instead. No number is written down anywhere in this package.
25
+ * `store.generated.ts` regenerated against the live store (542 doors, 1023 entity
26
+ tokens); `pnpm check:store-registry` was RED on that drift before this release.
27
+
28
+ **Consumer action:** none.
29
+
30
+ **Why 0.40.0 and not 0.38.0.** This work landed on `main` a few minutes after
31
+ 0.39.0 was tagged, so the published 0.39.0 does not contain it. The version number
32
+ moves rather than the tag.
33
+
34
+ ## 0.39.0
35
+
36
+ **Two things a records screen could not know: that a table's shape had changed,
37
+ and how many records it was actually looking at.**
38
+
39
+ **A table's shape now travels.** A table's columns are read by several surfaces
40
+ at once — the grid draws them, the field rail lists them, a form builder offers
41
+ them, a filter picks among them — and each called `useFields(tableId)` and got
42
+ its OWN copy. Nothing joined them, so a column added in one left every other
43
+ one drawing a table that no longer existed. Measured on production 2026-09-21
44
+ at Ridgeline Physical Therapy: "Add field" succeeded, the panel confirmed it
45
+ with a read-back and closed, and the grid beside it did not show the column
46
+ until a full page reload. The panel was not lying — the column WAS made — but
47
+ the screen next to it was showing a table that was already out of date, which
48
+ is the same thing to the person looking at it.
49
+
50
+ `tableShapeChanged(tableId)` is one table-scoped revision. `useFields`
51
+ subscribes to it and registers its interest on read; `useFieldMutation`'s
52
+ `declare`, `update` AND `retire` all announce — all three, because retyping a
53
+ column and retiring one leave exactly the same staleness, and a fix that only
54
+ knew about `declare` would have left two thirds of the class open. A listener
55
+ re-reads THROUGH THE DOOR: the tick is a nudge and never data. It is
56
+ deliberately independent of realtime, so a host that binds no realtime port
57
+ behaves identically here — realtime only makes a shape change travel between
58
+ browsers.
59
+
60
+ **A visible total instead of a shrug.** `client.list` answered `total: null`
61
+ unconditionally, so a table holding ten records told its owner *"Unknown total
62
+ — showing the 10 records loaded so far"* — a screen refusing to say a thing it
63
+ knows. A SHORT PAGE already answers the question: if the door was asked for
64
+ `limit` and handed back fewer, the reader has reached the end of the set it is
65
+ allowed to see, so that set's size is exactly `offset + rows.length`. No count
66
+ is ever asked for, no second query is made, and a full page still answers null,
67
+ because "at least this many" is not a total.
68
+
69
+ 🚨 That rests on one property of `custom.read_records`, and it is not the
70
+ obvious one. The door DOES clamp, through `custom.page_size` — the first draft
71
+ of the comment beside the inference said it did not, and writing the guard is
72
+ what caught that. What makes it safe is what the helper does at the ceiling: it
73
+ REFUSES BY NAME and reads nothing, rather than quietly serving fewer. So a
74
+ successful call that returns fewer rows than it asked for has reached the end
75
+ of the set, and nothing else. `src/__tests__/page-truth.test.ts` asserts the
76
+ refusal against the live store, so the day it softens into a silent shrink the
77
+ suite goes red and names the inference that has to change with it.
78
+
3
79
  ## 0.37.0
4
80
 
5
81
  **Three doors the store already had and this client could not reach.**
@@ -668,6 +668,7 @@ var STORE_DOORS = [
668
668
  { name: "assert_client_may_change", args: "p_organization_id uuid, p_subject_id uuid, p_door text, p_required permission_level DEFAULT 'editor'::permission_level, p_subject_word text DEFAULT 'record'::text", returns: "void", security: "invoker" },
669
669
  { name: "assert_client_may_open", args: "p_organization_id uuid, p_subject_id uuid, p_door text, p_required permission_level DEFAULT 'viewer'::permission_level, p_subject_word text DEFAULT 'record'::text", returns: "void", security: "invoker" },
670
670
  { name: "assert_client_may_reach", args: "p_organization_id uuid, p_door text", returns: "void", security: "invoker" },
671
+ { name: "assert_columns_are_defined", args: "p_organization_id uuid, p_record_id uuid, p_patch jsonb", returns: "void", security: "definer" },
671
672
  { name: "assert_entity_door", args: "p_organization_id uuid, p_door text", returns: "void", security: "definer" },
672
673
  { name: "assert_entity_is_organization_scoped", args: "p_token text, p_label text, p_scoped boolean", returns: "void", security: "invoker" },
673
674
  { name: "assert_may_know_table", args: "p_organization_id uuid, p_table_id uuid, p_door text", returns: "void", security: "invoker" },
@@ -722,6 +723,7 @@ var STORE_DOORS = [
722
723
  { name: "comment_mention_deliver", args: "p_organization_id uuid, p_record_id uuid, p_table_id uuid, p_comment_id uuid, p_recipient uuid, p_author_name text, p_record_title text, p_body text", returns: "uuid", security: "definer" },
723
724
  { name: "comment_thread", args: "p_organization_id uuid, p_record_id uuid, p_include_resolved boolean DEFAULT false", returns: "jsonb", security: "definer" },
724
725
  { name: "comment_write", args: "p_organization_id uuid, p_record_id uuid, p_body text, p_anchor jsonb DEFAULT '{}'::jsonb, p_parent_comment_id uuid DEFAULT NULL::uuid, p_mentions uuid[] DEFAULT NULL::uuid[]", returns: "jsonb", security: "definer" },
726
+ { name: "computed_block", args: "p_block jsonb", returns: "jsonb", security: "invoker" },
725
727
  { name: "computed_provenance", args: "p_organization_id uuid, p_record_id uuid", returns: "TABLE(field_key text, field_id uuid, value jsonb, rule_id uuid, rule_version integer, computed_at timestamp with time zone)", security: "invoker" },
726
728
  { name: "containment_chain", args: "p_organization_id uuid, p_record_id uuid", returns: "TABLE(ancestor_id uuid, depth integer)", security: "invoker" },
727
729
  { name: "containment_depth_ceiling", args: "p_organization_id uuid DEFAULT NULL::uuid", returns: "integer", security: "invoker" },
@@ -753,6 +755,7 @@ var STORE_DOORS = [
753
755
  { name: "derive_visibility", args: "p_container_type text, p_container_id uuid", returns: "TABLE(item_type text, item_id uuid, depth integer, max_level permission_level)", security: "definer" },
754
756
  { name: "derived_value", args: "p_organization_id uuid, p_record_id uuid, p_field_data jsonb, p_values jsonb DEFAULT NULL::jsonb", returns: "jsonb", security: "invoker" },
755
757
  { name: "derived_values", args: "p_organization_id uuid, p_record_id uuid", returns: "jsonb", security: "invoker" },
758
+ { name: "derived_values_of", args: "v_rec custom.record", returns: "jsonb", security: "invoker" },
756
759
  { name: "doc_content_hash", args: "p_body text", returns: "text", security: "invoker" },
757
760
  { name: "doc_format_value", args: "p_field_data jsonb, p_value jsonb", returns: "text", security: "invoker" },
758
761
  { name: "doc_letterhead", args: "p_organization_id uuid", returns: "jsonb", security: "definer" },
@@ -1018,6 +1021,7 @@ var STORE_DOORS = [
1018
1021
  { name: "record_table", args: "p_organization_id uuid, p_record_id uuid", returns: "uuid", security: "definer" },
1019
1022
  { name: "record_update", args: "p_organization_id uuid, p_record_id uuid, p_patch jsonb, p_expected_version integer DEFAULT NULL::integer", returns: "integer", security: "definer" },
1020
1023
  { name: "record_values", args: "p_organization_id uuid, p_record_id uuid", returns: "jsonb", security: "invoker" },
1024
+ { name: "record_values_of", args: "p_row custom.record", returns: "jsonb", security: "invoker" },
1021
1025
  { name: "record_values_versioned", args: "p_organization_id uuid, p_record_id uuid", returns: "TABLE(field_key text, field_id uuid, value jsonb, value_version integer, source jsonb, absent_reason text, actor text, on_behalf_of text, written_at timestamp with time zone, alternates jsonb)", security: "definer" },
1022
1026
  { name: "record_write", args: "p_organization_id uuid, p_table_id uuid, p_data jsonb", returns: "uuid", security: "definer" },
1023
1027
  { name: "record_write_many", args: "p_organization_id uuid, p_table_id uuid, p_rows jsonb[], p_ids uuid[] DEFAULT NULL::uuid[]", returns: "uuid[]", security: "definer" },
@@ -1221,7 +1225,7 @@ var STORE_REFUSAL_CODES = [
1221
1225
  { code: "22023", raised_by: "_booking_availability,_entity_custom_fields_guard,_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,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" },
1222
1226
  { code: "23503", raised_by: "_booking_availability,_checklist_instantiate,_containment_guard,_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" },
1223
1227
  { 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,work_approval_decide,work_slot_hold" },
1224
- { code: "23514", raised_by: "_checklist_instantiate,_checklist_step_guard,_claimed_column_guard,_containment_guard,_dated_values_guard,_derived_fields,_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,_work_shape_guard,_workdoors_approval_guard,assert_entity_is_organization_scoped,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" },
1228
+ { code: "23514", raised_by: "_checklist_instantiate,_checklist_step_guard,_containment_guard,_dated_values_guard,_derived_fields,_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,_work_shape_guard,_workdoors_approval_guard,assert_columns_are_defined,assert_entity_is_organization_scoped,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" },
1225
1229
  { code: "40001", raised_by: "has_visibility_at" },
1226
1230
  { 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,record_table,share_grant,subscription_mute,subscription_preview,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_person" },
1227
1231
  { code: "42703", raised_by: "entity_table,portal_declare" },
@@ -1793,6 +1797,7 @@ var ENTITY_TOKENS = [
1793
1797
  "mandate_advance_batch_row",
1794
1798
  "mandate_binding",
1795
1799
  "mandate_binding_legacy",
1800
+ "mandate_goal_clauses",
1796
1801
  "mandate_legacy",
1797
1802
  "mandate_observation",
1798
1803
  "mandate_reference",
@@ -2961,7 +2966,8 @@ function createRecordsClient(config) {
2961
2966
  const { document, hidden } = unfold(row.document);
2962
2967
  return { id: row.id, document, level: row.level, hidden };
2963
2968
  });
2964
- return ok({ rows, total: null });
2969
+ const total = rows.length < limit ? offset + rows.length : null;
2970
+ return ok({ rows, total });
2965
2971
  },
2966
2972
  async query(input) {
2967
2973
  const plan = planQuery(input);