@ai-matrx/associations 0.8.11 → 0.9.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,54 @@
1
1
  # Changelog — @ai-matrx/associations
2
2
 
3
+ ## 0.9.0 — BREAKING: the boot probe is deleted
4
+
5
+ `AssociationsProvider` used to run `assertDemandedSchema` on mount. That probe
6
+ established whether each of the 26 demanded RPCs existed **by calling it** with
7
+ sentinel arguments — and **fourteen of them are writes** (`assoc_add`,
8
+ `assoc_set_targets`, `assoc_remove_for_entity`, `conversation_file_add`,
9
+ `agent_resource_add`, `cat_create`/`cat_update`/`cat_reparent`/`cat_delete`,
10
+ `ues_set`, `ues_touch`, `cmt_add`, `cmt_edit`, `cmt_delete`). Measured in the
11
+ AI Matrx frontend on `/administration/billing/spend`: **25 POSTs to
12
+ `/rest/v1/rpc/<name>` answered 400 on every page load**, ahead of the page's own
13
+ reads.
14
+
15
+ It was documented as dev-only. It was not. The gate was
16
+ `process.env.NODE_ENV === "development"`, and tsup builds this package with
17
+ `platform: "browser"`, where esbuild SUBSTITUTES that expression at build time —
18
+ the shipped `dist/react/index.js` read `typeof process !== "undefined" && true`.
19
+ Every consumer fired the storm in production.
20
+
21
+ **THE CLASS RULE: a write RPC is never invoked to ask whether it exists.**
22
+
23
+ Removed, not deprecated (a knob nobody can safely turn on is not a knob):
24
+
25
+ - `assertDemandedSchema`, `SELF_TEST_MISSING_RPC`, `AssertDemandedSchemaOptions`
26
+ and `DemandedSchemaReport` are gone from `@ai-matrx/associations/core`.
27
+ - The `probeSchema` prop is gone from `AssociationsProvider`.
28
+ - Mounting the provider now issues **no network call at all**.
29
+
30
+ Nothing replaces it, because nothing needs to. A database that cannot answer a
31
+ demanded function still says so, loudly, at the first REAL call: PostgREST
32
+ returns PGRST202 and `mapPgError` raises `demanded_schema_violation` with the
33
+ same remedy — at the moment it matters, and at zero cost on the loads where the
34
+ database is right.
35
+
36
+ Guards (both proven failing-then-passing):
37
+ `src/react/__tests__/mount-is-inert.test.tsx` — a recorder sees an RPC when one
38
+ is made (the self-test), and mounting the provider records none.
39
+ `src/__tests__/no-node-env-gating.test.ts` — no source file reads `NODE_ENV` in
40
+ code, because our own build bakes it. `scripts/verify-tarball.mjs` asserts the
41
+ SHIPPED cjs core no longer exports `assertDemandedSchema`.
42
+
43
+ **Consumer action.** Delete any `probeSchema` prop you pass to
44
+ `AssociationsProvider` (it is now a type error) and any `assertDemandedSchema`
45
+ import. If you want a deliberate schema check in CI, assert against `pg_proc`
46
+ from a server-side script — never by invoking the functions.
47
+
48
+ This release also carries 0.8.10 and 0.8.11, whose artifacts never reached the
49
+ registry (the 0.8.10 workflow stopped on a stale workspace lockfile): the
50
+ regenerated `entity-types.generated.ts` vocabulary at **676 tokens**.
51
+
3
52
  ## 0.8.11
4
53
 
5
54
  Republishes the 0.8.10 vocabulary artifact under a fresh immutable version.
