@sdd-method/sdd-cli 0.122.2 → 1.0.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/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/catalogue/aggregate.d.ts +26 -2
- package/dist/lib/catalogue/aggregate.d.ts.map +1 -1
- package/dist/lib/catalogue/aggregate.js +220 -12
- package/dist/lib/catalogue/aggregate.js.map +1 -1
- package/dist/lib/catalogue/build.d.ts.map +1 -1
- package/dist/lib/catalogue/build.js +24 -5
- package/dist/lib/catalogue/build.js.map +1 -1
- package/dist/lib/catalogue/builders/c4-external-systems.d.ts.map +1 -1
- package/dist/lib/catalogue/builders/c4-external-systems.js +27 -19
- package/dist/lib/catalogue/builders/c4-external-systems.js.map +1 -1
- package/dist/lib/catalogue/builders/contracts.d.ts.map +1 -1
- package/dist/lib/catalogue/builders/contracts.js +12 -4
- package/dist/lib/catalogue/builders/contracts.js.map +1 -1
- package/dist/lib/catalogue/builders/external-system-exemption.d.ts +10 -0
- package/dist/lib/catalogue/builders/external-system-exemption.d.ts.map +1 -1
- package/dist/lib/catalogue/builders/external-system-exemption.js +19 -3
- package/dist/lib/catalogue/builders/external-system-exemption.js.map +1 -1
- package/dist/lib/catalogue/builders/products.d.ts.map +1 -1
- package/dist/lib/catalogue/builders/products.js +0 -6
- package/dist/lib/catalogue/builders/products.js.map +1 -1
- package/dist/lib/catalogue/builders/service-contract-consumes.d.ts +12 -0
- package/dist/lib/catalogue/builders/service-contract-consumes.d.ts.map +1 -1
- package/dist/lib/catalogue/builders/service-contract-consumes.js +14 -1
- package/dist/lib/catalogue/builders/service-contract-consumes.js.map +1 -1
- package/dist/lib/catalogue/canonical-schema.yaml +78 -17
- package/dist/lib/catalogue/contract-id.d.ts +11 -0
- package/dist/lib/catalogue/contract-id.d.ts.map +1 -0
- package/dist/lib/catalogue/contract-id.js +49 -0
- package/dist/lib/catalogue/contract-id.js.map +1 -0
- package/dist/lib/catalogue/enum-validator.d.ts +16 -0
- package/dist/lib/catalogue/enum-validator.d.ts.map +1 -0
- package/dist/lib/catalogue/enum-validator.js +46 -0
- package/dist/lib/catalogue/enum-validator.js.map +1 -0
- package/dist/lib/catalogue/ext-id.d.ts +25 -0
- package/dist/lib/catalogue/ext-id.d.ts.map +1 -0
- package/dist/lib/catalogue/ext-id.js +76 -0
- package/dist/lib/catalogue/ext-id.js.map +1 -0
- package/dist/lib/catalogue/index.d.ts +1 -0
- package/dist/lib/catalogue/index.d.ts.map +1 -1
- package/dist/lib/catalogue/index.js +1 -0
- package/dist/lib/catalogue/index.js.map +1 -1
- package/dist/lib/catalogue/migration-map.d.ts +27 -0
- package/dist/lib/catalogue/migration-map.d.ts.map +1 -0
- package/dist/lib/catalogue/migration-map.js +114 -0
- package/dist/lib/catalogue/migration-map.js.map +1 -0
- package/dist/lib/catalogue/reverse-product-quality.js +3 -3
- package/dist/lib/catalogue/reverse-product-quality.js.map +1 -1
- package/dist/lib/catalogue/schema-compat.d.ts +1 -1
- package/dist/lib/catalogue/schema-compat.js +1 -1
- package/dist/lib/catalogue/validate.d.ts +2 -0
- package/dist/lib/catalogue/validate.d.ts.map +1 -1
- package/dist/lib/catalogue/validate.js +22 -5
- package/dist/lib/catalogue/validate.js.map +1 -1
- package/dist/lib/contracts-availability/index.d.ts +44 -0
- package/dist/lib/contracts-availability/index.d.ts.map +1 -0
- package/dist/lib/contracts-availability/index.js +267 -0
- package/dist/lib/contracts-availability/index.js.map +1 -0
- package/dist/verbs/catalogue.d.ts.map +1 -1
- package/dist/verbs/catalogue.js +12 -1
- package/dist/verbs/catalogue.js.map +1 -1
- package/dist/verbs/contracts.d.ts +3 -0
- package/dist/verbs/contracts.d.ts.map +1 -0
- package/dist/verbs/contracts.js +33 -0
- package/dist/verbs/contracts.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAsBA,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAahE"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { getPackageVersion } from "./lib/package-version.js";
|
|
|
3
3
|
import { registerApiSurface } from "./verbs/api-surface.js";
|
|
4
4
|
import { registerCatalogue } from "./verbs/catalogue.js";
|
|
5
5
|
import { registerConformance } from "./verbs/conformance.js";
|
|
6
|
+
import { registerContracts } from "./verbs/contracts.js";
|
|
6
7
|
import { registerGenerateBriefing } from "./verbs/generate-briefing.js";
|
|
7
8
|
import { registerInit } from "./verbs/init.js";
|
|
8
9
|
import { registerList } from "./verbs/list.js";
|
|
@@ -51,6 +52,7 @@ function buildProgram() {
|
|
|
51
52
|
registerMcp(program);
|
|
52
53
|
registerCatalogue(program);
|
|
53
54
|
registerConformance(program);
|
|
55
|
+
registerContracts(program);
|
|
54
56
|
registerApiSurface(program);
|
|
55
57
|
registerPack(program);
|
|
56
58
|
registerRender(program);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CatalogueDef } from "./schema-loader.js";
|
|
1
|
+
import { type CanonicalSchema, type CatalogueDef } from "./schema-loader.js";
|
|
2
2
|
import { parseCatalogueCsv } from "./csv-reader.js";
|
|
3
3
|
import { type CatalogueRow } from "./csv-writer.js";
|
|
4
4
|
import { FindingsLog } from "./findings.js";
|
|
@@ -31,6 +31,7 @@ export interface AggregateResult {
|
|
|
31
31
|
}
|
|
32
32
|
export type Writer = (s: string) => void;
|
|
33
33
|
export declare function runCatalogueAggregate(inputs: AggregateInputs, write: Writer): Promise<AggregateResult>;
|
|
34
|
+
export declare function dedupExternalSystems(rowsByCatalogue: Map<string, CatalogueRow[]>, findings: FindingsLog): void;
|
|
34
35
|
declare function stampSourceSddId(catalogue: CatalogueDef, rows: readonly Record<string, string>[], sourceSddId: string): CatalogueRow[];
|
|
35
36
|
/**
|
|
36
37
|
* Resolve cross-SDD `screen_feature` edges (schema 2.24.0, R3).
|
|
@@ -99,7 +100,9 @@ export interface AdjudicationSummary {
|
|
|
99
100
|
* REFERENCE (§4.1), and even L1 declarations may name something the estate
|
|
100
101
|
* owns (measured: 39% of one estate's dependency edges pointed at an
|
|
101
102
|
* external-system row denoting its own platform). Identity is the declared
|
|
102
|
-
* diagram entry id (§4.2)
|
|
103
|
+
* diagram entry id (§4.2) — at schema 3.0.0 the row id derives from it
|
|
104
|
+
* (M-ADR 0203 §4.2) — with the display name only as a unique-match
|
|
105
|
+
* fallback.
|
|
103
106
|
*
|
|
104
107
|
* Outcomes per candidate:
|
|
105
108
|
* - resolves to an internal system of kind platform/subsystem/application:
|
|
@@ -119,6 +122,27 @@ export interface AdjudicationSummary {
|
|
|
119
122
|
* Exported for testing.
|
|
120
123
|
*/
|
|
121
124
|
export declare function adjudicateExternalSystems(rowsByCatalogue: Map<string, CatalogueRow[]>, findings: FindingsLog): AdjudicationSummary;
|
|
125
|
+
export interface ContractRekeySummary {
|
|
126
|
+
readonly rekeyed: number;
|
|
127
|
+
readonly deduped: number;
|
|
128
|
+
readonly ambiguous: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* M-ADR 0203 §4.4's cross-SDD detector-and-re-key, cut window only.
|
|
132
|
+
*
|
|
133
|
+
* Every FK referencing `contracts.id` (schema-driven, so a future contract
|
|
134
|
+
* edge is covered without touching this) is scanned over the merged rows: a
|
|
135
|
+
* value that resolves to no contract, but whose `${value}-${kind}` form
|
|
136
|
+
* resolves via the legacy map built from the MERGED contracts rows, is a
|
|
137
|
+
* provably tool-authored 2.x id — regenerable, not drift. The value re-keys
|
|
138
|
+
* and the composite row id's embedded `__`-delimited component re-keys with
|
|
139
|
+
* it. A re-keyed row whose id then collides with an existing row is the
|
|
140
|
+
* same fact asserted under both eras — deduped, counted. An old id that was
|
|
141
|
+
* a 2.x cross-family collision maps to several contracts: loud and left
|
|
142
|
+
* dangling for FK validation to report (the author qualifies; never a
|
|
143
|
+
* guess). Exported for testing.
|
|
144
|
+
*/
|
|
145
|
+
export declare function rekeyLegacyContractRefs(schema: CanonicalSchema, rowsByCatalogue: Map<string, CatalogueRow[]>, findings: FindingsLog): ContractRekeySummary;
|
|
122
146
|
/**
|
|
123
147
|
* M-ADR 0133 §4.2c open hygiene, shipped with M-ADR 0189 §4.4: the
|
|
124
148
|
* `platform` kind is documented "exactly one per ecosystem after federation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../../src/lib/catalogue/aggregate.ts"],"names":[],"mappings":"AA+BA,OAAO,
|
|
1
|
+
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../../src/lib/catalogue/aggregate.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAuB,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClG,OAAO,EAAoB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAc5C,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzC,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,CAAC,CAyW1B;AA2DD,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EAC5C,QAAQ,EAAE,WAAW,GACpB,IAAI,CAkFN;AAgPD,iBAAS,gBAAgB,CACvB,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACvC,WAAW,EAAE,MAAM,GAClB,YAAY,EAAE,CAUhB;AA2MD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,SAAS,YAAY,EAAE,EACvC,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,QAAQ,EAAE,WAAW,GACpB,YAAY,EAAE,CAiDhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,EACtC,QAAQ,EAAE,WAAW,GACpB,IAAI,CAoBN;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,qGAAqG;IACrG,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,mFAAmF;IACnF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,yGAAyG;IACzG,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+GAA+G;IAC/G,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,kEAAkE;IAClE,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,oFAAoF;IACpF,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EAC5C,QAAQ,EAAE,WAAW,GACpB,mBAAmB,CA6oBrB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,eAAe,EACvB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EAC5C,QAAQ,EAAE,WAAW,GACpB,oBAAoB,CAgFtB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,QAAQ,EAAE,WAAW,GACpB,IAAI,CAYN;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,QAAQ,EAAE,WAAW,GACpB,IAAI,CAoBN;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gCAAgC,CAC9C,mBAAmB,EAAE,SAAS,YAAY,EAAE,EAC5C,kBAAkB,EAAE,SAAS,YAAY,EAAE,EAC3C,QAAQ,EAAE,WAAW,GACpB,YAAY,EAAE,CAqIhB;AAID,eAAO,MAAM,aAAa;;;CAA0C,CAAC"}
|
|
@@ -27,6 +27,7 @@ import { SCHEMA_COMPAT } from "./schema-compat.js";
|
|
|
27
27
|
import { loadCanonicalSchema } from "./schema-loader.js";
|
|
28
28
|
import { readCatalogueCsv, parseCatalogueCsv } from "./csv-reader.js";
|
|
29
29
|
import { writeCatalogue } from "./csv-writer.js";
|
|
30
|
+
import { buildLegacyContractIdMap } from "./contract-id.js";
|
|
30
31
|
import { validateForeignKeys } from "./fk-validator.js";
|
|
31
32
|
import { FindingsLog } from "./findings.js";
|
|
32
33
|
import { mergeEdgeLabel } from "./builders/edge-label-merge.js";
|
|
@@ -220,6 +221,19 @@ export async function runCatalogueAggregate(inputs, write) {
|
|
|
220
221
|
`${adjudication.exempted} exempted, ${adjudication.unverified} unverified\n`);
|
|
221
222
|
// 5g. Platform-kind uniqueness (M-ADR 0133 §4.2c open hygiene).
|
|
222
223
|
checkPlatformKindUniqueness(rowsByCatalogue.get("c4_systems") ?? [], findings);
|
|
224
|
+
// 5h. M-ADR 0203 §4.4 cross-SDD re-key: the contract-id migration derives
|
|
225
|
+
// per-SDD, but contract-FK edges cross SDD boundaries — a federated SDD
|
|
226
|
+
// that owns no contracts has an empty map, so its references keep the 2.x
|
|
227
|
+
// kindless ids (measured on the first rehearsal probe: 170 dangling FKs
|
|
228
|
+
// over 6 old ids). This tier sees every contract row, so the old form is
|
|
229
|
+
// decidable here and nowhere earlier.
|
|
230
|
+
const contractRekey = rekeyLegacyContractRefs(schema, rowsByCatalogue, findings);
|
|
231
|
+
// Always emitted: an absent line is indistinguishable from a check that
|
|
232
|
+
// never ran (M-ADR 0200 §4.3 — a zero must be affirmable). Measured: the
|
|
233
|
+
// first all-zero estate correctly refused to report "0" for a line that
|
|
234
|
+
// was never printed.
|
|
235
|
+
write(` contract-id cut window (M-ADR 0203 §4.4): ${contractRekey.rekeyed} cross-SDD reference(s) re-keyed, ` +
|
|
236
|
+
`${contractRekey.deduped} duplicate fact(s) dropped, ${contractRekey.ambiguous} ambiguous left dangling\n`);
|
|
223
237
|
// 6. Duplicate primary key check.
|
|
224
238
|
const duplicates = findDuplicatePrimaryKeys(schema, rowsByCatalogue);
|
|
225
239
|
if (duplicates.length > 0) {
|
|
@@ -335,21 +349,31 @@ function rederiveAppend(rowsByCatalogue, catalogueName, rederivedRows) {
|
|
|
335
349
|
const added = rederivedRows.filter((r) => !existingIds.has(String(r["id"])));
|
|
336
350
|
rowsByCatalogue.set(catalogueName, [...concatenated, ...added]);
|
|
337
351
|
}
|
|
338
|
-
function dedupExternalSystems(rowsByCatalogue, findings) {
|
|
352
|
+
export function dedupExternalSystems(rowsByCatalogue, findings) {
|
|
339
353
|
const rows = rowsByCatalogue.get("c4_external_systems");
|
|
340
354
|
if (rows === undefined || rows.length === 0)
|
|
341
355
|
return;
|
|
342
356
|
const byId = new Map();
|
|
357
|
+
const namesById = new Map();
|
|
343
358
|
for (const row of rows) {
|
|
344
359
|
const id = String(row["id"] ?? "");
|
|
345
360
|
if (id === "")
|
|
346
361
|
continue;
|
|
362
|
+
const name = String(row["name"] ?? "");
|
|
363
|
+
const names = namesById.get(id);
|
|
364
|
+
if (names) {
|
|
365
|
+
if (name !== "" && !names.includes(name))
|
|
366
|
+
names.push(name);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
namesById.set(id, name === "" ? [] : [name]);
|
|
370
|
+
}
|
|
347
371
|
const existing = byId.get(id);
|
|
348
372
|
if (existing === undefined) {
|
|
349
373
|
byId.set(id, row);
|
|
350
374
|
continue;
|
|
351
375
|
}
|
|
352
|
-
for (const attr of ["
|
|
376
|
+
for (const attr of ["technology", "description"]) {
|
|
353
377
|
const a = String(existing[attr] ?? "");
|
|
354
378
|
const b = String(row[attr] ?? "");
|
|
355
379
|
if (a !== "" && b !== "" && a !== b) {
|
|
@@ -357,6 +381,47 @@ function dedupExternalSystems(rowsByCatalogue, findings) {
|
|
|
357
381
|
}
|
|
358
382
|
}
|
|
359
383
|
}
|
|
384
|
+
// M-ADR 0203 §4.2's second fallout detector — the mirror image of the
|
|
385
|
+
// same-name split. With identity now the DECLARED entry id, a generic
|
|
386
|
+
// diagram-local mnemonic shared across SDDs merges DIFFERENT real-world
|
|
387
|
+
// systems into one row (measured before this shipped: 17 integration
|
|
388
|
+
// SDDs each declaring their provider under one shared mnemonic — 17
|
|
389
|
+
// providers, one surviving row). A merge whose display names corroborate
|
|
390
|
+
// (compact-equal or token-subset — benign label variance on one
|
|
391
|
+
// referent) stays the quiet dedup this catalogue is designed for; a
|
|
392
|
+
// merge whose names DON'T corroborate is likely N referents under one
|
|
393
|
+
// entry id, and every hidden referent is named. Rename the diagram entry
|
|
394
|
+
// ids apart — a diagram edit, the M-ADR 0189 §4.2 sanctioned opt-out.
|
|
395
|
+
const compactName = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
396
|
+
const nameTokens = (s) => new Set(s
|
|
397
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
398
|
+
.toLowerCase()
|
|
399
|
+
.split(/[^a-z0-9]+/)
|
|
400
|
+
.filter((t) => t !== ""));
|
|
401
|
+
const namesCorroborate = (a, b) => {
|
|
402
|
+
if (compactName(a) === compactName(b))
|
|
403
|
+
return true;
|
|
404
|
+
const ta = nameTokens(a);
|
|
405
|
+
const tb = nameTokens(b);
|
|
406
|
+
const subset = (x, y) => x.size > 0 && [...x].every((t) => y.has(t));
|
|
407
|
+
return subset(ta, tb) || subset(tb, ta);
|
|
408
|
+
};
|
|
409
|
+
for (const [id, names] of namesById) {
|
|
410
|
+
if (names.length < 2)
|
|
411
|
+
continue;
|
|
412
|
+
const anchor = names[0];
|
|
413
|
+
const strangers = names.slice(1).filter((n) => !namesCorroborate(anchor, n));
|
|
414
|
+
if (strangers.length === 0) {
|
|
415
|
+
// Corroborating label variance — keep the plain per-attr conflict
|
|
416
|
+
// shape the pre-3.0.0 dedup reported.
|
|
417
|
+
findings.add("convention", `c4_external_systems: id "${id}" has conflicting name across SDDs [${names.join(", ")}] — first-seen kept`);
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
findings.add("divergence", `c4_external_systems: declared id "${id}" merges ${names.length} rows whose display names do not corroborate ` +
|
|
421
|
+
`[${names.join(" | ")}] — likely DIFFERENT systems under one shared diagram entry id; ` +
|
|
422
|
+
`first-seen kept and ${names.length - 1} referent(s) are hidden by this merge. ` +
|
|
423
|
+
`Rename the diagram entry ids apart (M-ADR 0203 §4.2 / M-ADR 0189 §4.2)`);
|
|
424
|
+
}
|
|
360
425
|
rowsByCatalogue.set("c4_external_systems", [...byId.values()]);
|
|
361
426
|
}
|
|
362
427
|
// Method-scoped ADRs (doc_path under docs/method/adr/) are the shared method
|
|
@@ -833,7 +898,9 @@ export function checkSuccessorReferences(products, contributorSddIds, findings)
|
|
|
833
898
|
* REFERENCE (§4.1), and even L1 declarations may name something the estate
|
|
834
899
|
* owns (measured: 39% of one estate's dependency edges pointed at an
|
|
835
900
|
* external-system row denoting its own platform). Identity is the declared
|
|
836
|
-
* diagram entry id (§4.2)
|
|
901
|
+
* diagram entry id (§4.2) — at schema 3.0.0 the row id derives from it
|
|
902
|
+
* (M-ADR 0203 §4.2) — with the display name only as a unique-match
|
|
903
|
+
* fallback.
|
|
837
904
|
*
|
|
838
905
|
* Outcomes per candidate:
|
|
839
906
|
* - resolves to an internal system of kind platform/subsystem/application:
|
|
@@ -915,7 +982,11 @@ export function adjudicateExternalSystems(rowsByCatalogue, findings) {
|
|
|
915
982
|
}
|
|
916
983
|
}
|
|
917
984
|
const resolveSystem = (ext) => {
|
|
918
|
-
|
|
985
|
+
// Schema 3.0.0: the row id itself derives from the declared entry id
|
|
986
|
+
// (M-ADR 0203 §4.2), so the id-slug IS declared identity — it backstops
|
|
987
|
+
// an empty declared_id cell, and a hit through it is a declared hit
|
|
988
|
+
// (corroboration-gated), never a label-keyed one.
|
|
989
|
+
const declared = norm(String(ext["declared_id"] ?? "")) || String(ext["id"]).replace(/^ext-/, "");
|
|
919
990
|
if (declared !== "") {
|
|
920
991
|
for (const cand of [declared, `${declared}-system`]) {
|
|
921
992
|
if (systemIds.has(cand))
|
|
@@ -925,14 +996,26 @@ export function adjudicateExternalSystems(rowsByCatalogue, findings) {
|
|
|
925
996
|
if (viaProduct !== undefined)
|
|
926
997
|
return { id: viaProduct, method: "declared" };
|
|
927
998
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
999
|
+
// NAME evidence, per M-ADR 0189 §4.2's ratified fallback ("the
|
|
1000
|
+
// display-name slug remains only as a fallback for entries whose id
|
|
1001
|
+
// resolves to nothing"). Under 2.x the row id WAS the name slug, so
|
|
1002
|
+
// the id-slug path carried this; at 3.0.0 the slug must be derived
|
|
1003
|
+
// from the name itself or the fallback silently narrows — measured on
|
|
1004
|
+
// the first forward-estate probe: 35 of 38 internal platform edges
|
|
1005
|
+
// reclassified external because `System_Ext(platform, "Acme
|
|
1006
|
+
// Platform", …)` stopped resolving against the `acme-platform`
|
|
1007
|
+
// product. Every hit here is method "name" (label-keyed — the
|
|
1008
|
+
// conversion emits the §4.2 convention finding).
|
|
1009
|
+
const nameSlug = norm(String(ext["name"] ?? ""));
|
|
1010
|
+
if (nameSlug !== "") {
|
|
1011
|
+
for (const cand of [nameSlug, `${nameSlug}-system`]) {
|
|
1012
|
+
if (systemIds.has(cand))
|
|
1013
|
+
return { id: cand, method: "name" };
|
|
1014
|
+
}
|
|
1015
|
+
const viaNameProduct = systemByProduct.get(nameSlug);
|
|
1016
|
+
if (viaNameProduct !== undefined)
|
|
1017
|
+
return { id: viaNameProduct, method: "name" };
|
|
932
1018
|
}
|
|
933
|
-
const viaSlugProduct = systemByProduct.get(slug);
|
|
934
|
-
if (viaSlugProduct !== undefined)
|
|
935
|
-
return { id: viaSlugProduct, method: "name" };
|
|
936
1019
|
const byName = systemsByName.get(compact(String(ext["name"] ?? "")));
|
|
937
1020
|
if (byName !== undefined && byName.length === 1) {
|
|
938
1021
|
return { id: byName[0], method: "name" };
|
|
@@ -965,6 +1048,8 @@ export function adjudicateExternalSystems(rowsByCatalogue, findings) {
|
|
|
965
1048
|
if (declarerSystem !== undefined) {
|
|
966
1049
|
const own = containerKeysBySystem.get(declarerSystem);
|
|
967
1050
|
if (own !== undefined) {
|
|
1051
|
+
// The id-slug key duplicates the declared key at 3.0.0 (the row id
|
|
1052
|
+
// derives from the entry id) but backstops an empty declared_id.
|
|
968
1053
|
for (const key of [
|
|
969
1054
|
compact(String(ext["declared_id"] ?? "")),
|
|
970
1055
|
compact(String(ext["id"]).replace(/^ext-/, "")),
|
|
@@ -977,7 +1062,8 @@ export function adjudicateExternalSystems(rowsByCatalogue, findings) {
|
|
|
977
1062
|
}
|
|
978
1063
|
}
|
|
979
1064
|
const extName = String(ext["name"] ?? "");
|
|
980
|
-
const declaredKey = compact(String(ext["declared_id"] ?? ""))
|
|
1065
|
+
const declaredKey = compact(String(ext["declared_id"] ?? "")) ||
|
|
1066
|
+
compact(String(ext["id"]).replace(/^ext-/, ""));
|
|
981
1067
|
const byKey = declaredKey !== "" ? containersByKey.get(declaredKey) : undefined;
|
|
982
1068
|
if (byKey !== undefined) {
|
|
983
1069
|
const corroborating = byKey.filter((cid) => corroboratesLabel(extName, containerNameById.get(cid) ?? "", ""));
|
|
@@ -1367,6 +1453,35 @@ export function adjudicateExternalSystems(rowsByCatalogue, findings) {
|
|
|
1367
1453
|
if (!existingIds.has(String(row["id"])))
|
|
1368
1454
|
merged.push(row);
|
|
1369
1455
|
}
|
|
1456
|
+
// M-ADR 0203 §4.2's detector for its own fallout: with row identity now
|
|
1457
|
+
// the DECLARED entry id, SDDs that declared different entry ids for one
|
|
1458
|
+
// real-world external system split into distinct rows — where the 2.x
|
|
1459
|
+
// name-slug ids silently merged them. Measured before this shipped: 5 old
|
|
1460
|
+
// ids deriving 2-3 conflicting new ids across one estate's SDDs. The
|
|
1461
|
+
// split must be loud, not latent: one display name held by several kept
|
|
1462
|
+
// declared identities is either one estate entity (converge the diagram
|
|
1463
|
+
// entry ids — the rename is a diagram edit, the sanctioned opt-out) or
|
|
1464
|
+
// genuinely several (rename the labels apart). Convention, not an error:
|
|
1465
|
+
// the aggregator cannot know which.
|
|
1466
|
+
const keptByName = new Map();
|
|
1467
|
+
for (const ext of keptExts) {
|
|
1468
|
+
const key = compact(String(ext["name"] ?? ""));
|
|
1469
|
+
if (key === "")
|
|
1470
|
+
continue;
|
|
1471
|
+
const ids = keptByName.get(key);
|
|
1472
|
+
if (ids)
|
|
1473
|
+
ids.push(String(ext["id"]));
|
|
1474
|
+
else
|
|
1475
|
+
keptByName.set(key, [String(ext["id"])]);
|
|
1476
|
+
}
|
|
1477
|
+
for (const [, ids] of [...keptByName.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
1478
|
+
if (ids.length < 2)
|
|
1479
|
+
continue;
|
|
1480
|
+
const sorted = [...ids].sort((a, b) => a.localeCompare(b));
|
|
1481
|
+
findings.add("convention", `c4_external_systems: one display name "${String(keptExts.find((e) => String(e["id"]) === sorted[0])?.["name"] ?? "")}" is held by ${sorted.length} declared identities [${sorted.join(", ")}] — ` +
|
|
1482
|
+
`either one estate entity split by divergent diagram entry ids (converge the entry ids) ` +
|
|
1483
|
+
`or several entities sharing a label (rename the labels apart) (M-ADR 0203 §4.2)`);
|
|
1484
|
+
}
|
|
1370
1485
|
rowsByCatalogue.set("c4_external_systems", keptExts);
|
|
1371
1486
|
rowsByCatalogue.set("system_external_dependency", keptDeps);
|
|
1372
1487
|
rowsByCatalogue.set("system_system_dependency", merged);
|
|
@@ -1385,6 +1500,99 @@ export function adjudicateExternalSystems(rowsByCatalogue, findings) {
|
|
|
1385
1500
|
systemFallbackEdges,
|
|
1386
1501
|
};
|
|
1387
1502
|
}
|
|
1503
|
+
/**
|
|
1504
|
+
* M-ADR 0203 §4.4's cross-SDD detector-and-re-key, cut window only.
|
|
1505
|
+
*
|
|
1506
|
+
* Every FK referencing `contracts.id` (schema-driven, so a future contract
|
|
1507
|
+
* edge is covered without touching this) is scanned over the merged rows: a
|
|
1508
|
+
* value that resolves to no contract, but whose `${value}-${kind}` form
|
|
1509
|
+
* resolves via the legacy map built from the MERGED contracts rows, is a
|
|
1510
|
+
* provably tool-authored 2.x id — regenerable, not drift. The value re-keys
|
|
1511
|
+
* and the composite row id's embedded `__`-delimited component re-keys with
|
|
1512
|
+
* it. A re-keyed row whose id then collides with an existing row is the
|
|
1513
|
+
* same fact asserted under both eras — deduped, counted. An old id that was
|
|
1514
|
+
* a 2.x cross-family collision maps to several contracts: loud and left
|
|
1515
|
+
* dangling for FK validation to report (the author qualifies; never a
|
|
1516
|
+
* guess). Exported for testing.
|
|
1517
|
+
*/
|
|
1518
|
+
export function rekeyLegacyContractRefs(schema, rowsByCatalogue, findings) {
|
|
1519
|
+
const contracts = rowsByCatalogue.get("contracts") ?? [];
|
|
1520
|
+
const contractIds = new Set(contracts.map((r) => String(r["id"])));
|
|
1521
|
+
const legacyMap = buildLegacyContractIdMap(contracts);
|
|
1522
|
+
let rekeyed = 0;
|
|
1523
|
+
let deduped = 0;
|
|
1524
|
+
let ambiguous = 0;
|
|
1525
|
+
if (legacyMap.size === 0)
|
|
1526
|
+
return { rekeyed, deduped, ambiguous };
|
|
1527
|
+
for (const cat of schema.catalogues) {
|
|
1528
|
+
const fkCols = cat.foreignKeys
|
|
1529
|
+
.filter((fk) => fk.references === "contracts.id")
|
|
1530
|
+
.map((fk) => fk.column);
|
|
1531
|
+
if (fkCols.length === 0 || cat.name === "contracts")
|
|
1532
|
+
continue;
|
|
1533
|
+
const rows = rowsByCatalogue.get(cat.name) ?? [];
|
|
1534
|
+
if (rows.length === 0)
|
|
1535
|
+
continue;
|
|
1536
|
+
const perOldId = new Map();
|
|
1537
|
+
const ambiguousOld = new Map();
|
|
1538
|
+
const kept = [];
|
|
1539
|
+
const idsAfter = new Set(rows.map((r) => String(r["id"])));
|
|
1540
|
+
for (const row of rows) {
|
|
1541
|
+
let dropped = false;
|
|
1542
|
+
let current = row;
|
|
1543
|
+
for (const col of fkCols) {
|
|
1544
|
+
const value = String(current[col] ?? "");
|
|
1545
|
+
if (value === "" || contractIds.has(value))
|
|
1546
|
+
continue;
|
|
1547
|
+
const mapped = legacyMap.get(value);
|
|
1548
|
+
if (mapped === undefined)
|
|
1549
|
+
continue;
|
|
1550
|
+
if (mapped.length > 1) {
|
|
1551
|
+
ambiguousOld.set(value, mapped);
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
const newVal = mapped[0];
|
|
1555
|
+
const oldRowId = String(current["id"] ?? "");
|
|
1556
|
+
const newRowId = oldRowId
|
|
1557
|
+
.split("__")
|
|
1558
|
+
.map((part) => (part === value ? newVal : part))
|
|
1559
|
+
.join("__");
|
|
1560
|
+
if (newRowId !== oldRowId && idsAfter.has(newRowId)) {
|
|
1561
|
+
// The same edge fact already exists under the new id (typically
|
|
1562
|
+
// the owning SDD's own re-keyed row) — one fact, two eras.
|
|
1563
|
+
deduped++;
|
|
1564
|
+
dropped = true;
|
|
1565
|
+
break;
|
|
1566
|
+
}
|
|
1567
|
+
const replaced = {
|
|
1568
|
+
...current,
|
|
1569
|
+
[col]: newVal,
|
|
1570
|
+
};
|
|
1571
|
+
if (newRowId !== oldRowId) {
|
|
1572
|
+
idsAfter.delete(oldRowId);
|
|
1573
|
+
idsAfter.add(newRowId);
|
|
1574
|
+
replaced["id"] = newRowId;
|
|
1575
|
+
}
|
|
1576
|
+
current = replaced;
|
|
1577
|
+
perOldId.set(value, (perOldId.get(value) ?? 0) + 1);
|
|
1578
|
+
rekeyed++;
|
|
1579
|
+
}
|
|
1580
|
+
if (!dropped)
|
|
1581
|
+
kept.push(current);
|
|
1582
|
+
}
|
|
1583
|
+
rowsByCatalogue.set(cat.name, kept);
|
|
1584
|
+
for (const [oldId, count] of [...perOldId.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
1585
|
+
findings.add("inferred", `${cat.name}: ${count} cross-SDD reference(s) to "${oldId}" re-keyed to "${legacyMap.get(oldId)[0]}" — ` +
|
|
1586
|
+
`2.x kindless contract id migrated per the 3.0.0 map (regenerable, not drift; M-ADR 0203 §4.4)`);
|
|
1587
|
+
}
|
|
1588
|
+
for (const [oldId, targets] of [...ambiguousOld.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
1589
|
+
ambiguous++;
|
|
1590
|
+
findings.add("convention", `${cat.name}: references to "${oldId}" map to ${targets.length} contracts at 3.0.0 [${targets.join(", ")}] — ` +
|
|
1591
|
+
`a 2.x cross-family collision; the referencing SDD must qualify with the kind-suffixed id; left dangling (M-ADR 0203 §4.4)`);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return { rekeyed, deduped, ambiguous };
|
|
1595
|
+
}
|
|
1388
1596
|
/**
|
|
1389
1597
|
* M-ADR 0133 §4.2c open hygiene, shipped with M-ADR 0189 §4.4: the
|
|
1390
1598
|
* `platform` kind is documented "exactly one per ecosystem after federation
|