@sdd-method/sdd-cli 0.19.0 → 0.21.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.
Files changed (31) hide show
  1. package/dist/lib/catalogue/build.d.ts.map +1 -1
  2. package/dist/lib/catalogue/build.js +55 -4
  3. package/dist/lib/catalogue/build.js.map +1 -1
  4. package/dist/lib/catalogue/builders/c4-containers.d.ts.map +1 -1
  5. package/dist/lib/catalogue/builders/c4-containers.js +15 -0
  6. package/dist/lib/catalogue/builders/c4-containers.js.map +1 -1
  7. package/dist/lib/catalogue/builders/capability-spec-walker.d.ts +55 -0
  8. package/dist/lib/catalogue/builders/capability-spec-walker.d.ts.map +1 -0
  9. package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
  10. package/dist/lib/catalogue/builders/capability-spec-walker.js.map +1 -0
  11. package/dist/lib/catalogue/builders/capability-spec.d.ts +51 -0
  12. package/dist/lib/catalogue/builders/capability-spec.d.ts.map +1 -0
  13. package/dist/lib/catalogue/builders/capability-spec.js +276 -0
  14. package/dist/lib/catalogue/builders/capability-spec.js.map +1 -0
  15. package/dist/lib/catalogue/builders/contracts.d.ts.map +1 -1
  16. package/dist/lib/catalogue/builders/contracts.js +88 -41
  17. package/dist/lib/catalogue/builders/contracts.js.map +1 -1
  18. package/dist/lib/catalogue/builders/index.d.ts +4 -0
  19. package/dist/lib/catalogue/builders/index.d.ts.map +1 -1
  20. package/dist/lib/catalogue/builders/index.js +2 -0
  21. package/dist/lib/catalogue/builders/index.js.map +1 -1
  22. package/dist/lib/catalogue/builders/service-contract-consumes.d.ts +24 -0
  23. package/dist/lib/catalogue/builders/service-contract-consumes.d.ts.map +1 -0
  24. package/dist/lib/catalogue/builders/service-contract-consumes.js +173 -0
  25. package/dist/lib/catalogue/builders/service-contract-consumes.js.map +1 -0
  26. package/dist/lib/catalogue/canonical-schema.yaml +5 -5
  27. package/dist/lib/catalogue/service-contract-aliases-loader.d.ts +60 -0
  28. package/dist/lib/catalogue/service-contract-aliases-loader.d.ts.map +1 -0
  29. package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
  30. package/dist/lib/catalogue/service-contract-aliases-loader.js.map +1 -0
  31. package/package.json +2 -2