package/README.md CHANGED
@@ -26,7 +26,6 @@ import {
26
26
  ```ts
27
27
  import {
28
28
  createAssociationsStore, // the ONE runtime object a host constructs
29
- assertDemandedSchema, // boot probe: every demanded RPC must answer
30
29
  } from "@ai-matrx/associations/core";
31
30
 
32
31
  const store = createAssociationsStore({
@@ -42,10 +41,9 @@ await store.add({ sourceType: "note", sourceId, targetType: "task", targetId });
42
41
  store.titles.prime("task", taskId, "Renamed");
43
42
  await store.favorites.setFavorite("note", noteId, true);
44
43
 
45
- // Dev/CI boot check for any new environment (zero side effects; selfTest
46
- // proves the probe can fail before you trust its green). You normally never
47
- // call this: <AssociationsProvider> runs it for you in a development build.
48
- await assertDemandedSchema(supabase, { selfTest: true });
44
+ // There is NO boot check to call (0.9.0). A database that cannot answer a
45
+ // demanded function says so at the first real call: PostgREST returns
46
+ // PGRST202 and the package screams `demanded_schema_violation` with a remedy.
49
47
  ```
50
48
 
51
49
  `useContainerLinks({ containerType, containerId, orgId })` is the canonical
@@ -91,7 +89,8 @@ adapt to your database — it DEMANDS ours. A consumer who won't run our schema
91
89
  not this package's consumer. It ships no DDL (the platform DB owns that; managed
92
90
  consumers run on our infrastructure); it ships the demand three ways —
93
91
  documented (here), typed (`DemandedRpcSignatures`, generated from the live DB),
94
- and probed (`assertDemandedSchema` in `/core`). Every violation
92
+ and screamed at the first real call (PGRST202 `demanded_schema_violation`,
93
+ with a remedy). Every violation
95
94
  **screams, never degrades**.
96
95
 
