@sdd-method/sdd-cli 3.0.0 → 4.0.1

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.
Files changed (133) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +4 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/catalogue/aggregate.d.ts.map +1 -1
  5. package/dist/lib/catalogue/aggregate.js +2 -0
  6. package/dist/lib/catalogue/aggregate.js.map +1 -1
  7. package/dist/lib/catalogue/build.d.ts +3 -1
  8. package/dist/lib/catalogue/build.d.ts.map +1 -1
  9. package/dist/lib/catalogue/build.js +33 -2
  10. package/dist/lib/catalogue/build.js.map +1 -1
  11. package/dist/lib/catalogue/builders/adr-governs.d.ts +4 -0
  12. package/dist/lib/catalogue/builders/adr-governs.d.ts.map +1 -0
  13. package/dist/lib/catalogue/builders/adr-governs.js +39 -0
  14. package/dist/lib/catalogue/builders/adr-governs.js.map +1 -0
  15. package/dist/lib/catalogue/builders/adrs.d.ts +2 -0
  16. package/dist/lib/catalogue/builders/adrs.d.ts.map +1 -1
  17. package/dist/lib/catalogue/builders/adrs.js +11 -0
  18. package/dist/lib/catalogue/builders/adrs.js.map +1 -1
  19. package/dist/lib/catalogue/builders/contract-revisions.d.ts +12 -0
  20. package/dist/lib/catalogue/builders/contract-revisions.d.ts.map +1 -0
  21. package/dist/lib/catalogue/builders/contract-revisions.js +126 -0
  22. package/dist/lib/catalogue/builders/contract-revisions.js.map +1 -0
  23. package/dist/lib/catalogue/builders/data-model.d.ts +73 -0
  24. package/dist/lib/catalogue/builders/data-model.d.ts.map +1 -0
  25. package/dist/lib/catalogue/builders/data-model.js +316 -0
  26. package/dist/lib/catalogue/builders/data-model.js.map +1 -0
  27. package/dist/lib/catalogue/builders/entities-mermaid.d.ts.map +1 -1
  28. package/dist/lib/catalogue/builders/entities-mermaid.js +18 -85
  29. package/dist/lib/catalogue/builders/entities-mermaid.js.map +1 -1
  30. package/dist/lib/catalogue/builders/integration-components.js +49 -5
  31. package/dist/lib/catalogue/builders/integration-components.js.map +1 -1
  32. package/dist/lib/catalogue/builders/quality-scores.d.ts.map +1 -1
  33. package/dist/lib/catalogue/builders/quality-scores.js +17 -0
  34. package/dist/lib/catalogue/builders/quality-scores.js.map +1 -1
  35. package/dist/lib/catalogue/builders/requirements.d.ts +7 -1
  36. package/dist/lib/catalogue/builders/requirements.d.ts.map +1 -1
  37. package/dist/lib/catalogue/builders/requirements.js +132 -23
  38. package/dist/lib/catalogue/builders/requirements.js.map +1 -1
  39. package/dist/lib/catalogue/builders/user-journeys.d.ts.map +1 -1
  40. package/dist/lib/catalogue/builders/user-journeys.js +8 -13
  41. package/dist/lib/catalogue/builders/user-journeys.js.map +1 -1
  42. package/dist/lib/catalogue/canonical-schema.yaml +767 -17
  43. package/dist/lib/catalogue/retired-catalogues.d.ts +6 -0
  44. package/dist/lib/catalogue/retired-catalogues.d.ts.map +1 -0
  45. package/dist/lib/catalogue/retired-catalogues.js +25 -0
  46. package/dist/lib/catalogue/retired-catalogues.js.map +1 -0
  47. package/dist/lib/catalogue/rubric-coverage-map.yaml +90 -2
  48. package/dist/lib/catalogue/schema-compat.d.ts +1 -1
  49. package/dist/lib/catalogue/schema-compat.js +1 -1
  50. package/dist/lib/catalogue/schema-loader.d.ts +6 -0
  51. package/dist/lib/catalogue/schema-loader.d.ts.map +1 -1
  52. package/dist/lib/catalogue/schema-loader.js +3 -0
  53. package/dist/lib/catalogue/schema-loader.js.map +1 -1
  54. package/dist/lib/catalogue/staleness-verdicts.d.ts.map +1 -1
  55. package/dist/lib/catalogue/staleness-verdicts.js +75 -6
  56. package/dist/lib/catalogue/staleness-verdicts.js.map +1 -1
  57. package/dist/lib/metamodel/roundtrip-gate.d.ts.map +1 -1
  58. package/dist/lib/metamodel/roundtrip-gate.js +3 -0
  59. package/dist/lib/metamodel/roundtrip-gate.js.map +1 -1
  60. package/dist/lib/metamodel/schema-projection.d.ts +6 -0
  61. package/dist/lib/metamodel/schema-projection.d.ts.map +1 -1
  62. package/dist/lib/metamodel/schema-projection.js +27 -1
  63. package/dist/lib/metamodel/schema-projection.js.map +1 -1
  64. package/dist/lib/structure/apply.d.ts +8 -0
  65. package/dist/lib/structure/apply.d.ts.map +1 -0
  66. package/dist/lib/structure/apply.js +71 -0
  67. package/dist/lib/structure/apply.js.map +1 -0
  68. package/dist/lib/structure/archive.d.ts +21 -0
  69. package/dist/lib/structure/archive.d.ts.map +1 -0
  70. package/dist/lib/structure/archive.js +318 -0
  71. package/dist/lib/structure/archive.js.map +1 -0
  72. package/dist/lib/structure/catalogue.d.ts +33 -0
  73. package/dist/lib/structure/catalogue.d.ts.map +1 -0
  74. package/dist/lib/structure/catalogue.js +183 -0
  75. package/dist/lib/structure/catalogue.js.map +1 -0
  76. package/dist/lib/structure/compose.d.ts +30 -0
  77. package/dist/lib/structure/compose.d.ts.map +1 -0
  78. package/dist/lib/structure/compose.js +174 -0
  79. package/dist/lib/structure/compose.js.map +1 -0
  80. package/dist/lib/structure/effects.d.ts +16 -0
  81. package/dist/lib/structure/effects.d.ts.map +1 -0
  82. package/dist/lib/structure/effects.js +36 -0
  83. package/dist/lib/structure/effects.js.map +1 -0
  84. package/dist/lib/structure/extract.d.ts +19 -0
  85. package/dist/lib/structure/extract.d.ts.map +1 -0
  86. package/dist/lib/structure/extract.js +123 -0
  87. package/dist/lib/structure/extract.js.map +1 -0
  88. package/dist/lib/structure/model.d.ts +57 -0
  89. package/dist/lib/structure/model.d.ts.map +1 -0
  90. package/dist/lib/structure/model.js +212 -0
  91. package/dist/lib/structure/model.js.map +1 -0
  92. package/dist/lib/structure/render.d.ts +11 -0
  93. package/dist/lib/structure/render.d.ts.map +1 -0
  94. package/dist/lib/structure/render.js +242 -0
  95. package/dist/lib/structure/render.js.map +1 -0
  96. package/dist/lib/structure/view.d.ts +36 -0
  97. package/dist/lib/structure/view.d.ts.map +1 -0
  98. package/dist/lib/structure/view.js +301 -0
  99. package/dist/lib/structure/view.js.map +1 -0
  100. package/dist/lib/validate/agreement-evidence.d.ts +8 -2
  101. package/dist/lib/validate/agreement-evidence.d.ts.map +1 -1
  102. package/dist/lib/validate/agreement-evidence.js +20 -8
  103. package/dist/lib/validate/agreement-evidence.js.map +1 -1
  104. package/dist/lib/validate/behaviour-store.d.ts +5 -0
  105. package/dist/lib/validate/behaviour-store.d.ts.map +1 -1
  106. package/dist/lib/validate/behaviour-store.js +54 -13
  107. package/dist/lib/validate/behaviour-store.js.map +1 -1
  108. package/dist/lib/validate/dispatch.d.ts.map +1 -1
  109. package/dist/lib/validate/dispatch.js +7 -0
  110. package/dist/lib/validate/dispatch.js.map +1 -1
  111. package/dist/lib/validate/journey-impact.d.ts +2 -0
  112. package/dist/lib/validate/journey-impact.d.ts.map +1 -1
  113. package/dist/lib/validate/journey-impact.js +17 -2
  114. package/dist/lib/validate/journey-impact.js.map +1 -1
  115. package/dist/lib/validate/structural-model.d.ts +11 -0
  116. package/dist/lib/validate/structural-model.d.ts.map +1 -0
  117. package/dist/lib/validate/structural-model.js +265 -0
  118. package/dist/lib/validate/structural-model.js.map +1 -0
  119. package/dist/maintain-version.json +2 -2
  120. package/dist/verbs/change.d.ts +3 -0
  121. package/dist/verbs/change.d.ts.map +1 -0
  122. package/dist/verbs/change.js +15 -0
  123. package/dist/verbs/change.js.map +1 -0
  124. package/dist/verbs/structure.d.ts +10 -0
  125. package/dist/verbs/structure.d.ts.map +1 -0
  126. package/dist/verbs/structure.js +124 -0
  127. package/dist/verbs/structure.js.map +1 -0
  128. package/dist/verify-version.json +2 -2
  129. package/package.json +1 -1
  130. package/scripts/author-version.json +2 -2
  131. package/scripts/build-persona-package.mjs +5 -0
  132. package/scripts/maintain-version.json +2 -2
  133. package/scripts/verify-version.json +2 -2