@@ -0,0 +1,173 @@
1
+ // service_contract consumes edges — emits one row per (service,
2
+ // contract, consumes) triple derived from each service's
3
+ // `repos/<service>/docs/service-architecture.md` External Interfaces
4
+ // table (Outbound rows).
5
+ //
6
+ // Resolution path:
7
+ // 1. Read the External Interfaces section of the service-architecture
8
+ // markdown. Pipe-delimited table; Direction column = "Outbound"
9
+ // (case-insensitive).
10
+ // 2. For each Outbound row, lookup the Interface column text against
11
+ // the adopter's `.sdd/service-contract-aliases.yaml` (interface_aliases
12
+ // exact match, then interface_contains substring) to get a target
13
+ // contract id.
14
+ // 3. Fall back to same_product_inference: if the interface matches a
15
+ // generic placeholder (e.g. "Private API Gateway"), substitute
16
+ // `{prefix}` with the caller's prefix (service_id minus known
17
+ // suffix). The resolved value is also a contract id.
18
+ // 4. Targets are contract IDs (not names). The schema marks both
19
+ // service_contract.{service_id,contract_id} as `external: true`
20
+ // (schema 2.8.0) — federated SDDs that lack a local services.csv +
21
+ // contracts.csv emit consumes rows whose FKs resolve at aggregation;
22
+ // connect-mono validates internally first per the fk-validator
23
+ // optimistic-resolve rule.
24
+ //
25
+ // Pattern documented in sdd-method ADR 0147.
26
+ import { readFile, readdir } from "node:fs/promises";
27
+ import { join } from "node:path";
28
+ const REPOS_DIR_REL = "repos";
29
+ const SERVICE_ARCHITECTURE_REL = join("docs", "service-architecture.md");
30
+ const KNOWN_SUFFIXES = ["-api", "-bff", "-ui", "-consumer", "-web", "-service"];
31
+ export async function buildServiceContractConsumes(inputs) {
32
+ const rows = [];
33
+ const seen = new Set();
34
+ if (inputs.aliases === null)
35
+ return rows;
36
+ const useKnownGate = inputs.knownServiceIds.size > 0;
37
+ const repoNames = await listRepoDirs(join(inputs.sddPath, REPOS_DIR_REL));
38
+ for (const serviceId of repoNames) {
39
+ if (useKnownGate && !inputs.knownServiceIds.has(serviceId))
40
+ continue;
41
+ if (inputs.aliases.skipCallers.has(serviceId))
42
+ continue;
43
+ const archPath = join(inputs.sddPath, REPOS_DIR_REL, serviceId, SERVICE_ARCHITECTURE_REL);
44
+ let text;
45
+ try {
46
+ text = await readFile(archPath, "utf-8");
47
+ }
48
+ catch (err) {
49
+ const code = err.code;
50
+ if (code === "ENOENT")
51
+ continue;
52
+ throw err;
53
+ }
54
+ const outbound = parseOutboundInterfaces(text);
55
+ for (const iface of outbound) {
56
+ const contractId = resolveContractId(serviceId, iface.interfaceText, inputs.aliases);
57
+ if (contractId === null)
58
+ continue;
59
+ const id = `${serviceId}__${contractId}__consumes`;
60
+ if (seen.has(id))
61
+ continue;
62
+ seen.add(id);
63
+ rows.push({
64
+ id,
65
+ service_id: serviceId,
66
+ contract_id: contractId,
67
+ role: "consumes",
68
+ });
69
+ }
70
+ }
71
+ return rows;
72
+ }
73
+ function resolveContractId(callerServiceId, interfaceText, aliases) {
74
+ const key = interfaceText.toLowerCase().trim();
75
+ // 1. Direct (exact) alias → contract id.
76
+ const direct = aliases.interfaceAliases.get(key);
77
+ if (direct !== undefined)
78
+ return direct;
79
+ // 2. Substring (contains) alias — longest-first.
80
+ for (const [substr, target] of aliases.interfaceContains) {
81
+ if (key.includes(substr))
82
+ return target;
83
+ }
84
+ // 3. Same-product template inference.
85
+ const template = aliases.sameProductTemplates.get(key);
86
+ if (template !== undefined) {
87
+ const prefix = derivePrefix(callerServiceId);
88
+ return template.replace(/\{prefix\}/g, prefix);
89
+ }
90
+ return null;
91
+ }
92
+ function derivePrefix(serviceId) {
93
+ for (const suffix of KNOWN_SUFFIXES) {
94
+ if (serviceId.endsWith(suffix)) {
95
+ return serviceId.slice(0, -suffix.length);
96
+ }
97
+ }
98
+ return serviceId;
99
+ }
100
+ export function parseOutboundInterfaces(markdown) {
101
+ const out = [];
102
+ const sectionRegex = /^##\s+External Interfaces\s*$/im;
103
+ const match = sectionRegex.exec(markdown);
104
+ if (match === null)
105
+ return out;
106
+ // Slice from end of heading to start of next ## heading (or EOF).
107
+ const rest = markdown.slice(match.index + match[0].length);
108
+ const nextHeading = /^##\s+/m.exec(rest);
109
+ const section = nextHeading !== null ? rest.slice(0, nextHeading.index) : rest;
110
+ const lines = section.split(/\r?\n/);
111
+ let headerSeen = false;
112
+ let dividerSeen = false;
113
+ let cols = null;
114
+ for (const raw of lines) {
115
+ const line = raw.trim();
116
+ if (line === "" || !line.startsWith("|")) {
117
+ if (headerSeen)
118
+ break;
119
+ continue;
120
+ }
121
+ const cells = splitRow(line);
122
+ if (!headerSeen) {
123
+ const interfaceIdx = cells.findIndex((c) => c.toLowerCase() === "interface");
124
+ const directionIdx = cells.findIndex((c) => c.toLowerCase() === "direction");
125
+ if (interfaceIdx === -1 || directionIdx === -1)
126
+ continue;
127
+ cols = { iface: interfaceIdx, direction: directionIdx };
128
+ headerSeen = true;
129
+ continue;
130
+ }
131
+ if (!dividerSeen) {
132
+ // Divider row: cells of dashes (e.g. "---")
133
+ if (cells.every((c) => /^:?-+:?$/.test(c.trim()))) {
134
+ dividerSeen = true;
135
+ continue;
136
+ }
137
+ // Some authors omit dividers — accept and fall through.
138
+ dividerSeen = true;
139
+ }
140
+ if (cols === null)
141
+ continue;
142
+ const direction = (cells[cols.direction] ?? "").trim().toLowerCase();
143
+ if (direction !== "outbound")
144
+ continue;
145
+ const ifaceText = (cells[cols.iface] ?? "").trim();
146
+ if (ifaceText === "")
147
+ continue;
148
+ out.push({ interfaceText: ifaceText });
149
+ }
150
+ return out;
151
+ }
152
+ function splitRow(line) {
153
+ // Drop leading + trailing | then split on internal |.
154
+ let s = line;
155
+ if (s.startsWith("|"))
156
+ s = s.slice(1);
157
+ if (s.endsWith("|"))
158
+ s = s.slice(0, -1);
159
+ return s.split("|").map((c) => c.trim());
160
+ }
161
+ async function listRepoDirs(reposRoot) {
162
+ try {
163
+ const entries = await readdir(reposRoot, { withFileTypes: true });
164
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
165
+ }
166
+ catch (err) {
167
+ const code = err.code;
168
+ if (code === "ENOENT")
169
+ return [];
170
+ throw err;
171
+ }
172
+ }
173
+ //# sourceMappingURL=service-contract-consumes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-contract-consumes.js","sourceRoot":"","sources":["../../../../src/lib/catalogue/builders/service-contract-consumes.ts"],"names":[],"mappings":"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"}
@@ -45,7 +45,7 @@
45
45
  # Builder contract: see docs/method/methods/catalogue-builder-specification.md.
