@descryy/core 0.8.0 → 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.
@@ -10,4 +10,6 @@ export { checkContracts } from "./engine.ts";
10
10
  export type { ContractCheck, ContractOptions, ShapeCheck, ShapeDirection, ShapeMismatch, } from "./engine.ts";
11
11
  export { originMatchesRepo, repoOfEndpoint, resolveWorkspaceApiJoins } from "./workspace-join.ts";
12
12
  export type { AmbiguousApiJoin, ResolvedApiJoin, WorkspaceApiJoinOptions, WorkspaceApiJoinResult, } from "./workspace-join.ts";
13
+ export { repoOfTable, resolveWorkspaceTableJoins } from "./workspace-table-join.ts";
14
+ export type { AmbiguousTableJoin, ResolvedTableJoin, TableKey, WorkspaceTableJoinResult, } from "./workspace-table-join.ts";
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7G,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EACV,KAAK,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAClG,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7G,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EACV,KAAK,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAClG,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACpF,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,wBAAwB,GACzB,MAAM,2BAA2B,CAAC"}
@@ -4,4 +4,5 @@ export { findContractOrphans } from "./orphans.js";
4
4
  export { checkObservedRoutes, declaredRoutesFrom } from "./observed-routes.js";
5
5
  export { checkContracts } from "./engine.js";
6
6
  export { originMatchesRepo, repoOfEndpoint, resolveWorkspaceApiJoins } from "./workspace-join.js";