97
96
  1. **The edge.** `platform.associations(source_type, source_id, target_type,
@@ -335,7 +335,7 @@ type AssociationsRpcErrorCode = "unauthorized" | "forbidden_org" | "forbidden_ro
335
335
  * on any demanded RPC maps to this code: the database this client is
336
336
  * pointed at does not run the schema this package demands (README front
337
337
  * door). It always reaches the errorSink — one unambiguous scream, never a
338
- * mystery toast. Probe it at boot with `assertDemandedSchema` (/core, W2).
338
+ * mystery toast PGRST202 arrives as `demanded_schema_violation`.
339
339
  */
340
340
  | "demanded_schema_violation" | "internal";
341
341
  interface AssociationsRpcError {
@@ -335,7 +335,7 @@ type AssociationsRpcErrorCode = "unauthorized" | "forbidden_org" | "forbidden_ro
335
335
  * on any demanded RPC maps to this code: the database this client is
336
336
  * pointed at does not run the schema this package demands (README front
337
337
  * door). It always reaches the errorSink — one unambiguous scream, never a
338
- * mystery toast. Probe it at boot with `assertDemandedSchema` (/core, W2).
338
+ * mystery toast PGRST202 arrives as `demanded_schema_violation`.
339
339
  */
340
340
  | "demanded_schema_violation" | "internal";
341
341
  interface AssociationsRpcError {
@@ -23,8 +23,6 @@ __export(core_exports, {
23
23
  DEFAULT_ORG_COLUMN: () => DEFAULT_ORG_COLUMN,
24
24
  DEFAULT_OWNER_COLUMN: () => DEFAULT_OWNER_COLUMN,
25
25
  NON_CONTENT_SOURCE_TYPES: () => NON_CONTENT_SOURCE_TYPES,
26
- SELF_TEST_MISSING_RPC: () => SELF_TEST_MISSING_RPC,
27
- assertDemandedSchema: () => assertDemandedSchema,
28
26
  associationsKey: () => associationsKey,
29
27
  buildCategoryHierarchy: () => buildCategoryHierarchy,
30
28
  createAssociationGuards: () => createAssociationGuards,
@@ -3316,169 +3314,6 @@ function createAssociationsStore(config) {
3316
3314
  };
3317
3315
  }
3318
3316
 
3319
- // src/demanded-rpcs.generated.ts
3320
- var DEMANDED_RPC_NAMES = [
3321
- "assoc_add",
3322
- "assoc_remove",
3323
- "assoc_set_targets",
3324
- "assoc_remove_for_entity",
3325
- "assoc_for_entity",
3326
- "assoc_for_targets",
3327
- "assoc_for_sources",
3328
- "assoc_members_visible",
3329
- "conversation_file_add",
3330
- "conversation_file_remove",
3331
- "conversation_files",
3332
- "agent_resource_add",
3333
- "agent_resource_remove",
3334
- "cat_list",
3335
- "cat_create",
3336
- "cat_update",
3337
- "cat_reparent",
3338
- "cat_delete",
3339
- "ues_set",
3340
- "ues_list",
3341
- "ues_get_bulk",
3342
- "ues_touch",
3343
- "reference_search_candidates",
3344
- "cmt_list",
3345
- "cmt_add",
3346
- "cmt_edit",
3347
- "cmt_delete"
3348
- ];
3349
-
3350
- // src/core/assertDemandedSchema.ts
3351
- var BAD_UUID = "__not_a_uuid__";
3352
- var SELF_TEST_MISSING_RPC = "assoc_probe_self_test_missing_fn";
3353
- var PROBE_ARGS = {
3354
- // edges — writes
3355
- assoc_add: {
3356
- p_source_type: "note",
3357
- p_source_id: BAD_UUID,
3358
- p_target_type: "task",
3359
- p_target_id: BAD_UUID
3360
- },
3361
- assoc_remove: {
3362
- p_source_type: "note",
3363
- p_source_id: BAD_UUID,
3364
- p_target_type: "task",
3365
- p_target_id: BAD_UUID
3366
- },
3367
- assoc_set_targets: {
3368
- p_source_type: "note",
3369
- p_source_id: BAD_UUID,
3370
- p_target_type: "task",
3371
- p_target_ids: [BAD_UUID]
3372
- },
3373
- assoc_remove_for_entity: { p_type: "note", p_id: BAD_UUID },
3374
- // edges — reads
3375
- assoc_for_entity: { p_type: "note", p_id: BAD_UUID },
3376
- assoc_for_targets: { p_target_type: "task", p_target_ids: [BAD_UUID] },
3377
- assoc_for_sources: { p_source_type: "note", p_source_ids: [BAD_UUID] },
3378
- assoc_members_visible: { p_target_type: "task", p_target_ids: [BAD_UUID] },
3379
- // conversation files
3380
- conversation_file_add: {
3381
- p_conversation_id: BAD_UUID,
3382
- p_file_id: BAD_UUID
3383
- },
3384
- conversation_file_remove: {
3385
- p_conversation_id: BAD_UUID,
3386
- p_file_id: BAD_UUID
3387
- },
3388
- conversation_files: { p_conversation_id: BAD_UUID },
3389
- // agent resources
3390
- agent_resource_add: {
3391
- p_agent_id: BAD_UUID,
3392
- p_source_type: "note",
3393
- p_source_id: BAD_UUID
3394
- },
3395
- agent_resource_remove: {
3396
- p_agent_id: BAD_UUID,
3397
- p_source_type: "note",
3398
- p_source_id: BAD_UUID
3399
- },
3400
- // categories
3401
- cat_list: {},
3402
- cat_create: { p_dimension: "", p_name: "", p_org_id: BAD_UUID },
3403
- cat_update: { p_category_id: BAD_UUID, p_name: "__probe__" },
3404
- cat_reparent: { p_category_id: BAD_UUID },
3405
- cat_delete: { p_category_id: BAD_UUID },
3406
- // favorites / recents
3407
- ues_set: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
3408
- ues_list: {},
3409
- ues_get_bulk: { p_entity_type: "__probe__", p_entity_ids: [BAD_UUID] },
3410
- ues_touch: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
3411
- // candidates
3412
- reference_search_candidates: { p_token: "__probe__", p_limit: 1 },
3413
- // comments (W6) — every fn takes a uuid arg, so the unparseable sentinel
3414
- // guarantees 22P02 before any SECURITY DEFINER body runs (no write occurs).
3415
- cmt_list: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
3416
- cmt_add: {
3417
- p_entity_type: "__probe__",
3418
- p_entity_id: BAD_UUID,
3419
- p_body: "__probe__"
3420
- },
3421
- cmt_edit: { p_id: BAD_UUID, p_body: "__probe__" },
3422
- cmt_delete: { p_id: BAD_UUID }
3423
- };
3424
- function isMissingFunctionError(error) {
3425
- if (!error || typeof error !== "object") return false;
3426
- return error.code === "PGRST202";
3427
- }
3428
- async function assertDemandedSchema(dataSource, options = {}) {
3429
- const { selfTest = false, concurrency = 6, throwOnViolation = true } = options;
3430
- const names = [...DEMANDED_RPC_NAMES];
3431
- if (selfTest) names.push(SELF_TEST_MISSING_RPC);
3432
- const missing = [];
3433
- const unreachable = [];
3434
- const answered = [];
3435
- let cursor = 0;
3436
- async function worker() {
3437
- while (cursor < names.length) {
3438
- const fn = names[cursor];
3439
- cursor += 1;
3440
- if (!fn) continue;
3441
- const args = fn === SELF_TEST_MISSING_RPC ? {} : PROBE_ARGS[fn];
3442
- try {
3443
- const { error } = await dataSource.rpc(fn, args);
3444
- if (error == null) answered.push(fn);
3445
- else if (isMissingFunctionError(error)) missing.push(fn);
3446
- else answered.push(fn);
3447
- } catch (e) {
3448
- unreachable.push({ fn, error: e });
3449
- }
3450
- }
3451
- }
3452
- await Promise.all(
3453
- Array.from({ length: Math.min(concurrency, names.length) }, worker)
3454
- );
3455
- if (selfTest) {
3456
- const selfTestMissing = missing.includes(SELF_TEST_MISSING_RPC);
3457
- const idx = missing.indexOf(SELF_TEST_MISSING_RPC);
3458
- if (idx >= 0) missing.splice(idx, 1);
3459
- const answeredIdx = answered.indexOf(SELF_TEST_MISSING_RPC);
3460
- if (answeredIdx >= 0) answered.splice(answeredIdx, 1);
3461
- if (!selfTestMissing) {
3462
- throw new Error(
3463
- `assertDemandedSchema self-test FAILED: the fabricated function "${SELF_TEST_MISSING_RPC}" did not come back as missing (PGRST202). This probe cannot currently fail, so its green result proves nothing \u2014 the dataSource is swallowing or faking errors.`
3464
- );
3465
- }
3466
- }
3467
- if (unreachable.length > 0) {
3468
- throw new Error(
3469
- `assertDemandedSchema could not complete: ${unreachable.length} probe call(s) failed to reach the database (${unreachable.map((u) => u.fn).join(", ")}). This is NOT a schema verdict \u2014 fix connectivity and re-run.`
3470
- );
3471
- }
3472
- const ok2 = missing.length === 0;
3473
- const report = { ok: ok2, missing, unreachable, answered };
3474
- if (!ok2 && throwOnViolation) {
3475
- throw new Error(
3476
- `DEMANDED SCHEMA VIOLATION: the database this dataSource points at cannot answer ${missing.length} demanded @ai-matrx/associations function(s): ${missing.join(", ")}. This package demands the AI Matrx platform schema (README \xA7 The demanded schema); a database without it is not this package's database.`
3477
- );
3478
- }
3479
- return report;
3480
- }
3481
-
3482
3317
  // src/core/edgeClassifier.ts
3483
3318
  var NON_CONTENT_SOURCE_TYPES = /* @__PURE__ */ new Set([
3484
3319
  "thread",