46
46
  # Category taxonomy: see docs/method/methods/product-category-taxonomy.md.
47
47
 
48
- schema_version: "2.6.0"
48
+ schema_version: "2.8.0"
49
49
  schema_owner: "sdd-method"
50
50
  schema_status: "canonical"
51
51
 
@@ -275,7 +275,7 @@ catalogues:
275
275
  - { name: description, type: string, nullable: true }
276
276
  - { name: maturity, type: string, nullable: true, description: "Standard: working | provisional | unknown | absent. Ecosystem-specific values may be added with parser awareness." }
277
277
  - { name: status, type: string, nullable: false, description: "Default 'active'." }
278
- - { name: source, type: string, nullable: false, description: "feature-tree | feature-set | capability-spec | feature-spec | feature-grounding. NOTE: feature-tree-group source removed in 1.3.0 — groups are now first-class in feature_groups.csv. As of schema 2.2.0, source: feature-tree is emitted by the canonical builder for rows derived from feature-tree.md bullets. As of schema 2.5.0, source: feature-grounding is emitted for rows derived from `docs/domains/<dom>/design/<capability>/feature-grounding.md` markdown tables (per ADR 0143). Feature-grounding rows are capability-owned: product_id is empty; capability_id resolves from the file path." }
278
+ - { name: source, type: string, nullable: false, description: "feature-tree | feature-set | capability-spec | feature-spec | feature-grounding. NOTE: feature-tree-group source removed in 1.3.0 — groups are now first-class in feature_groups.csv. As of schema 2.2.0, source: feature-tree is emitted by the canonical builder for rows derived from feature-tree.md bullets. As of schema 2.5.0, source: feature-grounding is emitted for rows derived from `docs/domains/<dom>/design/<capability>/feature-grounding.md` markdown tables (per ADR 0143). As of schema 2.7.0, source: capability-spec is emitted by the canonical builder for rows derived from `## Feature Grounding (code-path trace)` tables in `docs/domains/<dom>/product/<capability>.md` capability spec markdown (per ADR 0145). Feature-grounding and capability-spec rows are both capability-owned: product_id is empty; capability_id resolves from the file path." }
279
279
  - { name: runway, type: string, nullable: true, description: "Optional adopter-owned lifecycle / migration marker. Free string at method level — the schema declares no enum and ships no controlled vocabulary. Adopters MAY declare a per-SDD `feature-runway-vocabulary.yaml` at their SDD root; when present, the canonical builder validates captured values against it and emits a `convention` finding for unknown values. Empty when the adopter does not track migration state. Comparability across SDDs requires ecosystem-level vocabulary alignment as an explicit pre-step. Added in schema 2.2.0." }