7
+ export { repoOfTable, resolveWorkspaceTableJoins } from "./workspace-table-join.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG7G,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AASnD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS/E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG7G,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AASnD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS/E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAQlG,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Cross-repo `DATABASE_TABLE`/`DATABASE_COLUMN` join — a sibling to
3
+ * `workspace-join.ts`'s frontend-to-backend `API_ENDPOINT` join, not an
4
+ * extension of it. Filed as a separate module rather than folded into that
5
+ * one: see "Why the endpoint design doesn't transfer whole" below for why
6
+ * the two resolution shapes genuinely differ, and every other file in this
7
+ * directory (`paths.ts`, `shapes.ts`, `orphans.ts`, `observed-routes.ts`,
8
+ * `engine.ts`) already owns one mechanism per file — this keeps that.
9
+ *
10
+ * Same repo-scoping ruling drives both:
11
+ * `documents/decisions-inbox/DEC-NEXT-endpoint-identity-repo-scoping.md`
12
+ * folded `repo` into `API_ENDPOINT`, `DATABASE_TABLE` and `DATABASE_COLUMN`
13
+ * ids unconditionally (`packages/ir/src/identity.ts`'s `NODE_SCOPE`). That
14
+ * flip is correct and stays — two backend services exposing the identical
15
+ * path, or two services each honestly owning their own `orders` table,
16
+ * must not silently collapse onto one node. But it also means two services
17
+ * that genuinely share one physical table now mint two unconnected nodes,
18
+ * with nothing downstream to re-join them. This module is that downstream
19
+ * join, run once per workspace build
20
+ * (`packages/mcp/src/workspace-index.ts`), the same composition point the
21
+ * `API_ENDPOINT` join already uses.
22
+ *
23
+ * ## Why the endpoint design doesn't transfer whole
24
+ *
25
+ * The endpoint join is tractable because of a real asymmetry: a frontend
26
+ * mints a *placeholder* `API_ENDPOINT` (an unconfirmed reference to
27
+ * something it calls) and a real backend mints the *served* one
28
+ * (`SERVES_API`-reachable from an `API_ROUTE`) — there is an obvious
29
+ * canonical target the moment exactly one backend serves a path.
30
+ *
31
+ * Two `DATABASE_TABLE` nodes from two repos have no such built-in
32
+ * asymmetry by default: both can be minted the exact same way, through the
33
+ * exact same shared function
34
+ * (`packages/ir/src/model-tables.ts`'s `databaseTableNodesFromModel`, used
35
+ * by every ORM/model reader). Table *name* alone is not evidence of
36
+ * physical identity — two unrelated services can each honestly own a
37
+ * `users` table in their own separate database, and joining on name alone
38
+ * would be exactly the wrong edge rule 2 warns about: it would tell a user
39
+ * two unrelated services share a table when they do not.
40
+ *
41
+ * ## What evidence the graph actually carries (checked against
42
+ * ## `descry-adapters`, not assumed)
43
+ *
44
+ * - Every `DATABASE_TABLE`'s `attrs` is `{ name, schema }`, optionally
45
+ * `missingFromLiveSchema`; every `DATABASE_COLUMN`'s is
46
+ * `{ name, nullable, dataType? }`, optionally `liveObserved`/
47
+ * `liveDivergence` (`adapter-sql/src/adapter.ts`) or nothing beyond
48
+ * `{ name, nullable }` (`packages/ir/src/model-tables.ts`, the shared
49
+ * ORM-model path). **Neither ever carries a connection string, database
50
+ * name or host.** `adapter-sql`'s own `PgConnectionConfig`
51
+ * (`adapter-sql/src/live-schema.ts`) does carry a real `database`/`host`
52
+ * — but it is a *run configuration* passed into `createSqlAdapter`,
53
+ * never propagated onto the node it produces. So there is no per-node
54
+ * connection/database identity to join on today — a fact established by
55
+ * reading every minting site, not assumed from the module doing the
56
+ * minting.
57
+ * - `producedBy` does distinguish two real kinds of table node, though.
58
+ * `adapter-sql` (`ADAPTER_ID = "adapter-sql"`) is the one DDL/migration
59
+ * reader in the corpus — its own module header calls it out: "reaches R3
60
+ * with no language adapter at all, because a schema is *declared*, not
61
+ * inferred." A table it mints (`producedBy` starting with
62
+ * `"adapter-sql@"`) was read from an actual `CREATE TABLE`/migration —
63
+ * a **declaration**. A table minted by any other adapter went through
64
+ * `databaseTableNodesFromModel`, called from an ORM/model reader — a
65
+ * **reference** to a table the reading repo does not itself declare (it
66
+ * maps to it, nothing more). `producedBy` is IR-level provenance every
67
+ * batch and node already carries (`IRBatch.producedBy`,
68
+ * `IRNode.producedBy`, both typed in `packages/ir/src/ir.ts`) — reading
69
+ * it is not a language check above the boundary (rule 1); it is reading
70
+ * the one field the IR contract puts there for precisely this question.
71
+ * Cross-checked against the conformance corpus too: golden pattern
72
+ * `10-migration-adding-column` (`fixtures/golden/patterns.json`) is
73
+ * explicit — "Produced by the SQL adapter, not the language adapter" —
74
+ * R0, no edges, confirming this is the corpus's own stated distinction,
75
+ * not one invented here.
76
+ *
77
+ * ## Design question 1 — what justifies joining two table nodes at all
78
+ *
79
+ * A declaration is real, disclosed evidence that a table with this exact
80
+ * (schema, name) exists somewhere in the workspace; a reference is only a
81
+ * claim that some repo talks to a table by that name. **Exactly one
82
+ * declaring node for a (schema, name) key, anywhere in the workspace, is
83
+ * the evidence this join accepts** — every other repo's *referencing* node
84
+ * for the same key is the same physical table by construction, because a
85
+ * reference names what it reads/writes and a workspace-wide unique
86
+ * declaration is the only thing it can be naming. Name alone, backed by
87
+ * zero or by more than one declaring node, is **not** accepted — that is
88
+ * exactly the "same name, different database" risk this module exists to
89
+ * avoid, and the honest outcome there is a disclosed ambiguity, never a
90
+ * guess (see the two ambiguous cases below).
91
+ *
92
+ * ## Design question 2 — is there a canonical side
93
+ *
94
+ * Yes — the declaring node (a real migration, read by `adapter-sql`) is
95
+ * canonical, the same role a backend's `SERVES_API`-reached node plays for
96
+ * endpoints. Every referencing repo's edges rewrite onto it.
97
+ *
98
+ * Unlike the endpoint join, **there is no caller-hint tier here.**
99
+ * `workspace-join.ts`'s `originMatchesRepo` works because a caller reports
100
+ * its own base URL (`USES_API.attrs.origin`) and a repo has a name to match
101
+ * it against. Tables have no table-side counterpart in the graph today — no
102
+ * connection or database identity is recorded anywhere (see above) — so
103
+ * building a hint matcher would mean inventing a signal, not reading one.
104
+ * That is a disclosed gap, not an oversight: if a future adapter starts
105
+ * recording a connection/database identity on these nodes, a hint tier can
106
+ * be added the same way `matchHint` is already overridable on the endpoint
107
+ * join. Until then, more than one declaring node for the same key is
108
+ * reported ambiguous, full stop — never picked by, say, repo-name
109
+ * similarity, which would be exactly the "confident wrong merge" rule 2
110
+ * forbids.
111
+ *
112
+ * ## Design question 3 — columns follow tables, never resolved alone
113
+ *
114
+ * Once a referencing table is joined onto a canonical one, its own
115
+ * `DATABASE_COLUMN` nodes are found by scanning the *same repo's* columns
116
+ * whose `name` is `"<table>.<column>"` (the shape both minting sites use —
117
+ * `adapter-sql/src/adapter.ts` and `model-tables.ts` alike). For each, the
118
+ * canonical table's column of the same bare name is computed directly —
119
+ * `nodeId({ repo: canonicalRepo }, "DATABASE_COLUMN", tableQsp(schema,
120
+ * table, bareName), null)` — never searched for, since a column's id is
121
+ * fully determined by (scope, schema, table, column). A referencing column
122
+ * with no same-named canonical counterpart is left exactly as its adapter
123
+ * minted it: rule 2's "when in doubt, omit" — never invented, never joined
124
+ * to a near-miss by similarity.
125
+ *
126
+ * ## What this never does
127
+ *
128
+ * No new node or edge type — the frozen 15/15 stays frozen. No node is
129
+ * ever deleted: an unresolved or ambiguous table/column stays exactly as
130
+ * its adapter minted it. A resolved rewrite touches `edge.from` **and**
131
+ * `edge.to`, unlike the endpoint join's `to`-only rewrite — a `DATABASE_
132
+ * TABLE`/`DATABASE_COLUMN` has no analogue to `SERVES_API` (a declaring
133
+ * edge that's already correct and must be left alone); `adapter-sql` emits
134
+ * *no* edges at all (`extractEdges(): return []`), so there is nothing to
135
+ * protect from being rewritten by accident.
136
+ *
137
+ * That said: **as of this writing, no adapter in `descry-adapters` emits
138
+ * any edge naming a `DATABASE_TABLE` or `DATABASE_COLUMN` at all** —
139
+ * checked across every adapter's source, not assumed, and confirmed by the
140
+ * conformance corpus itself (golden pattern `10-migration-adding-column`
141
+ * is `"edges": []`). So `rewrittenEdges`/`columnsJoined` read `0`/`0` on
142
+ * every real graph this runs against today — a disclosed, honest fact, not
143
+ * a bug in this module. What this resolves is *node* identity, so that
144
+ * whichever edge type a future producer chooses to connect a `FUNCTION` or
145
+ * `MODEL` to a table (`READS`/`ASSIGNS` are the two the frozen vocabulary
146
+ * offers for this — there is no `WRITES` edge type in
147
+ * `packages/ir/src/vocabulary.ts`) needs no second migration to join
148
+ * correctly the day it ships.
149
+ */
150
+ import { type IRBatch, type IREdge, type IRNode } from "@descryy/ir";
151
+ export interface TableKey {
152
+ readonly schema: string;
153
+ readonly table: string;
154
+ }
155
+ export interface ResolvedTableJoin {
156
+ readonly referencingTableId: string;
157
+ readonly canonicalTableId: string;
158
+ readonly key: TableKey;
159
+ /** The only tier this join has — see this module's header for why there is
160
+ * no caller-hint tier the way `workspace-join.ts` has one. */
161
+ readonly via: "declaring-node";
162
+ /** How many edges (naming either the table or one of its joined columns,
163
+ * on either `from` or `to`) were rewritten from the referencing node onto
164
+ * the canonical one. Reads `0` on every graph today — see the module
165
+ * header's "What this never does". */
166
+ readonly rewrittenEdges: number;
167
+ /** Referencing columns that found a same-named canonical column and were rewritten. */
168
+ readonly columnsJoined: number;
169
+ /** Referencing columns with no same-named canonical counterpart — left untouched, never guessed. */
170
+ readonly columnsUnjoined: number;
171
+ }
172
+ export interface AmbiguousTableJoin {
173
+ readonly key: TableKey;
174
+ readonly candidateTableIds: readonly string[];
175
+ /** The repo each candidate was minted by, `"unknown"` when `repoOf` had no entry for it. */
176
+ readonly candidateRepos: readonly string[];
177
+ /** How many of `candidateTableIds` were a declaration (`adapter-sql`), not a reference. */
178
+ readonly declaringCount: number;
179
+ readonly detail: string;
180
+ }
181
+ export interface WorkspaceTableJoinResult {
182
+ /** The input edges, with every resolved `DATABASE_TABLE`/`DATABASE_COLUMN`
183
+ * reference rewritten (on both `from` and `to`) onto its canonical node.
184
+ * Every other edge is returned unchanged. */
185
+ readonly edges: readonly IREdge[];
186
+ readonly resolved: readonly ResolvedTableJoin[];
187
+ readonly ambiguous: readonly AmbiguousTableJoin[];
188
+ /** `DATABASE_TABLE` nodes missing `attrs.name`/`attrs.schema` — defensive; every
189
+ * real producer sets both (`model-tables.ts`, `adapter-sql/src/adapter.ts`). */
190
+ readonly unkeyed: number;
191
+ /** (schema, name) keys with exactly one table node in the whole workspace —
192
+ * the ordinary case (most tables exist in one repo only). Counted for
193
+ * visibility, never reported as an ambiguity. */
194
+ readonly singletons: number;
195
+ }
196
+ /** Which repo minted each `DATABASE_TABLE`/`DATABASE_COLUMN` node — read
197
+ * directly off each batch's own (pre-merge) nodes, mirroring
198
+ * `workspace-join.ts`'s `repoOfEndpoint`. */
199
+ export declare function repoOfTable(batches: readonly IRBatch[]): ReadonlyMap<string, string>;
200
+ /** Resolve every cross-repo `DATABASE_TABLE`/`DATABASE_COLUMN` reference in a
201
+ * merged workspace graph against its declaring node, when exactly one
202
+ * exists. Pure: no store, no IO, same posture as `resolveWorkspaceApiJoins`. */
203
+ export declare function resolveWorkspaceTableJoins(nodes: readonly IRNode[], edges: readonly IREdge[], repoOf: ReadonlyMap<string, string>): WorkspaceTableJoinResult;
204
+ //# sourceMappingURL=workspace-table-join.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-table-join.d.ts","sourceRoot":"","sources":["../../src/contracts/workspace-table-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoJG;AAEH,OAAO,EAAoB,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAMvF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAqBD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB;mEAC+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAC/B;;;2CAGuC;IACvC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,uFAAuF;IACvF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,oGAAoG;IACpG,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,4FAA4F;IAC5F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,2FAA2F;IAC3F,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC;;kDAE8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAClD;qFACiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;sDAEkD;IAClD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;8CAE8C;AAC9C,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAQpF;AA8CD;;iFAEiF;AACjF,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,wBAAwB,CAmH1B"}
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Cross-repo `DATABASE_TABLE`/`DATABASE_COLUMN` join — a sibling to
3
+ * `workspace-join.ts`'s frontend-to-backend `API_ENDPOINT` join, not an
4
+ * extension of it. Filed as a separate module rather than folded into that
5
+ * one: see "Why the endpoint design doesn't transfer whole" below for why
6
+ * the two resolution shapes genuinely differ, and every other file in this
7
+ * directory (`paths.ts`, `shapes.ts`, `orphans.ts`, `observed-routes.ts`,
8
+ * `engine.ts`) already owns one mechanism per file — this keeps that.
9
+ *
10
+ * Same repo-scoping ruling drives both:
11
+ * `documents/decisions-inbox/DEC-NEXT-endpoint-identity-repo-scoping.md`
12
+ * folded `repo` into `API_ENDPOINT`, `DATABASE_TABLE` and `DATABASE_COLUMN`
13
+ * ids unconditionally (`packages/ir/src/identity.ts`'s `NODE_SCOPE`). That
14
+ * flip is correct and stays — two backend services exposing the identical
15
+ * path, or two services each honestly owning their own `orders` table,
16
+ * must not silently collapse onto one node. But it also means two services
17
+ * that genuinely share one physical table now mint two unconnected nodes,
18
+ * with nothing downstream to re-join them. This module is that downstream
19
+ * join, run once per workspace build
20
+ * (`packages/mcp/src/workspace-index.ts`), the same composition point the
21
+ * `API_ENDPOINT` join already uses.
22
+ *
23
+ * ## Why the endpoint design doesn't transfer whole
24
+ *
25
+ * The endpoint join is tractable because of a real asymmetry: a frontend
26
+ * mints a *placeholder* `API_ENDPOINT` (an unconfirmed reference to
27
+ * something it calls) and a real backend mints the *served* one
28
+ * (`SERVES_API`-reachable from an `API_ROUTE`) — there is an obvious
29
+ * canonical target the moment exactly one backend serves a path.
30
+ *
31
+ * Two `DATABASE_TABLE` nodes from two repos have no such built-in
32
+ * asymmetry by default: both can be minted the exact same way, through the
33
+ * exact same shared function
34
+ * (`packages/ir/src/model-tables.ts`'s `databaseTableNodesFromModel`, used
35
+ * by every ORM/model reader). Table *name* alone is not evidence of
36
+ * physical identity — two unrelated services can each honestly own a
37
+ * `users` table in their own separate database, and joining on name alone
38
+ * would be exactly the wrong edge rule 2 warns about: it would tell a user
39
+ * two unrelated services share a table when they do not.
40
+ *
41
+ * ## What evidence the graph actually carries (checked against
42
+ * ## `descry-adapters`, not assumed)
43
+ *
44
+ * - Every `DATABASE_TABLE`'s `attrs` is `{ name, schema }`, optionally
45
+ * `missingFromLiveSchema`; every `DATABASE_COLUMN`'s is
46
+ * `{ name, nullable, dataType? }`, optionally `liveObserved`/
47
+ * `liveDivergence` (`adapter-sql/src/adapter.ts`) or nothing beyond
48
+ * `{ name, nullable }` (`packages/ir/src/model-tables.ts`, the shared
49
+ * ORM-model path). **Neither ever carries a connection string, database
50
+ * name or host.** `adapter-sql`'s own `PgConnectionConfig`
51
+ * (`adapter-sql/src/live-schema.ts`) does carry a real `database`/`host`
52
+ * — but it is a *run configuration* passed into `createSqlAdapter`,
53
+ * never propagated onto the node it produces. So there is no per-node
54
+ * connection/database identity to join on today — a fact established by
55
+ * reading every minting site, not assumed from the module doing the
56
+ * minting.
57
+ * - `producedBy` does distinguish two real kinds of table node, though.
58
+ * `adapter-sql` (`ADAPTER_ID = "adapter-sql"`) is the one DDL/migration
59
+ * reader in the corpus — its own module header calls it out: "reaches R3
60
+ * with no language adapter at all, because a schema is *declared*, not
61
+ * inferred." A table it mints (`producedBy` starting with
62
+ * `"adapter-sql@"`) was read from an actual `CREATE TABLE`/migration —
63
+ * a **declaration**. A table minted by any other adapter went through
64
+ * `databaseTableNodesFromModel`, called from an ORM/model reader — a
65
+ * **reference** to a table the reading repo does not itself declare (it
66
+ * maps to it, nothing more). `producedBy` is IR-level provenance every
67
+ * batch and node already carries (`IRBatch.producedBy`,
68
+ * `IRNode.producedBy`, both typed in `packages/ir/src/ir.ts`) — reading
69
+ * it is not a language check above the boundary (rule 1); it is reading
70
+ * the one field the IR contract puts there for precisely this question.
71
+ * Cross-checked against the conformance corpus too: golden pattern
72
+ * `10-migration-adding-column` (`fixtures/golden/patterns.json`) is
73
+ * explicit — "Produced by the SQL adapter, not the language adapter" —
74
+ * R0, no edges, confirming this is the corpus's own stated distinction,
75
+ * not one invented here.
76
+ *
77
+ * ## Design question 1 — what justifies joining two table nodes at all
78
+ *
79
+ * A declaration is real, disclosed evidence that a table with this exact
80
+ * (schema, name) exists somewhere in the workspace; a reference is only a
81
+ * claim that some repo talks to a table by that name. **Exactly one
82
+ * declaring node for a (schema, name) key, anywhere in the workspace, is
83
+ * the evidence this join accepts** — every other repo's *referencing* node
84
+ * for the same key is the same physical table by construction, because a
85
+ * reference names what it reads/writes and a workspace-wide unique
86
+ * declaration is the only thing it can be naming. Name alone, backed by
87
+ * zero or by more than one declaring node, is **not** accepted — that is
88
+ * exactly the "same name, different database" risk this module exists to
89
+ * avoid, and the honest outcome there is a disclosed ambiguity, never a
90
+ * guess (see the two ambiguous cases below).
91
+ *
92
+ * ## Design question 2 — is there a canonical side
93
+ *
94
+ * Yes — the declaring node (a real migration, read by `adapter-sql`) is
95
+ * canonical, the same role a backend's `SERVES_API`-reached node plays for
96
+ * endpoints. Every referencing repo's edges rewrite onto it.
97
+ *
98
+ * Unlike the endpoint join, **there is no caller-hint tier here.**
99
+ * `workspace-join.ts`'s `originMatchesRepo` works because a caller reports
100
+ * its own base URL (`USES_API.attrs.origin`) and a repo has a name to match
101
+ * it against. Tables have no table-side counterpart in the graph today — no
102
+ * connection or database identity is recorded anywhere (see above) — so
103
+ * building a hint matcher would mean inventing a signal, not reading one.
104
+ * That is a disclosed gap, not an oversight: if a future adapter starts
105
+ * recording a connection/database identity on these nodes, a hint tier can
106
+ * be added the same way `matchHint` is already overridable on the endpoint
107
+ * join. Until then, more than one declaring node for the same key is
108
+ * reported ambiguous, full stop — never picked by, say, repo-name
109
+ * similarity, which would be exactly the "confident wrong merge" rule 2
110
+ * forbids.
111
+ *
112
+ * ## Design question 3 — columns follow tables, never resolved alone
113
+ *
114
+ * Once a referencing table is joined onto a canonical one, its own
115
+ * `DATABASE_COLUMN` nodes are found by scanning the *same repo's* columns
116
+ * whose `name` is `"<table>.<column>"` (the shape both minting sites use —
117
+ * `adapter-sql/src/adapter.ts` and `model-tables.ts` alike). For each, the
118
+ * canonical table's column of the same bare name is computed directly —
119
+ * `nodeId({ repo: canonicalRepo }, "DATABASE_COLUMN", tableQsp(schema,
120
+ * table, bareName), null)` — never searched for, since a column's id is
121
+ * fully determined by (scope, schema, table, column). A referencing column
122
+ * with no same-named canonical counterpart is left exactly as its adapter
123
+ * minted it: rule 2's "when in doubt, omit" — never invented, never joined
124
+ * to a near-miss by similarity.
125
+ *
126
+ * ## What this never does
127
+ *
128
+ * No new node or edge type — the frozen 15/15 stays frozen. No node is
129
+ * ever deleted: an unresolved or ambiguous table/column stays exactly as
130
+ * its adapter minted it. A resolved rewrite touches `edge.from` **and**
131
+ * `edge.to`, unlike the endpoint join's `to`-only rewrite — a `DATABASE_
132
+ * TABLE`/`DATABASE_COLUMN` has no analogue to `SERVES_API` (a declaring
133
+ * edge that's already correct and must be left alone); `adapter-sql` emits
134
+ * *no* edges at all (`extractEdges(): return []`), so there is nothing to
135
+ * protect from being rewritten by accident.
136
+ *
137
+ * That said: **as of this writing, no adapter in `descry-adapters` emits
138
+ * any edge naming a `DATABASE_TABLE` or `DATABASE_COLUMN` at all** —
139
+ * checked across every adapter's source, not assumed, and confirmed by the
140
+ * conformance corpus itself (golden pattern `10-migration-adding-column`
141
+ * is `"edges": []`). So `rewrittenEdges`/`columnsJoined` read `0`/`0` on
142
+ * every real graph this runs against today — a disclosed, honest fact, not
143
+ * a bug in this module. What this resolves is *node* identity, so that
144
+ * whichever edge type a future producer chooses to connect a `FUNCTION` or
145
+ * `MODEL` to a table (`READS`/`ASSIGNS` are the two the frozen vocabulary
146
+ * offers for this — there is no `WRITES` edge type in
147
+ * `packages/ir/src/vocabulary.ts`) needs no second migration to join
148
+ * correctly the day it ships.
149
+ */
150
+ import { nodeId, tableQsp } from "@descryy/ir";
151
+ /** Matches `adapter-sql`'s own `ADAPTER_ID` (`packages/adapter-sql/src/adapter.ts` in
152
+ * `descry-adapters`) — the one DDL/migration reader in the corpus today. */
153
+ const SQL_ADAPTER_ID = "adapter-sql";
154
+ function keyOf(table) {
155
+ const name = table.attrs["name"];
156
+ const schema = table.attrs["schema"];
157
+ return typeof name === "string" && typeof schema === "string" ? { schema, table: name } : null;
158
+ }
159
+ function keyString(key) {
160
+ return `${key.schema}.${key.table}`;
161
+ }
162
+ /** `producedBy` is `${adapterId}@${version}` (IR contract) — this reads the
163
+ * id before the `@`, not a substring match, so a hypothetical
164
+ * `adapter-nosql` is never mistaken for `adapter-sql`. */
165
+ function isDeclared(node) {
166
+ const at = node.producedBy.indexOf("@");
167
+ const adapterId = at === -1 ? node.producedBy : node.producedBy.slice(0, at);
168
+ return adapterId === SQL_ADAPTER_ID;
169
+ }
170
+ /** Which repo minted each `DATABASE_TABLE`/`DATABASE_COLUMN` node — read
171
+ * directly off each batch's own (pre-merge) nodes, mirroring
172
+ * `workspace-join.ts`'s `repoOfEndpoint`. */
173
+ export function repoOfTable(batches) {
174
+ const map = new Map();
175
+ for (const batch of batches) {
176
+ for (const node of batch.nodes) {
177
+ if (node.type === "DATABASE_TABLE" || node.type === "DATABASE_COLUMN")
178
+ map.set(node.id, batch.repo);
179
+ }
180
+ }
181
+ return map;
182
+ }
183
+ /** A referencing table's own columns (found by repo + `"<table>."` name
184
+ * prefix, the shape both minting sites use), each joined onto the
185
+ * canonical table's same-named column when one exists — never independently,
186
+ * never invented (module header, design question 3). */
187
+ function joinColumns(canonicalRepo, key,
188
+ // Pre-filtered by the caller to the referencing table's own repo — a
189
+ // column from a different repo must never be matched here, even if it
190
+ // happens to share a name prefix.
191
+ referencingRepoColumns, nodeIds, rewrite) {
192
+ const prefix = `${key.table}.`;
193
+ const columnIds = [];
194
+ let joined = 0;
195
+ let unjoined = 0;
196
+ for (const column of referencingRepoColumns) {
197
+ if (!column.name.startsWith(prefix))
198
+ continue;
199
+ columnIds.push(column.id);
200
+ const bareName = column.attrs["name"];
201
+ if (typeof bareName !== "string") {
202
+ unjoined += 1;
203
+ continue;
204
+ }
205
+ const canonicalColumnId = nodeId({ repo: canonicalRepo }, "DATABASE_COLUMN", tableQsp(key.schema, key.table, bareName), null);
206
+ if (!nodeIds.has(canonicalColumnId) || canonicalColumnId === column.id) {
207
+ unjoined += 1;
208
+ continue;
209
+ }
210
+ rewrite.set(column.id, canonicalColumnId);
211
+ joined += 1;
212
+ }
213
+ return { joined, unjoined, columnIds };
214
+ }
215
+ /** Resolve every cross-repo `DATABASE_TABLE`/`DATABASE_COLUMN` reference in a
216
+ * merged workspace graph against its declaring node, when exactly one
217
+ * exists. Pure: no store, no IO, same posture as `resolveWorkspaceApiJoins`. */
218
+ export function resolveWorkspaceTableJoins(nodes, edges, repoOf) {
219
+ const nodeIds = new Set(nodes.map((n) => n.id));
220
+ const tables = nodes.filter((n) => n.type === "DATABASE_TABLE");
221
+ const columns = nodes.filter((n) => n.type === "DATABASE_COLUMN");
222
+ const byKey = new Map();
223
+ let unkeyed = 0;
224
+ for (const table of tables) {
225
+ const key = keyOf(table);
226
+ if (key === null) {
227
+ unkeyed += 1;
228
+ continue;
229
+ }
230
+ const list = byKey.get(keyString(key));
231
+ if (list === undefined)
232
+ byKey.set(keyString(key), [table]);
233
+ else
234
+ list.push(table);
235
+ }
236
+ const drafts = [];
237
+ const ambiguous = [];
238
+ let singletons = 0;
239
+ const rewrite = new Map();
240
+ for (const group of byKey.values()) {
241
+ if (group.length < 2) {
242
+ singletons += 1;
243
+ continue;
244
+ }
245
+ const key = keyOf(group[0]);
246
+ const declaring = group.filter(isDeclared);
247
+ const referencing = group.filter((n) => !isDeclared(n));
248
+ if (declaring.length === 1) {
249
+ const canonical = declaring[0];
250
+ const canonicalRepo = repoOf.get(canonical.id);
251
+ if (canonicalRepo === undefined) {
252
+ // No standing to resolve against — disclosed as ambiguous rather
253
+ // than silently skipped, since real referencing nodes exist for this key.
254
+ ambiguous.push({
255
+ key,
256
+ candidateTableIds: group.map((n) => n.id),
257
+ candidateRepos: group.map((n) => repoOf.get(n.id) ?? "unknown"),
258
+ declaringCount: declaring.length,
259
+ detail: `${keyString(key)} has a declaring node but its repo could not be determined — reported as ` +
260
+ "ambiguous rather than joining against an unattributed node.",
261
+ });
262
+ continue;
263
+ }
264
+ for (const ref of referencing) {
265
+ const refRepo = repoOf.get(ref.id);
266
+ if (refRepo === undefined)
267
+ continue; // nothing to attribute this rewrite to
268
+ rewrite.set(ref.id, canonical.id);
269
+ const refColumns = columns.filter((c) => repoOf.get(c.id) === refRepo);
270
+ const { joined, unjoined, columnIds } = joinColumns(canonicalRepo, key, refColumns, nodeIds, rewrite);
271
+ drafts.push({
272
+ referencingTableId: ref.id,
273
+ canonicalTableId: canonical.id,
274
+ key,
275
+ columnIds,
276
+ columnsJoined: joined,
277
+ columnsUnjoined: unjoined,
278
+ });
279
+ }
280
+ continue;
281
+ }
282
+ ambiguous.push({
283
+ key,
284
+ candidateTableIds: group.map((n) => n.id),
285
+ candidateRepos: group.map((n) => repoOf.get(n.id) ?? "unknown"),
286
+ declaringCount: declaring.length,
287
+ detail: declaring.length === 0
288
+ ? `${group.length} repositories in this workspace each reference a table named ${keyString(key)}, ` +
289
+ "and none of them declares it (no migration this workspace's SQL adapter parsed) — reported as " +
290
+ "ambiguous rather than assuming they are the same physical table."
291
+ : `${declaring.length} repositories in this workspace each declare a table named ${keyString(key)} ` +
292
+ "independently — reported as ambiguous rather than assuming they are the same physical table.",
293
+ });
294
+ }
295
+ const rewrittenEdges = edges.map((edge) => {
296
+ const to = rewrite.get(edge.to);
297
+ const from = rewrite.get(edge.from);
298
+ if (to === undefined && from === undefined)
299
+ return edge;
300
+ return { ...edge, ...(to === undefined ? {} : { to }), ...(from === undefined ? {} : { from }) };
301
+ });
302
+ const resolved = drafts.map((draft) => ({
303
+ referencingTableId: draft.referencingTableId,
304
+ canonicalTableId: draft.canonicalTableId,
305
+ key: draft.key,
306
+ via: "declaring-node",
307
+ rewrittenEdges: edges.filter((e) => e.to === draft.referencingTableId ||
308
+ e.from === draft.referencingTableId ||
309
+ draft.columnIds.includes(e.to) ||
310
+ draft.columnIds.includes(e.from)).length,
311
+ columnsJoined: draft.columnsJoined,
312
+ columnsUnjoined: draft.columnsUnjoined,
313
+ }));
314
+ return { edges: rewrittenEdges, resolved, ambiguous, unkeyed, singletons };
315
+ }
316
+ //# sourceMappingURL=workspace-table-join.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-table-join.js","sourceRoot":"","sources":["../../src/contracts/workspace-table-join.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descryy/core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "Descry graph engine: storage, graph builder, query layer, traversal, diff scoping, git source, MCP server.",
6
6
  "license": "MIT",