@@ -0,0 +1,183 @@
1
+ // The catalogue from the structural model, and the model a built catalogue implies (M-ADR 0230 §4.3, §5.3,
2
+ // Proposed). Where an SDD has a model, it is the record of structure, so every structure catalogue is built from it whole:
3
+ // a row per element of the current view, with the model's structure columns;
4
+ // its other columns (every class but structure: derived, declaration, evidence, intent, execution) from the
5
+ // source-built row with the same id;
6
+ // for an element only the model holds, the derived columns the model itself settles (MODEL_DERIVED).
7
+ // What the sources hold and the model does not, and a structure column where they disagree, is reported once per
8
+ // catalogue. The model wins: either finding means the sources moved outside a change. Pure: no file is read or written.
9
+ import { stringify } from "yaml";
10
+ import { FORMAT_VERSION } from "./model.js";
11
+ import { STRUCTURE } from "./view.js";
12
+ /** The view each generated file renders, for a derived path column of an element only the model holds. */
13
+ export const VIEW_PATHS = {
14
+ c4Context: "docs/structure/c4-context.md",
15
+ c4Containers: "docs/structure/c4-containers.md",
16
+ services: "docs/structure/services.md",
17
+ featureTree: "docs/structure/feature-tree.md",
18
+ dataModel: "docs/structure/data-model.md",
19
+ };
20
+ const EXAMPLES = 5;
21
+ const cell = (v) => (v === null || v === undefined ? "" : typeof v === "boolean" ? (v ? "true" : "false") : String(v));
22
+ /** The derived columns of a row built from an element the sources do not hold. */
23
+ function modelDerived(catalogue, e, view) {
24
+ switch (catalogue) {
25
+ case "features":
26
+ case "capabilities":
27
+ return { source: "structural-model" };
28
+ case "domain_models":
29
+ return { doc_path: VIEW_PATHS.dataModel };
30
+ case "c4_systems":
31
+ return { diagram_path: VIEW_PATHS.c4Context };
32
+ case "domains":
33
+ return { repo_count: [...(view.get("repositories")?.values() ?? [])].filter((r) => r.columns["domain_id"] === e.id).length };
34
+ default:
35
+ return {};
36
+ }
37
+ }
38
+ function structureColumns(cat) {
39
+ return cat.columns.filter((c) => c.name !== cat.primaryKey && c.sourceOfTruth === STRUCTURE).map((c) => c.name);
40
+ }
41
+ function sample(ids) {
42
+ return ids.slice(0, EXAMPLES).join(", ") + (ids.length > EXAMPLES ? `, and ${ids.length - EXAMPLES} more` : "");
43
+ }
44
+ /**
45
+ * Replace every structure catalogue's rows with the view's. Rows of other catalogues are returned as they are. Source
46
+ * rows sharing an id are merged first, later values filling earlier gaps, as the keyed-table dedupe would.
47
+ */
48
+ export function overlayModel(schema, view, rows, findings) {
49
+ const out = new Map(rows);
50
+ for (const cat of schema.catalogues) {
51
+ if (cat.sourceOfTruth !== STRUCTURE)
52
+ continue;
53
+ const pk = cat.primaryKey;
54
+ const structural = structureColumns(cat);
55
+ const source = new Map();
56
+ for (const r of rows.get(cat.name) ?? []) {
57
+ const id = cell(r[pk]);
58
+ const prior = source.get(id);
59
+ if (!prior)
60
+ source.set(id, { ...r });
61
+ else
62
+ for (const [k, v] of Object.entries(r))
63
+ if (cell(prior[k]) === "" && cell(v) !== "")
64
+ prior[k] = v;
65
+ }
66
+ const elements = view.get(cat.name) ?? new Map();
67
+ const built = [];
68
+ const differ = [];
69
+ for (const e of elements.values()) {
70
+ const from = source.get(e.id);
71
+ const row = {};
72
+ if (from) {
73
+ for (const [k, v] of Object.entries(from))
74
+ if (!structural.includes(k))
75
+ row[k] = v;
76
+ const cols = structural.filter((c) => cell(from[c]) !== "" && cell(from[c]) !== cell(e.columns[c]));
77
+ if (cols.length > 0)
78
+ differ.push(`${e.id} (${cols.join(", ")})`);
79
+ }
80
+ else {
81
+ Object.assign(row, modelDerived(cat.name, e, view));
82
+ }
83
+ row[pk] = e.id;
84
+ for (const c of structural)
85
+ row[c] = e.columns[c];
86
+ built.push(row);
87
+ }
88
+ const missing = [...source.keys()].filter((id) => id !== "" && !elements.has(id)).sort();
89
+ if (missing.length > 0) {
90
+ findings.add("divergence", `${cat.name}: the sources hold ${missing.length} row(s) the structural model does not (${sample(missing)}); the model is the record of structure, so they are not catalogued. Add them through a change's structural deltas, or remove them from the sources (M-ADR 0230 §4.3, Proposed)`, "warn", `catalogue:${cat.name}`);
91
+ }
92
+ if (differ.length > 0) {
93
+ findings.add("divergence", `${cat.name}: ${differ.length} element(s) where the sources and the structural model disagree, catalogued as the model states them: ${sample(differ.sort())}. A source that is a generated view is rendered again; any other is brought into line through a change (M-ADR 0230 §4.3, Proposed)`, "warn", `catalogue:${cat.name}`);
94
+ }
95
+ out.set(cat.name, built);
96
+ }
97
+ return out;
98
+ }
99
+ function typed(type, v) {
100
+ switch (type) {
101
+ case "int":
102
+ case "integer":
103
+ case "number": {
104
+ const n = Number(v);
105
+ return Number.isFinite(n) ? n : null;
106
+ }
107
+ case "boolean":
108
+ return v === "true" ? true : v === "false" ? false : null;
109
+ default:
110
+ return v;
111
+ }
112
+ }
113
+ /**
114
+ * The model a built catalogue implies: each structure row as an element of its non-empty structure columns, typed by
115
+ * column. A platform's element goes to its domain's partition (its own `domain_id`, or the first element it references
116
+ * that has one; a domain is its own), else to `platform`; an application or integration SDD's to its sdd_id. A cell
117
+ * that does not parse as its column's type is reported and left out, which is the projection's only loss.
118
+ */
119
+ export function modelFromRows(schema, rows, partitioning, loss) {
120
+ const byKey = new Map();
121
+ for (const cat of schema.catalogues) {
122
+ if (cat.sourceOfTruth !== STRUCTURE)
123
+ continue;
124
+ for (const r of rows.get(cat.name) ?? [])
125
+ byKey.set(`${cat.name}/${cell(r[cat.primaryKey])}`, r);
126
+ }
127
+ const memo = new Map();
128
+ const domainOf = (catName, row, seen) => {
129
+ const cat = schema.catalogues.find((c) => c.name === catName);
130
+ const key = `${catName}/${cell(row[cat.primaryKey])}`;
131
+ if (memo.has(key))
132
+ return memo.get(key);
133
+ if (seen.has(key))
134
+ return null;
135
+ seen.add(key);
136
+ let d = catName === "domains" ? cell(row[cat.primaryKey]) : cell(row["domain_id"]) || null;
137
+ for (const fk of d === null ? cat.foreignKeys : []) {
138
+ const [target, column] = fk.references.split(".");
139
+ if (fk.external || column !== "id" || cell(row[fk.column]) === "")
140
+ continue;
141
+ const next = byKey.get(`${target}/${cell(row[fk.column])}`);
142
+ if (next && (d = domainOf(target, next, seen)) !== null)
143
+ break;
144
+ }
145
+ memo.set(key, d);
146
+ return d;
147
+ };
148
+ const out = new Map();
149
+ for (const cat of schema.catalogues) {
150
+ if (cat.sourceOfTruth !== STRUCTURE)
151
+ continue;
152
+ const cols = cat.columns.filter((c) => c.name !== cat.primaryKey && c.sourceOfTruth === STRUCTURE);
153
+ const sorted = [...(rows.get(cat.name) ?? [])].sort((a, b) => cell(a[cat.primaryKey]).localeCompare(cell(b[cat.primaryKey])));
154
+ const seen = new Set();
155
+ for (const r of sorted) {
156
+ const id = cell(r[cat.primaryKey]);
157
+ if (id === "" || seen.has(id))
158
+ continue;
159
+ seen.add(id);
160
+ const element = { id };
161
+ for (const c of cols) {
162
+ const v = cell(r[c.name]);
163
+ if (v === "")
164
+ continue;
165
+ const t = typed(c.type, v);
166
+ if (t === null)
167
+ loss.push(`${cat.name}/${id}.${c.name}: ${JSON.stringify(v)} is not of type ${c.type}`);
168
+ else
169
+ element[c.name] = t;
170
+ }
171
+ const partition = partitioning.platform ? (domainOf(cat.name, r, new Set()) ?? "platform") : partitioning.sddId;
172
+ const file = out.get(partition) ?? {};
173
+ (file[cat.name] ??= []).push(element);
174
+ out.set(partition, file);
175
+ }
176
+ }
177
+ return out;
178
+ }
179
+ /** One partition's model file, in the format loadModel reads. */
180
+ export function modelFileText(partition, elements) {
181
+ return stringify({ schema_version: FORMAT_VERSION, partition, elements });
182
+ }
183
+ //# sourceMappingURL=catalogue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../../../src/lib/structure/catalogue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { type ChangeStructure } from "./model.js";
2
+ import type { View } from "./view.js";
3
+ export type Lifecycle = "proposed" | "agreed" | "withdrawn" | "superseded" | "unrecorded";
4
+ export interface OpenChange {
5
+ readonly structure: ChangeStructure;
6
+ /** The agreement record's status, or `unrecorded` when the change has no record. */
7
+ readonly status: Lifecycle;
8
+ }
9
+ export type Write = "ADDED" | "MODIFIED" | "REMOVED";
10
+ /** Element key -> the write a change makes to it. */
11
+ export declare function writesOf(s: ChangeStructure): Map<string, Write>;
12
+ /** The view with one change's deltas applied: an addition or modification replaces whole, a removal deletes. */
13
+ export declare function apply(view: View, s: ChangeStructure): View;
14
+ /**
15
+ * A change's dependency closure, dependencies first and the change last. A dependency that is not open is skipped
16
+ * here (archived: its additions are current; otherwise reported as withdrawn by checkDependencies). A cycle stops the
17
+ * walk and is reported there too.
18
+ */
19
+ export declare function closure(change: string, open: ReadonlyMap<string, OpenChange>): string[];
20
+ export declare function viewAt(current: View, change: string, open: ReadonlyMap<string, OpenChange>): View;
21
+ /** Agreed changes in an order that applies each after its dependencies. */
22
+ export declare function agreedOrder(open: ReadonlyMap<string, OpenChange>): string[];
23
+ export declare function intended(current: View, open: ReadonlyMap<string, OpenChange>): View;
24
+ /** Check 1: write overlap. */
25
+ export declare function checkOverlap(open: ReadonlyMap<string, OpenChange>, problems: string[]): void;
26
+ /** Check 3, beyond base revisions: withdrawn dependencies, agreement before a dependency, cycles. */
27
+ export declare function checkDependencies(open: ReadonlyMap<string, OpenChange>, archived: ReadonlySet<string>, problems: string[]): void;
28
+ /** The changes that write any element a finding names. */
29
+ export declare function attribute(problem: string, open: ReadonlyMap<string, OpenChange>, among: readonly string[]): string[];
30
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../../src/lib/structure/compose.ts"],"names":[],"mappings":"AAYA,OAAO,EAAc,KAAK,eAAe,EAAgB,MAAM,YAAY,CAAC;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;AAE1F,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAErD,qDAAqD;AACrD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,eAAe,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAQ/D;AAQD,gHAAgH;AAChH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,eAAe,GAAG,IAAI,CAa1D;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,EAAE,CAcvF;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,CAIjG;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,EAAE,CAO3E;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,CAInF;AAED,8BAA8B;AAC9B,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAyB5F;AAED,qGAAqG;AACrG,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,EACrC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7B,QAAQ,EAAE,MAAM,EAAE,GACjB,IAAI,CA6BN;AAID,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAMpH"}
@@ -0,0 +1,174 @@
1
+ // Views over the structural model, and how changes compose (M-ADR 0230 §4.5, §4.7, Proposed).
2
+ // current the model as published
3
+ // at a change current with that change's deltas and those of its dependency closure, dependencies first
4
+ // intended current with every agreed change's deltas
5
+ // Distinct ids do not prove independence, so three checks run:
6
+ // 1 write overlap two agreed changes never MODIFY or REMOVE one element, and never ADD one id; a change
7
+ // not yet agreed that overlaps an agreed one cannot be agreed while that one is open
8
+ // 2 composed validation each view is validated on its own; what fails in a view and not in current is reported
9
+ // against the changes that write the elements it names
10
+ // 3 read dependencies base revisions still current (validateDeltas); a dependency withdrawn invalidates its
11
+ // dependants; an agreed change depending on one not yet agreed; a cycle of dependencies
12
+ // Pure: reads nothing; the structural-model validator uses it.
13
+ import { elementKey } from "./model.js";
14
+ /** Element key -> the write a change makes to it. */
15
+ export function writesOf(s) {
16
+ const out = new Map();
17
+ for (const d of s.deltas) {
18
+ for (const e of d.added)
19
+ out.set(elementKey(e.catalogue, e.id), "ADDED");
20
+ for (const e of d.modified)
21
+ out.set(elementKey(e.catalogue, e.id), "MODIFIED");
22
+ for (const r of d.removed)
23
+ out.set(elementKey(r.catalogue, r.id), "REMOVED");
24
+ }
25
+ return out;
26
+ }
27
+ function copy(view) {
28
+ const out = new Map();
29
+ for (const [c, m] of view)
30
+ out.set(c, new Map(m));
31
+ return out;
32
+ }
33
+ /** The view with one change's deltas applied: an addition or modification replaces whole, a removal deletes. */
34
+ export function apply(view, s) {
35
+ const out = copy(view);
36
+ const put = (e) => {
37
+ const m = out.get(e.catalogue) ?? new Map();
38
+ m.set(e.id, e);
39
+ out.set(e.catalogue, m);
40
+ };
41
+ for (const d of s.deltas) {
42
+ for (const e of d.added)
43
+ put(e);
44
+ for (const e of d.modified)
45
+ put(e);
46
+ for (const r of d.removed)
47
+ out.get(r.catalogue)?.delete(r.id);
48
+ }
49
+ return out;
50
+ }
51
+ /**
52
+ * A change's dependency closure, dependencies first and the change last. A dependency that is not open is skipped
53
+ * here (archived: its additions are current; otherwise reported as withdrawn by checkDependencies). A cycle stops the
54
+ * walk and is reported there too.
55
+ */
56
+ export function closure(change, open) {
57
+ const order = [];
58
+ const visiting = new Set();
59
+ const visit = (c) => {
60
+ if (order.includes(c) || visiting.has(c))
61
+ return;
62
+ const oc = open.get(c);
63
+ if (!oc)
64
+ return;
65
+ visiting.add(c);
66
+ for (const d of oc.structure.deltas)
67
+ for (const dep of d.dependsOn)
68
+ visit(dep);
69
+ visiting.delete(c);
70
+ order.push(c);
71
+ };
72
+ visit(change);
73
+ return order;
74
+ }
75
+ export function viewAt(current, change, open) {
76
+ let v = current;
77
+ for (const c of closure(change, open))
78
+ v = apply(v, open.get(c).structure);
79
+ return v;
80
+ }
81
+ /** Agreed changes in an order that applies each after its dependencies. */
82
+ export function agreedOrder(open) {
83
+ const order = [];
84
+ for (const [name, oc] of [...open].sort(([a], [b]) => a.localeCompare(b))) {
85
+ if (oc.status !== "agreed")
86
+ continue;
87
+ for (const c of closure(name, open))
88
+ if (!order.includes(c) && open.get(c).status === "agreed")
89
+ order.push(c);
90
+ }
91
+ return order;
92
+ }
93
+ export function intended(current, open) {
94
+ let v = current;
95
+ for (const c of agreedOrder(open))
96
+ v = apply(v, open.get(c).structure);
97
+ return v;
98
+ }
99
+ /** Check 1: write overlap. */
100
+ export function checkOverlap(open, problems) {
101
+ const names = [...open.keys()].sort();
102
+ const writes = new Map(names.map((n) => [n, writesOf(open.get(n).structure)]));
103
+ for (let i = 0; i < names.length; i++) {
104
+ for (let j = i + 1; j < names.length; j++) {
105
+ const a = names[i];
106
+ const b = names[j];
107
+ const sa = open.get(a).status;
108
+ const sb = open.get(b).status;
109
+ if (sa !== "agreed" && sb !== "agreed")
110
+ continue;
111
+ for (const [key, wa] of writes.get(a)) {
112
+ const wb = writes.get(b).get(key);
113
+ if (wb === undefined)
114
+ continue;
115
+ // One adding what the other modifies or removes is reported by the other's own deltas, whose base cannot exist.
116
+ // Two additions of one id are one id claimed twice, whatever their content.
117
+ if ((wa === "ADDED") !== (wb === "ADDED"))
118
+ continue;
119
+ const verb = wa === "ADDED" ? "both ADD" : wa === wb ? `both ${wa}` : `${wa} and ${wb}`;
120
+ if (sa === "agreed" && sb === "agreed")
121
+ problems.push(`write overlap: agreed changes ${a} and ${b} ${verb} ${key}; reorder or merge them`);
122
+ else {
123
+ const [held, waiting] = sa === "agreed" ? [a, b] : [b, a];
124
+ problems.push(`write overlap: ${waiting} ${verb === "both ADD" ? "adds" : "writes"} ${key}, which agreed change ${held} also ${verb === "both ADD" ? "adds" : "writes"}; it cannot be agreed while ${held} is open`);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ /** Check 3, beyond base revisions: withdrawn dependencies, agreement before a dependency, cycles. */
131
+ export function checkDependencies(open, archived, problems) {
132
+ for (const [name, oc] of [...open].sort(([a], [b]) => a.localeCompare(b))) {
133
+ const deps = new Set(oc.structure.deltas.flatMap((d) => [...d.dependsOn]));
134
+ for (const dep of deps) {
135
+ const target = open.get(dep);
136
+ if (target === undefined) {
137
+ if (!archived.has(dep))
138
+ problems.push(`read dependency: ${name} depends on ${dep}, which is neither open nor archived: withdrawn, so ${name} is invalidated until it no longer needs it`);
139
+ continue;
140
+ }
141
+ if (target.status === "withdrawn" || target.status === "superseded") {
142
+ problems.push(`read dependency: ${name} depends on ${dep}, whose agreement record is ${target.status}: ${name} is invalidated until it no longer needs it`);
143
+ }
144
+ else if (oc.status === "agreed" && target.status !== "agreed") {
145
+ problems.push(`read dependency: ${name} is agreed and depends on ${dep}, which is not: a change is agreed with its dependency closure`);
146
+ }
147
+ }
148
+ // A cycle through this change.
149
+ const seen = new Set();
150
+ const stack = [...deps];
151
+ while (stack.length > 0) {
152
+ const c = stack.pop();
153
+ if (c === name) {
154
+ problems.push(`read dependency: ${name} depends on itself through depends_on; a cycle can never archive`);
155
+ break;
156
+ }
157
+ if (seen.has(c))
158
+ continue;
159
+ seen.add(c);
160
+ for (const d of open.get(c)?.structure.deltas ?? [])
161
+ stack.push(...d.dependsOn);
162
+ }
163
+ }
164
+ }
165
+ const KEY_RE = /\b([a-z][a-z0-9_]*)\/([^\s,;:()"]+)/g;
166
+ /** The changes that write any element a finding names. */
167
+ export function attribute(problem, open, among) {
168
+ const keys = new Set([...problem.matchAll(KEY_RE)].map((m) => `${m[1]}/${m[2]}`));
169
+ return among.filter((c) => {
170
+ const w = writesOf(open.get(c).structure);
171
+ return [...keys].some((k) => w.has(k));
172
+ });
173
+ }
174
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/lib/structure/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type ChangeStructure, type Element } from "./model.js";
2
+ export interface Effect {
3
+ readonly element: string;
4
+ readonly operation: "ADDED" | "MODIFIED" | "REMOVED";
5
+ /** The revision written against (MODIFIED, REMOVED). */
6
+ readonly base?: string;
7
+ /** The revision the change leaves (ADDED, MODIFIED). */
8
+ readonly target?: string;
9
+ }
10
+ export declare function effectsOf(s: ChangeStructure, current: ReadonlyMap<string, ReadonlyMap<string, Element>>): Effect[];
11
+ export declare function parseAppliedRecord(text: string): {
12
+ change: string;
13
+ baseCommit: string;
14
+ effects: Effect[];
15
+ } | null;
16
+ //# sourceMappingURL=effects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../../../src/lib/structure/effects.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwB,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEtF,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACrD,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,EAAE,CAkBlH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAQjH"}
@@ -0,0 +1,36 @@
1
+ // A change's structural effect, and the effect record an archive leaves (M-ADR 0230 §4.6, §4.7, Proposed). Pure reads,
2
+ // so the catalogue builder and the verify-only CLI can carry them; writing the record is apply.ts's.
3
+ import { parseDocument } from "yaml";
4
+ import { elementKey, revision } from "./model.js";
5
+ export function effectsOf(s, current) {
6
+ const out = [];
7
+ const rev = (catalogue, id) => {
8
+ const e = current.get(catalogue)?.get(id);
9
+ return e ? revision(e.catalogue, e.id, e.columns) : undefined;
10
+ };
11
+ for (const d of s.deltas) {
12
+ for (const e of d.added)
13
+ out.push({ element: elementKey(e.catalogue, e.id), operation: "ADDED", target: revision(e.catalogue, e.id, e.columns) });
14
+ for (const e of d.modified) {
15
+ const base = rev(e.catalogue, e.id);
16
+ out.push({ element: elementKey(e.catalogue, e.id), operation: "MODIFIED", ...(base ? { base } : {}), target: revision(e.catalogue, e.id, e.columns) });
17
+ }
18
+ for (const r of d.removed) {
19
+ const base = rev(r.catalogue, r.id);
20
+ out.push({ element: elementKey(r.catalogue, r.id), operation: "REMOVED", ...(base ? { base } : {}) });
21
+ }
22
+ }
23
+ return out.sort((a, b) => a.element.localeCompare(b.element));
24
+ }
25
+ export function parseAppliedRecord(text) {
26
+ try {
27
+ const v = parseDocument(text).toJS();
28
+ if (!v || typeof v.change !== "string" || !Array.isArray(v.effects))
29
+ return null;
30
+ return { change: v.change, baseCommit: String(v.base_commit ?? ""), effects: v.effects };
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ //# sourceMappingURL=effects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effects.js","sourceRoot":"","sources":["../../../src/lib/structure/effects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { runCatalogueBuild } from "../catalogue/build.js";
2
+ import type { Finding } from "../catalogue/findings.js";
3
+ export declare const REPORT_FILE = "extraction-report.md";
4
+ export interface Extraction {
5
+ /** Why nothing was extracted, or null. */
6
+ readonly refused: string | null;
7
+ /** Partition -> file text. */
8
+ readonly files: ReadonlyMap<string, string>;
9
+ /** Catalogue -> element count, structure catalogues with elements only. */
10
+ readonly counts: ReadonlyMap<string, number>;
11
+ readonly loss: readonly string[];
12
+ readonly extraction: readonly Finding[];
13
+ readonly report: string;
14
+ }
15
+ /** Extract the model. Writes nothing; `writeExtraction` writes it. `build` is the catalogue build, replaceable in tests. */
16
+ export declare function extractStructure(sddPath: string, build?: typeof runCatalogueBuild): Promise<Extraction>;
17
+ /** Write the partition files and the report under openspec/structure/. Returns the paths written. */
18
+ export declare function writeExtraction(sddPath: string, x: Extraction): Promise<string[]>;
19
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../src/lib/structure/extract.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAMxD,eAAO,MAAM,WAAW,yBAAyB,CAAC;AAKlD,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,OAAO,EAAE,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAoDD,4HAA4H;AAC5H,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,OAAO,iBAAqC,GAAG,OAAO,CAAC,UAAU,CAAC,CAoChI;AAED,qGAAqG;AACrG,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWvF"}
@@ -0,0 +1,123 @@
1
+ // An SDD's first structural model, extracted from its sources (M-ADR 0230 §5.3, §6 step 1, Proposed). The builders
2
+ // already parse the sources: the diagrams, data models, service tables, configuration, traceability manifests,
3
+ // feature trees and groundings. So extraction builds the catalogue from the sources into a scratch directory, and
4
+ // projects the model it implies (modelFromRows). It writes the partition files and an extraction report beside
5
+ // them, and touches no source file. From the moment openspec/structure/ exists, committed or not, the catalogue is
6
+ // built from the model, so writing it is the admission: review the extraction against the code first, with --dry-run
7
+ // (which writes nothing) or in a scratch copy, and keep the originals until that review is complete.
8
+ import { execFileSync } from "node:child_process";
9
+ import { mkdir, mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { runCatalogueBuild } from "../catalogue/build.js";
13
+ import { readCatalogueCsv } from "../catalogue/csv-reader.js";
14
+ import { loadCanonicalSchema } from "../catalogue/schema-loader.js";
15
+ import { modelFileText, modelFromRows } from "./catalogue.js";
16
+ import { STRUCTURE_DIR } from "./model.js";
17
+ import { STRUCTURE } from "./view.js";
18
+ export const REPORT_FILE = "extraction-report.md";
19
+ /** Derived path columns of structure catalogues: the source files the elements were read from. */
20
+ const SOURCE_PATH_COLUMNS = ["doc_path", "diagram_path", "spec_path"];
21
+ function head(sddPath) {
22
+ try {
23
+ const sha = execFileSync("git", ["-C", sddPath, "rev-parse", "--short", "HEAD"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
24
+ const dirty = execFileSync("git", ["-C", sddPath, "status", "--porcelain"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() !== "";
25
+ return dirty ? `${sha}, with uncommitted changes` : sha;
26
+ }
27
+ catch {
28
+ return "not a git repository";
29
+ }
30
+ }
31
+ function report(sddId, at, schemaVersion, x, sources, otherFindings) {
32
+ const lines = [
33
+ "# Structural model extraction",
34
+ "",
35
+ `Extracted by \`sdd-cli migrate structure\` from ${sddId}'s sources at ${at}, against catalogue schema ${schemaVersion} (M-ADR 0230, Proposed).`,
36
+ "",
37
+ "**Writing this model admits it as the baseline** (M-ADR 0230 §6 step 1). From the moment `openspec/structure/` exists, committed or not, it is the record of structure: the catalogue is built from it, and the structural-model check asks every open change for its structural deltas or a `none.yaml` (report only while M-ADR 0230 is Proposed). Review it against the code before it is written (`sdd-cli migrate structure --dry-run` prints this report and writes nothing), and keep the sources listed below until that review is complete; nothing here changed them. Removing `openspec/structure/` withdraws a model written too soon. After admission, structure changes only through a change's structural deltas, and the diagrams and tables are generated by `sdd-cli structure render`.",
38
+ "",
39
+ "## Elements",
40
+ "",
41
+ "| Catalogue | Elements |",
42
+ "| --- | --- |",
43
+ ...[...x.counts].map(([c, n]) => `| ${c} | ${n} |`),
44
+ "",
45
+ `Partitions: ${[...x.files.keys()].map((p) => `\`${p}\``).join(", ") || "none"}.`,
46
+ "",
47
+ "## What the extraction could not carry",
48
+ "",
49
+ ];
50
+ if (x.loss.length === 0 && x.extraction.length === 0)
51
+ lines.push("Nothing: every structural fact the sources state that a builder reads is in the model.", "");
52
+ for (const l of x.loss)
53
+ lines.push(`- ${l}`);
54
+ for (const f of x.extraction)
55
+ lines.push(`- ${f.summary}`);
56
+ if (x.loss.length + x.extraction.length > 0)
57
+ lines.push("");
58
+ lines.push("Prose, and anything a builder does not read, stays in the original files only. A diagram's layout and styling are not structure, and the generated views do not reproduce them.", "", "## Sources the elements were read from", "", ...(sources.length > 0 ? sources.map((s) => `- \`${s}\``) : ["- (no element names its source file)"]), "", "## Other findings of the build", "", ...(otherFindings.size > 0 ? [...otherFindings].map(([c, n]) => `- ${c}: ${n}`) : ["- none"]), "", "These are the build's findings about the sources. They are reported by `sdd-cli catalogue build` as before, and extraction does not change them.", "");
59
+ return lines.join("\n");
60
+ }
61
+ /** Extract the model. Writes nothing; `writeExtraction` writes it. `build` is the catalogue build, replaceable in tests. */
62
+ export async function extractStructure(sddPath, build = runCatalogueBuild) {
63
+ const none = { files: new Map(), counts: new Map(), loss: [], extraction: [], report: "" };
64
+ try {
65
+ await readdir(join(sddPath, STRUCTURE_DIR));
66
+ return { ...none, refused: `${STRUCTURE_DIR}/ already exists; an SDD's first model is extracted once, and after that structure changes through a change's structural deltas` };
67
+ }
68
+ catch {
69
+ // no model yet
70
+ }
71
+ const schema = await loadCanonicalSchema();
72
+ const scratch = await mkdtemp(join(tmpdir(), "sdd-migrate-structure-"));
73
+ try {
74
+ const built = await build({ sddPath, outDir: scratch }, () => undefined);
75
+ if (built.exitCode !== 0) {
76
+ return { ...none, refused: `the sources do not build cleanly (sdd-cli catalogue build exits ${built.exitCode}: ${built.internalFkPass ? "" : "dangling references, "}${built.pkConflicts} key conflict(s)); a model extracted from them would carry the same faults. Fix the build first` };
77
+ }
78
+ const rows = new Map();
79
+ for (const c of schema.catalogues) {
80
+ if (c.sourceOfTruth !== STRUCTURE)
81
+ continue;
82
+ rows.set(c.name, (await readCatalogueCsv(join(scratch, `${c.name}.csv`)).catch(() => ({ rows: [] }))).rows);
83
+ }
84
+ const loss = [];
85
+ const model = modelFromRows(schema, rows, { platform: built.profile === "platform-profile", sddId: built.sddId }, loss);
86
+ const files = new Map([...model].sort(([a], [b]) => a.localeCompare(b)).map(([p, els]) => [p, modelFileText(p, els)]));
87
+ const counts = new Map();
88
+ for (const els of model.values())
89
+ for (const [c, list] of Object.entries(els))
90
+ counts.set(c, (counts.get(c) ?? 0) + list.length);
91
+ const sortedCounts = new Map([...counts].sort(([a], [b]) => a.localeCompare(b)));
92
+ const sources = new Set();
93
+ for (const list of rows.values())
94
+ for (const r of list)
95
+ for (const col of SOURCE_PATH_COLUMNS)
96
+ if (r[col])
97
+ sources.add(String(r[col]));
98
+ const extraction = built.findings.filter((f) => f.category === "extraction");
99
+ const other = new Map();
100
+ for (const f of built.findings)
101
+ if (f.category !== "extraction")
102
+ other.set(String(f.category), (other.get(String(f.category)) ?? 0) + 1);
103
+ const x = { files, counts: sortedCounts, loss, extraction };
104
+ return { ...x, refused: null, report: report(built.sddId, head(sddPath), schema.schemaVersion, x, [...sources].sort(), new Map([...other].sort())) };
105
+ }
106
+ finally {
107
+ await rm(scratch, { recursive: true, force: true });
108
+ }
109
+ }
110
+ /** Write the partition files and the report under openspec/structure/. Returns the paths written. */
111
+ export async function writeExtraction(sddPath, x) {
112
+ const dir = join(sddPath, STRUCTURE_DIR);
113
+ await mkdir(dir, { recursive: true });
114
+ const written = [];
115
+ for (const [p, text] of x.files) {
116
+ await writeFile(join(dir, `${p}.yaml`), text, "utf8");
117
+ written.push(join(STRUCTURE_DIR, `${p}.yaml`));
118
+ }
119
+ await writeFile(join(dir, REPORT_FILE), x.report, "utf8");
120
+ written.push(join(STRUCTURE_DIR, REPORT_FILE));
121
+ return written;
122
+ }
123
+ //# sourceMappingURL=extract.js.map