280
280
  - { name: source_sdd_id, type: string, nullable: true, description: "Originating SDD slug. Populated by the aggregator per ADR 0134." }
281
281
 
@@ -489,10 +489,10 @@ catalogues:
489
489
 
490
490
  service_contract:
491
491
  primary_key: id
492
- description: "Edge: service exposes or consumes contract."
492
+ description: "Edge: service exposes or consumes contract. Both FKs are external (per ADR 0147): federated SDDs that walk per-repo service-architecture tables emit consumes rows where service_id resolves to the platform-root catalogue's services.csv (post-aggregation) and contract_id resolves to the platform-root contracts.csv. Internal builds (e.g. connect-mono) still optimistically validate locally. Schema 2.8.0."
493
493
  foreign_keys:
494
- - { column: service_id, references: services.id, nullable: false }
495
- - { column: contract_id, references: contracts.id, nullable: false }
494
+ - { column: service_id, references: services.id, nullable: false, external: true }
495
+ - { column: contract_id, references: contracts.id, nullable: false, external: true }
496
496
  columns:
497
497
  - { name: id, type: string, nullable: false, description: "{service_id}__{contract_id}__{role}." }
498
498
  - { name: service_id, type: string, nullable: false }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Loader for the optional `.sdd/service-contract-aliases.yaml` config.
3
+ *
4
+ * Adopters use this map to translate outbound interface display text
5
+ * (as it appears in service-architecture.md External Interfaces tables)
6
+ * into canonical contract names that resolve against contracts.csv.
7
+ *
8
+ * File shape:
9
+ *
10
+ * interface_aliases:
11
+ * # Case-insensitive exact match on the Interface column text.
12
+ * Masternauth service: masternauth-oauth-api
13
+ * CWS: cws-paddington-api
14
+ * Paddington: cws-paddington-api
15
+ *
16
+ * interface_contains:
17
+ * # Case-insensitive substring match. The longest key wins on
18
+ * # ties; ordering in the file otherwise matters only when keys
19
+ * # overlap. Use when adopter interface text has many qualified
20
+ * # variants ("Masternauth API", "Masternauth `/token`", etc.).
21
+ * Masternauth: masternauth-oauth-api
22
+ * Paddington: cws-paddington-api
23
+ *
24
+ * skip_callers:
25
+ * # Repo names to skip entirely (postman collections, smoke tests,
26
+ * # legacy folders inside a federated SDD that aren't runtime
27
+ * # services). Use when the federated SDD lacks a services.csv to
28
+ * # gate emission and consumes rows would otherwise dangle at the
29
+ * # aggregator with unresolved service_id.
30
+ * - postman-tacho
31
+ * - smoke-tests
32
+ *
33
+ * same_product_inference:
34
+ * # Maps generic interface text (case-insensitive exact) to a
35
+ * # contract-name template. `{prefix}` is substituted with the
36
+ * # caller's service_id minus a known suffix (-api, -bff, -ui,
37
+ * # -consumer, -web). The builder then looks up the resolved name
38
+ * # in contracts.csv.
39
+ * Private API: "{prefix}-private-api"
40
+ * Private API Gateway: "{prefix}-private-api"
41
+ *
42
+ * Either block is optional. Empty / missing file = no aliases.
43
+ */
44
+ export interface LoadedServiceContractAliases {
45
+ /** Lowercased interface display text → canonical contract name. */
46
+ readonly interfaceAliases: ReadonlyMap<string, string>;
47
+ /**
48
+ * Lowercased substring → canonical contract name. Sorted longest-first
49
+ * by the loader so resolution picks the most specific match.
50
+ */
51
+ readonly interfaceContains: ReadonlyArray<readonly [string, string]>;
52
+ /** Lowercased interface display text → contract-name template (uses {prefix}). */
53
+ readonly sameProductTemplates: ReadonlyMap<string, string>;
54
+ /** Set of caller (repo) directory names to skip entirely. */
55
+ readonly skipCallers: ReadonlySet<string>;
56
+ /** Path the aliases were loaded from (relative to SDD root). */
57
+ readonly relPath: string;
58
+ }
59
+ export declare function loadServiceContractAliases(sddPath: string): Promise<LoadedServiceContractAliases | null>;
60
+ //# sourceMappingURL=service-contract-aliases-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-contract-aliases-loader.d.ts","sourceRoot":"","sources":["../../../src/lib/catalogue/service-contract-aliases-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAQH,MAAM,WAAW,4BAA4B;IAC3C,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrE,kFAAkF;IAClF,QAAQ,CAAC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3D,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AASD,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAyB9C"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Loader for the optional `.sdd/service-contract-aliases.yaml` config.
3
+ *
4
+ * Adopters use this map to translate outbound interface display text
5
+ * (as it appears in service-architecture.md External Interfaces tables)
6
+ * into canonical contract names that resolve against contracts.csv.
7
+ *
8
+ * File shape:
9
+ *
10
+ * interface_aliases:
11
+ * # Case-insensitive exact match on the Interface column text.
12
+ * Masternauth service: masternauth-oauth-api
13
+ * CWS: cws-paddington-api
14
+ * Paddington: cws-paddington-api
15
+ *
16
+ * interface_contains:
17
+ * # Case-insensitive substring match. The longest key wins on
18
+ * # ties; ordering in the file otherwise matters only when keys
19
+ * # overlap. Use when adopter interface text has many qualified
20
+ * # variants ("Masternauth API", "Masternauth `/token`", etc.).
21
+ * Masternauth: masternauth-oauth-api
22
+ * Paddington: cws-paddington-api
23
+ *
24
+ * skip_callers:
25
+ * # Repo names to skip entirely (postman collections, smoke tests,
26
+ * # legacy folders inside a federated SDD that aren't runtime
27
+ * # services). Use when the federated SDD lacks a services.csv to
28
+ * # gate emission and consumes rows would otherwise dangle at the
29
+ * # aggregator with unresolved service_id.
30
+ * - postman-tacho
31
+ * - smoke-tests
32
+ *
33
+ * same_product_inference:
34
+ * # Maps generic interface text (case-insensitive exact) to a
35
+ * # contract-name template. `{prefix}` is substituted with the
36
+ * # caller's service_id minus a known suffix (-api, -bff, -ui,
37
+ * # -consumer, -web). The builder then looks up the resolved name
38
+ * # in contracts.csv.
39
+ * Private API: "{prefix}-private-api"
40
+ * Private API Gateway: "{prefix}-private-api"
41
+ *
42
+ * Either block is optional. Empty / missing file = no aliases.
43
+ */
44
+ import { readFile } from "node:fs/promises";
45
+ import { join } from "node:path";
46
+ import { parse as parseYaml } from "yaml";
47
+ const ALIASES_FILENAME = join(".sdd", "service-contract-aliases.yaml");
48
+ export async function loadServiceContractAliases(sddPath) {
49
+ const full = join(sddPath, ALIASES_FILENAME);
50
+ let text;
51
+ try {
52
+ text = await readFile(full, "utf-8");
53
+ }
54
+ catch (err) {
55
+ const code = err.code;
56
+ if (code === "ENOENT")
57
+ return null;
58
+ throw err;
59
+ }
60
+ const raw = parseYaml(text);
61
+ if (raw === null || typeof raw !== "object") {
62
+ throw new Error(`${ALIASES_FILENAME}: expected a mapping at the document root`);
63
+ }
64
+ return {
65
+ interfaceAliases: toLowerKeyMap(raw.interface_aliases),
66
+ interfaceContains: toContainsList(raw.interface_contains),
67
+ sameProductTemplates: toLowerKeyMap(raw.same_product_inference),
68
+ skipCallers: toStringSet(raw.skip_callers),
69
+ relPath: ALIASES_FILENAME,
70
+ };
71
+ }
72
+ function toStringSet(raw) {
73
+ const out = new Set();
74
+ if (Array.isArray(raw)) {
75
+ for (const item of raw) {
76
+ if (typeof item === "string" && item !== "")
77
+ out.add(item);
78
+ }
79
+ }
80
+ return out;
81
+ }
82
+ function toContainsList(raw) {
83
+ const out = [];
84
+ if (raw === null || raw === undefined)
85
+ return out;
86
+ if (typeof raw !== "object" || Array.isArray(raw))
87
+ return out;
88
+ for (const [k, v] of Object.entries(raw)) {
89
+ if (typeof v === "string" && v !== "") {
90
+ out.push([k.toLowerCase().trim(), v]);
91
+ }
92
+ }
93
+ // Sort longest-key-first so the most specific substring wins.
94
+ out.sort((a, b) => b[0].length - a[0].length);
95
+ return out;
96
+ }
97
+ function toLowerKeyMap(raw) {
98
+ const out = new Map();
99
+ if (raw === null || raw === undefined)
100
+ return out;
101
+ if (typeof raw !== "object" || Array.isArray(raw))
102
+ return out;
103
+ for (const [k, v] of Object.entries(raw)) {
104
+ if (typeof v === "string" && v !== "")
105
+ out.set(k.toLowerCase().trim(), v);
106
+ }
107
+ return out;
108
+ }
109
+ //# sourceMappingURL=service-contract-aliases-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-contract-aliases-loader.js","sourceRoot":"","sources":["../../../src/lib/catalogue/service-contract-aliases-loader.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sdd-method/sdd-cli",
3
- "version": "0.19.0",
4
- "description": "Method-layer CLI for SDD repository lifecycle — init, sync, validate, list, plan, generate-claude-md, mcp serve, catalogue {build,aggregate,validate,version}. v0.19.0 ships catalogue schema 2.6.0 (lockstep with sdd-method): adds optional `sub_integrations:` array on integration-manifest.yaml per ADR 0144. Multi-provider integration monoliths (modernisation-in-flight or intentional permanent gateways brokering N providers) can declare each provider; the integrations.ts builder emits one integrations.csv row per sub_integration with parent_product_id = umbrella SDD's integrationId. Uses the existing parent_product_id column — no new schema columns. Backward compatible (optional field); SDDs without sub_integrations emit identical output to pre-2.6.0. Originating motivation: connect's sdd-camera-platform-legacy covering Surfsight v2 + VisionTrack v1/v2 + SureCam in one Spring-Boot codebase. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.18.0 ships catalogue schema 2.5.0 (lockstep with sdd-method): adds feature-grounding source per ADR 0143. Canonical builder walks `docs/domains/<dom>/design/<capability>/feature-grounding.md` (platform-profile) and `docs/design/integration/feature-grounding.md` (integration-product), parses the markdown table form, and emits features.csv rows (source: feature-grounding, capability-owned) plus capability_feature / feature_repo / feature_service edges derived from the table columns. Optional per-SDD `.sdd/feature-grounding-aliases.yaml` translates display names (class names, file basenames) to canonical service / repo ids. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.17.0 enables multi-level federation: organisation-level SDDs can now aggregate from organisation-level children (e.g. CXS aggregating sdd-mining, sdd-latam) via optional level: organisation field on siblings, skipping manifest requirements for pure orchestration repos. v0.16.0 adds organisation-profile support for system-of-systems aggregators (e.g. CXS aggregating sdd-connect, sdd-mining, sdd-latam), closing the gap for organisation-level SDD manifests. v0.15.0 ships catalogue schema 2.4.0 (lockstep with sdd-method): adds feature_contract derived edge (forward: capability_feature ⋈ capability_contract; reverse: feature_service ⋈ service_contract) closing the Capability → Feature → Contract chain pre-implementation, plus the new DomainModel entity per ADR 0142 with domain_models / capability_domain_model / feature_domain_model catalogues sourced from a new traceability.domain_model manifest field (forward) or docs/domains/*/architecture/data-model.mermaid.md file-system scan (reverse). Platform-profile only for domain_models; the metamodel now also documents non-catalogued method artefacts (work orders, discovery stories, execution stories, traceability manifests) so it's a complete reference for the forward SDD method's artefact set. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.14.1 patches v0.14.0's domains-builder fix: products-builder now also clears domain_id for non-platform-profile rows even when manifest.domainId is declared. v0.14.0 stopped emitting umbrella domain rows but products.domain_id (and the applications.csv filtered view) still pointed at them, dangling at FK validation. Per ADR 0136 + schema description for products.domain_id, app/integration/support products do not have a primary domain — the builder now honours that statement regardless of manifest declaration, and emits an inferred finding when a manifest-declared value is overridden. v0.14.0 ships two post-v0.9.0-release fixes surfaced by the connect-cohort sync: (1) sync now enforces minimum_sdd_cli_version declared by bundle manifests — previously the orchestration check existed only in the shell sync script and was a no-op when adopters used `sdd-cli sync`, leaving the safety net for stale-CLI scenarios silently absent; (2) per ADR 0136 (App and Integration Products Do Not Belong to a Primary Domain), the domains.ts catalogue builder now skips row emission for non-platform-profile SDDs — application-product and integration-product SDDs that have a docs/domains/<umbrella>/ subdirectory as a structural-organisation convention no longer emit a member-declaration row that collides on PK at aggregation against the canonical platform-profile contribution. v0.13.2 fixed the aggregator's sibling-vendored-schema cross-check to use semver range satisfaction against SCHEMA_COMPAT.catalogue_schema (^2.0.0) instead of strict equality — siblings vendored at lower minor versions inside the same major are now accepted, unblocking aggregation across federated cohorts where the canonical schema is ahead of the bundled schema. v0.13.0 ships the feature-tree.md bullet parser per sdd-method schema 2.2.0 + plan extract-feature-tree-bullets.md: application-product builders now emit features.csv rows with source: \"feature-tree\" from per-app feature-tree.md bullets, populating the previously-empty edge catalogues (persona_feature, journey_feature) for portfolio monoliths whose feature inventory lives in the bullet form. Adds the optional runway column on features.csv with adopter-owned controlled-vocabulary validation via feature-runway-vocabulary.yaml at the SDD root. v0.12.2 dedupes equivalent unmarked adopter PreToolUse entries on settings.json upsert. v0.12.0 extends the integration profileType enum to 11 values (per sdd-method ADR 0137) and fixes the init→sync conflict. v0.11.0 shipped the canonical catalogue surface per ADR 0134 + schema 2.1.0.",
3
+ "version": "0.21.0",
4
+ "description": "Method-layer CLI for SDD repository lifecycle — init, sync, validate, list, plan, generate-claude-md, mcp serve, catalogue {build,aggregate,validate,version}. v0.21.0 ships catalogue schema 2.8.0 closing the long-tail contract-consumer mapping gap (K-03a). The contracts builder now also walks the platform-profile layout `docs/domains/<dom>/contracts/{openapi,proto,avro}/<file>.<ext>` documented in reverse/workflows/contract.md, in addition to the canonical `contracts/<subdir>/<dom>/<file>.<ext>` layout — backward-compatible additive change for federated SDDs that don't carry per-domain contracts. Adds a new buildServiceContractConsumes builder that parses each service's `repos/<service>/docs/service-architecture.md` External Interfaces table, reads Outbound rows, and emits `service_contract` rows with role=consumes via an adopter-supplied `.sdd/service-contract-aliases.yaml` (case-insensitive interface_aliases + interface_contains substring tier + same_product_inference templates with `{prefix}` substitution). Schema 2.8.0 marks service_contract.contract_id as external:true so federated SDDs can emit consumes rows pointing to platform-root contracts; aggregator validates the merged catalogue. Resolution failures become convention findings, not FK-invalid rows. Pattern documented in sdd-method ADR 0147. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.19.0 ships catalogue schema 2.6.0 (lockstep with sdd-method): adds optional `sub_integrations:` array on integration-manifest.yaml per ADR 0144. Multi-provider integration monoliths (modernisation-in-flight or intentional permanent gateways brokering N providers) can declare each provider; the integrations.ts builder emits one integrations.csv row per sub_integration with parent_product_id = umbrella SDD's integrationId. Uses the existing parent_product_id column — no new schema columns. Backward compatible (optional field); SDDs without sub_integrations emit identical output to pre-2.6.0. Originating motivation: connect's sdd-camera-platform-legacy covering Surfsight v2 + VisionTrack v1/v2 + SureCam in one Spring-Boot codebase. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.18.0 ships catalogue schema 2.5.0 (lockstep with sdd-method): adds feature-grounding source per ADR 0143. Canonical builder walks `docs/domains/<dom>/design/<capability>/feature-grounding.md` (platform-profile) and `docs/design/integration/feature-grounding.md` (integration-product), parses the markdown table form, and emits features.csv rows (source: feature-grounding, capability-owned) plus capability_feature / feature_repo / feature_service edges derived from the table columns. Optional per-SDD `.sdd/feature-grounding-aliases.yaml` translates display names (class names, file basenames) to canonical service / repo ids. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.17.0 enables multi-level federation: organisation-level SDDs can now aggregate from organisation-level children (e.g. CXS aggregating sdd-mining, sdd-latam) via optional level: organisation field on siblings, skipping manifest requirements for pure orchestration repos. v0.16.0 adds organisation-profile support for system-of-systems aggregators (e.g. CXS aggregating sdd-connect, sdd-mining, sdd-latam), closing the gap for organisation-level SDD manifests. v0.15.0 ships catalogue schema 2.4.0 (lockstep with sdd-method): adds feature_contract derived edge (forward: capability_feature ⋈ capability_contract; reverse: feature_service ⋈ service_contract) closing the Capability → Feature → Contract chain pre-implementation, plus the new DomainModel entity per ADR 0142 with domain_models / capability_domain_model / feature_domain_model catalogues sourced from a new traceability.domain_model manifest field (forward) or docs/domains/*/architecture/data-model.mermaid.md file-system scan (reverse). Platform-profile only for domain_models; the metamodel now also documents non-catalogued method artefacts (work orders, discovery stories, execution stories, traceability manifests) so it's a complete reference for the forward SDD method's artefact set. SCHEMA_COMPAT unchanged (still ^2.0.0). v0.14.1 patches v0.14.0's domains-builder fix: products-builder now also clears domain_id for non-platform-profile rows even when manifest.domainId is declared. v0.14.0 stopped emitting umbrella domain rows but products.domain_id (and the applications.csv filtered view) still pointed at them, dangling at FK validation. Per ADR 0136 + schema description for products.domain_id, app/integration/support products do not have a primary domain — the builder now honours that statement regardless of manifest declaration, and emits an inferred finding when a manifest-declared value is overridden. v0.14.0 ships two post-v0.9.0-release fixes surfaced by the connect-cohort sync: (1) sync now enforces minimum_sdd_cli_version declared by bundle manifests — previously the orchestration check existed only in the shell sync script and was a no-op when adopters used `sdd-cli sync`, leaving the safety net for stale-CLI scenarios silently absent; (2) per ADR 0136 (App and Integration Products Do Not Belong to a Primary Domain), the domains.ts catalogue builder now skips row emission for non-platform-profile SDDs — application-product and integration-product SDDs that have a docs/domains/<umbrella>/ subdirectory as a structural-organisation convention no longer emit a member-declaration row that collides on PK at aggregation against the canonical platform-profile contribution. v0.13.2 fixed the aggregator's sibling-vendored-schema cross-check to use semver range satisfaction against SCHEMA_COMPAT.catalogue_schema (^2.0.0) instead of strict equality — siblings vendored at lower minor versions inside the same major are now accepted, unblocking aggregation across federated cohorts where the canonical schema is ahead of the bundled schema. v0.13.0 ships the feature-tree.md bullet parser per sdd-method schema 2.2.0 + plan extract-feature-tree-bullets.md: application-product builders now emit features.csv rows with source: \"feature-tree\" from per-app feature-tree.md bullets, populating the previously-empty edge catalogues (persona_feature, journey_feature) for portfolio monoliths whose feature inventory lives in the bullet form. Adds the optional runway column on features.csv with adopter-owned controlled-vocabulary validation via feature-runway-vocabulary.yaml at the SDD root. v0.12.2 dedupes equivalent unmarked adopter PreToolUse entries on settings.json upsert. v0.12.0 extends the integration profileType enum to 11 values (per sdd-method ADR 0137) and fixes the init→sync conflict. v0.11.0 shipped the canonical catalogue surface per ADR 0134 + schema 2.1.0.",
5
5
  "keywords": [
6
6
  "sdd-method",
7
7
  "sdd",