@sdd-method/maintain-cli 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/NOTICE +17 -0
- package/README.md +6 -2
- package/dist/lib/adr-origin.js +59 -0
- package/dist/lib/agent-harness.js +28 -0
- package/dist/lib/briefing/index.js +13 -0
- package/dist/lib/briefing/render.js +123 -0
- package/dist/lib/briefing/variables.js +92 -0
- package/dist/lib/bundle.js +123 -0
- package/dist/lib/catalogue/aggregate.js +2404 -0
- package/dist/lib/catalogue/aggregation-manifest-loader.js +173 -0
- package/dist/lib/catalogue/application-archetype/expectations.js +119 -0
- package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
- package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
- package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
- package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
- package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
- package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
- package/dist/lib/catalogue/archetype/resolver.js +103 -0
- package/dist/lib/catalogue/archetype/wireframe.js +418 -0
- package/dist/lib/catalogue/baseline-check.js +151 -0
- package/dist/lib/catalogue/build.js +1502 -0
- package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
- package/dist/lib/catalogue/builders/adrs.js +222 -0
- package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
- package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
- package/dist/lib/catalogue/builders/c4-components.js +229 -0
- package/dist/lib/catalogue/builders/c4-containers.js +240 -0
- package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
- package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
- package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
- package/dist/lib/catalogue/builders/c4-systems.js +98 -0
- package/dist/lib/catalogue/builders/capabilities.js +488 -0
- package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
- package/dist/lib/catalogue/builders/capability-edges.js +256 -0
- package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
- package/dist/lib/catalogue/builders/capability-spec.js +348 -0
- package/dist/lib/catalogue/builders/container-dependency.js +183 -0
- package/dist/lib/catalogue/builders/contracts.js +402 -0
- package/dist/lib/catalogue/builders/data-items.js +344 -0
- package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
- package/dist/lib/catalogue/builders/domain-models.js +175 -0
- package/dist/lib/catalogue/builders/domains.js +119 -0
- package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
- package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
- package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
- package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
- package/dist/lib/catalogue/builders/entity-slug.js +47 -0
- package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
- package/dist/lib/catalogue/builders/event-edges.js +327 -0
- package/dist/lib/catalogue/builders/events.js +168 -0
- package/dist/lib/catalogue/builders/evidence-class.js +43 -0
- package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
- package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
- package/dist/lib/catalogue/builders/feature-contract.js +77 -0
- package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
- package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
- package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
- package/dist/lib/catalogue/builders/feature-groups.js +63 -0
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
- package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
- package/dist/lib/catalogue/builders/features.js +300 -0
- package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
- package/dist/lib/catalogue/builders/glossary.js +204 -0
- package/dist/lib/catalogue/builders/implementations.js +214 -0
- package/dist/lib/catalogue/builders/index.js +64 -0
- package/dist/lib/catalogue/builders/integration-components.js +344 -0
- package/dist/lib/catalogue/builders/integrations.js +181 -0
- package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
- package/dist/lib/catalogue/builders/persona-container.js +133 -0
- package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
- package/dist/lib/catalogue/builders/personas.js +152 -0
- package/dist/lib/catalogue/builders/product-edges.js +106 -0
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
- package/dist/lib/catalogue/builders/products.js +162 -0
- package/dist/lib/catalogue/builders/quality-scores.js +635 -0
- package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
- package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
- package/dist/lib/catalogue/builders/repo-edges.js +340 -0
- package/dist/lib/catalogue/builders/repositories.js +458 -0
- package/dist/lib/catalogue/builders/scaffolds.js +197 -0
- package/dist/lib/catalogue/builders/screen-tree.js +222 -0
- package/dist/lib/catalogue/builders/screens.js +396 -0
- package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
- package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
- package/dist/lib/catalogue/builders/service-contract.js +74 -0
- package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
- package/dist/lib/catalogue/builders/services.js +232 -0
- package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
- package/dist/lib/catalogue/builders/user-journeys.js +253 -0
- package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
- package/dist/lib/catalogue/cli-version.js +10 -0
- package/dist/lib/catalogue/contract-id.js +49 -0
- package/dist/lib/catalogue/contract-path-resolution.js +149 -0
- package/dist/lib/catalogue/coverage-predicates.js +603 -0
- package/dist/lib/catalogue/coverage-render.js +552 -0
- package/dist/lib/catalogue/coverage-run.js +115 -0
- package/dist/lib/catalogue/csv-reader.js +97 -0
- package/dist/lib/catalogue/csv-writer.js +96 -0
- package/dist/lib/catalogue/data-item-coverage.js +142 -0
- package/dist/lib/catalogue/data-plane-coherence.js +77 -0
- package/dist/lib/catalogue/ddl-generator.js +233 -0
- package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
- package/dist/lib/catalogue/engine-validator.js +239 -0
- package/dist/lib/catalogue/ext-id.js +76 -0
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
- package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
- package/dist/lib/catalogue/finding-identity.js +183 -0
- package/dist/lib/catalogue/findings.js +46 -0
- package/dist/lib/catalogue/fk-validator.js +136 -0
- package/dist/lib/catalogue/freshness.js +69 -0
- package/dist/lib/catalogue/gate-checks.js +338 -0
- package/dist/lib/catalogue/graph-projection.js +346 -0
- package/dist/lib/catalogue/graph-run.js +64 -0
- package/dist/lib/catalogue/impact-run.js +228 -0
- package/dist/lib/catalogue/manifest-loader.js +435 -0
- package/dist/lib/catalogue/platform-scope.js +7 -0
- package/dist/lib/catalogue/population-checks.js +87 -0
- package/dist/lib/catalogue/relationship-semantics.js +66 -0
- package/dist/lib/catalogue/reverse-product-quality.js +99 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
- package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
- package/dist/lib/catalogue/scaffold-delta.js +261 -0
- package/dist/lib/catalogue/schema-compat.js +52 -0
- package/dist/lib/catalogue/schema-loader.js +87 -0
- package/dist/lib/catalogue/scores-coverage.js +573 -0
- package/dist/lib/catalogue/screen-validation-loader.js +89 -0
- package/dist/lib/catalogue/service-architecture-parser.js +193 -0
- package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
- package/dist/lib/catalogue/staleness-verdicts.js +345 -0
- package/dist/lib/catalogue/traceability-loader.js +350 -0
- package/dist/lib/catalogue/validate.js +200 -0
- package/dist/lib/catalogue/version.js +18 -0
- package/dist/lib/ci-binding/index.js +220 -0
- package/dist/lib/ci-binding/module.js +204 -0
- package/dist/lib/conformance/binding.js +70 -0
- package/dist/lib/context-pull/index.js +151 -0
- package/dist/lib/context-pull/resolve.js +70 -0
- package/dist/lib/generate-briefing/index.js +77 -0
- package/dist/lib/hooks-seed/copilot.js +167 -0
- package/dist/lib/hooks-seed/entries.js +47 -0
- package/dist/lib/hooks-seed/index.js +17 -0
- package/dist/lib/hooks-seed/lifecycle-mapping.js +28 -0
- package/dist/lib/hooks-seed/merge.js +128 -0
- package/dist/lib/hooks-seed/types.js +20 -0
- package/dist/lib/init/ci-workflow.js +98 -0
- package/dist/lib/list/discover.js +100 -0
- package/dist/lib/list/format.js +84 -0
- package/dist/lib/list/index.js +16 -0
- package/dist/lib/lock-file.js +437 -0
- package/dist/lib/mcp-seed/entries.js +14 -0
- package/dist/lib/mcp-seed/index.js +28 -0
- package/dist/lib/mcp-seed/merge.js +99 -0
- package/dist/lib/mcp-seed/types.js +13 -0
- package/dist/lib/pack/builder.js +303 -0
- package/dist/lib/pack/index.js +87 -0
- package/dist/lib/pack/leakage.js +144 -0
- package/dist/lib/pack/log.js +18 -0
- package/dist/lib/pack/publish-transform.js +233 -0
- package/dist/lib/pack/reference-closure.js +125 -0
- package/dist/lib/pack/schema.js +121 -0
- package/dist/lib/pack/seed.js +182 -0
- package/dist/lib/pack/validate.js +49 -0
- package/dist/lib/package-version.js +92 -0
- package/dist/lib/repo-kind.js +129 -0
- package/dist/lib/scripts.js +7 -0
- package/dist/lib/snapshot/catalogue-diff.js +219 -0
- package/dist/lib/snapshot/manifest.js +206 -0
- package/dist/lib/sync/adr-transitions.js +250 -0
- package/dist/lib/sync/apply.js +91 -0
- package/dist/lib/sync/briefing-merge.js +90 -0
- package/dist/lib/sync/bundle-cache.js +109 -0
- package/dist/lib/sync/bundle-source.js +61 -0
- package/dist/lib/sync/config.js +72 -0
- package/dist/lib/sync/copilot-hooks-merge.js +108 -0
- package/dist/lib/sync/diff.js +251 -0
- package/dist/lib/sync/index.js +818 -0
- package/dist/lib/sync/settings-merge.js +216 -0
- package/dist/lib/sync/yaml-block-merge.js +120 -0
- package/dist/lib/validate/aggregate.js +51 -0
- package/dist/lib/validate/bundle-coherence.js +74 -0
- package/dist/lib/validate/dispatch.js +243 -0
- package/dist/lib/validate/index.js +110 -0
- package/dist/lib/validate/plan.js +54 -0
- package/dist/lib/validate/runner.js +85 -0
- package/dist/lib/validate/yaml-parse.js +112 -0
- package/dist/maintain-bin.js +8 -0
- package/dist/maintain-program.js +91 -0
- package/dist/maintain-version.json +4 -0
- package/dist/verbs/catalogue-maintain.js +110 -0
- package/dist/verbs/catalogue-verify.js +114 -0
- package/dist/verbs/context.js +39 -0
- package/dist/verbs/generate-briefing.js +29 -0
- package/dist/verbs/list.js +45 -0
- package/dist/verbs/pack-build.js +31 -0
- package/dist/verbs/pack-validate.js +17 -0
- package/dist/verbs/path-option.js +25 -0
- package/dist/verbs/render.js +87 -0
- package/dist/verbs/sync.js +110 -0
- package/dist/verbs/validate.js +48 -0
- package/dist/verify-program.js +122 -0
- package/dist/verify-version.json +4 -0
- package/package.json +32 -4
- package/scripts/validate-app-dependencies.sh +664 -0
- package/scripts/validate-app-docs.sh +199 -0
- package/scripts/validate-integration-docs.sh +505 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator for `sdd-cli catalogue graph` — loads the schema, the
|
|
3
|
+
* vendored rubric-coverage-map (the node/edge kind authority; the schema
|
|
4
|
+
* itself does not declare kind), and the catalogues directory, projects
|
|
5
|
+
* the graph (graph-projection.ts holds the derivation rule), writes the
|
|
6
|
+
* JSONL export, and prints the counts + digest so the figures are stated
|
|
7
|
+
* on every run, not discoverable after the fact.
|
|
8
|
+
*
|
|
9
|
+
* The export is regenerated on every invocation and is NOT committed
|
|
10
|
+
* (M-ADR 0205 §4.6): default output is `<catalogues-dir>-graph.jsonl`
|
|
11
|
+
* beside the catalogues directory, which adopter .gitignore entries cover.
|
|
12
|
+
* Tier is explicit, never inferred, same discipline as every other reader.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { writeFile } from "node:fs/promises";
|
|
16
|
+
import { join, resolve } from "node:path";
|
|
17
|
+
import { readCatalogueCsv } from "./csv-reader.js";
|
|
18
|
+
import { loadCanonicalSchema } from "./schema-loader.js";
|
|
19
|
+
import { defaultCataloguesDir, projectGraph, } from "./graph-projection.js";
|
|
20
|
+
import { loadCatalogueKinds, loadRelationshipSemantics } from "./relationship-semantics.js";
|
|
21
|
+
export * from "./relationship-semantics.js";
|
|
22
|
+
// defaultCataloguesDir lives in graph-projection.ts (read-only), re-exported
|
|
23
|
+
// here for existing importers. Read-only verbs must import it from there:
|
|
24
|
+
// importing it from this module drags the graph WRITER into the verify-only
|
|
25
|
+
// package's closure (build-persona-package's canary; the fdf9453 leak).
|
|
26
|
+
export { defaultCataloguesDir };
|
|
27
|
+
export async function runCatalogueGraph(inputs, write) {
|
|
28
|
+
const sddRoot = resolve(inputs.sddPath ?? process.cwd());
|
|
29
|
+
const tier = inputs.tier ?? "member";
|
|
30
|
+
const dir = resolve(sddRoot, inputs.cataloguesDir ?? defaultCataloguesDir(tier));
|
|
31
|
+
const outPath = inputs.out !== undefined
|
|
32
|
+
? resolve(sddRoot, inputs.out)
|
|
33
|
+
: `${dir}-graph.jsonl`;
|
|
34
|
+
if (!existsSync(dir)) {
|
|
35
|
+
write(`catalogues directory not found at ${dir}\n` +
|
|
36
|
+
` Run \`sdd-cli catalogue build\` (or aggregate) first — the graph ` +
|
|
37
|
+
`is derived from built catalogues, never authored.\n`);
|
|
38
|
+
return { exitCode: 2 };
|
|
39
|
+
}
|
|
40
|
+
const schema = await loadCanonicalSchema();
|
|
41
|
+
const kinds = await loadCatalogueKinds();
|
|
42
|
+
const semantics = await loadRelationshipSemantics();
|
|
43
|
+
const rowsByCatalogue = new Map();
|
|
44
|
+
for (const cat of schema.catalogues) {
|
|
45
|
+
const filePath = join(dir, `${cat.name}.csv`);
|
|
46
|
+
if (!existsSync(filePath))
|
|
47
|
+
continue;
|
|
48
|
+
const parsed = await readCatalogueCsv(filePath);
|
|
49
|
+
rowsByCatalogue.set(cat.name, parsed.rows);
|
|
50
|
+
}
|
|
51
|
+
const projection = projectGraph(schema, kinds, rowsByCatalogue, tier, semantics);
|
|
52
|
+
await writeFile(outPath, projection.text, "utf-8");
|
|
53
|
+
const c = projection.counts;
|
|
54
|
+
write(`graph projection (${tier} tier, schema ${schema.schemaVersion})\n`);
|
|
55
|
+
write(` nodes: ${c.nodes}\n`);
|
|
56
|
+
write(` edges: ${c.edges}\n`);
|
|
57
|
+
write(` property records: ${c.properties}\n`);
|
|
58
|
+
write(` omitted (external, resolves at aggregation): ${c.omittedExternal}\n`);
|
|
59
|
+
write(` omitted (unresolved — mirrors FK findings): ${c.omittedUnresolved}\n`);
|
|
60
|
+
write(` digest: sha256:${projection.digest}\n`);
|
|
61
|
+
write(` wrote ${outPath}\n`);
|
|
62
|
+
return { exitCode: 0, counts: c, outPath };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=graph-run.js.map
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transitive impact analysis — `sdd-cli catalogue impact` (M-ADR 0206 §4.6).
|
|
3
|
+
*
|
|
4
|
+
* Impact traverses the graph-projection edge set BY DECLARED SEMANTICS: every
|
|
5
|
+
* relationship carries an impact-flow token in the rubric-coverage-map
|
|
6
|
+
* (M-ADR 0206 §4.1) naming which endpoint RECEIVES impact — `toward-source`
|
|
7
|
+
* (dependency shape: a change at the target propagates to the source),
|
|
8
|
+
* `toward-target` (membership shape: a part's change reaches its whole),
|
|
9
|
+
* `both` (realisation), `none` (xref, lineage records, attributes). One
|
|
10
|
+
* relationship (`service_event`) resolves its flow per row from a named
|
|
11
|
+
* column (§4.2). The closure is the set of nodes impact can reach from the
|
|
12
|
+
* seed along declared propagation directions, computed by recursive SQL
|
|
13
|
+
* (`WITH RECURSIVE`) on the Phase A engine.
|
|
14
|
+
*
|
|
15
|
+
* THE CO-DIRECTIONALITY INVARIANT (§4.4, a condition of 0206's acceptance):
|
|
16
|
+
* the Phase C mirror-dedup heuristic is retired because under the declared
|
|
17
|
+
* tokens every true-mirror pair propagates the SAME direction. That premise
|
|
18
|
+
* is exercised, not assumed: buildImpactPropagation asserts that no node
|
|
19
|
+
* pair receives propagation in BOTH directions from two DIFFERENT
|
|
20
|
+
* single-direction relationship types, and fails loudly naming the pair and
|
|
21
|
+
* types. A single relationship declaring `both` is deliberate bidirectional
|
|
22
|
+
* coupling and is exempt; a self-referential type contributing both
|
|
23
|
+
* directions from different rows (a hierarchy cycle) is likewise exempt.
|
|
24
|
+
* A future token edit that would silently bring the undirected flood back
|
|
25
|
+
* trips this assertion instead.
|
|
26
|
+
*
|
|
27
|
+
* Cycle discipline: the recursion carries NO depth column — recursive UNION
|
|
28
|
+
* deduplicates whole rows, and a depth column makes every lap of a cycle a
|
|
29
|
+
* "new" row: the query that looks more informative is the query that never
|
|
30
|
+
* terminates. As a bare id-set the closure saturates and stops.
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync } from "node:fs";
|
|
33
|
+
import { join, resolve } from "node:path";
|
|
34
|
+
import { readCatalogueCsv } from "./csv-reader.js";
|
|
35
|
+
import { loadCanonicalSchema } from "./schema-loader.js";
|
|
36
|
+
import { loadCatalogueKinds, loadRelationshipSemantics, } from "./relationship-semantics.js";
|
|
37
|
+
import { defaultCataloguesDir, projectGraph, } from "./graph-projection.js";
|
|
38
|
+
function resolveFlow(edge, semantics) {
|
|
39
|
+
const decl = edge.type.includes(".")
|
|
40
|
+
? semantics.entityFks.get(edge.type)
|
|
41
|
+
: semantics.edges.get(edge.type);
|
|
42
|
+
if (decl === undefined) {
|
|
43
|
+
throw new Error(`impact: relationship ${edge.type} has no declaration in the ` +
|
|
44
|
+
`rubric-coverage-map (M-ADR 0206) — sync the vendored map.`);
|
|
45
|
+
}
|
|
46
|
+
if (decl.impactFlow !== undefined)
|
|
47
|
+
return decl.impactFlow;
|
|
48
|
+
const by = decl.impactFlowBy;
|
|
49
|
+
const value = (edge.properties?.[by.column] ?? "").trim();
|
|
50
|
+
const flow = by.values[value];
|
|
51
|
+
if (flow === undefined) {
|
|
52
|
+
throw new Error(`impact: ${edge.type} resolves flow by column '${by.column}' but row ` +
|
|
53
|
+
`value '${value}' is not in the declared map — extend the ` +
|
|
54
|
+
`declaration or fix the row.`);
|
|
55
|
+
}
|
|
56
|
+
return flow;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolve every declared edge into propagation steps per its flow, and
|
|
60
|
+
* exercise the §4.4 co-directionality invariant.
|
|
61
|
+
*/
|
|
62
|
+
export function buildImpactPropagation(edges, semantics) {
|
|
63
|
+
const out = [];
|
|
64
|
+
const byFlow = new Map();
|
|
65
|
+
let flowNone = 0;
|
|
66
|
+
const pairs = new Map();
|
|
67
|
+
const notePair = (from, to, type) => {
|
|
68
|
+
const [a, b] = from < to ? [from, to] : [to, from];
|
|
69
|
+
const key = `${a}␟${b}`;
|
|
70
|
+
let rec = pairs.get(key);
|
|
71
|
+
if (rec === undefined) {
|
|
72
|
+
rec = { ab: new Set(), ba: new Set() };
|
|
73
|
+
pairs.set(key, rec);
|
|
74
|
+
}
|
|
75
|
+
(from === a ? rec.ab : rec.ba).add(type);
|
|
76
|
+
};
|
|
77
|
+
for (const e of edges) {
|
|
78
|
+
const flow = resolveFlow(e, semantics);
|
|
79
|
+
byFlow.set(flow, (byFlow.get(flow) ?? 0) + 1);
|
|
80
|
+
if (flow === "none") {
|
|
81
|
+
flowNone += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (flow === "toward-source" || flow === "both") {
|
|
85
|
+
out.push({ from: e.target, to: e.source, type: e.type });
|
|
86
|
+
notePair(e.target, e.source, e.type);
|
|
87
|
+
}
|
|
88
|
+
if (flow === "toward-target" || flow === "both") {
|
|
89
|
+
out.push({ from: e.source, to: e.target, type: e.type });
|
|
90
|
+
notePair(e.source, e.target, e.type);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// §4.4 invariant, refined on measured data (2026-08-06): a pair fails only
|
|
94
|
+
// when one type contributes EXCLUSIVELY a→b against another contributing
|
|
95
|
+
// EXCLUSIVELY b→a. A type that itself spans both directions on the pair —
|
|
96
|
+
// opposing role-rows of a role-conditional relationship (a service that
|
|
97
|
+
// publishes AND consumes the same event: five real pairs on the reverse
|
|
98
|
+
// estate, one service, all session-broadcast shaped), a self-referential
|
|
99
|
+
// hierarchy cycle, or a declared `both` flow — has DECLARED that pair
|
|
100
|
+
// bidirectional, so other types aligning with either direction are
|
|
101
|
+
// consistent with declarations, not drift. The flipped-token flood shape
|
|
102
|
+
// (two single-direction declarations opposing) still fails, loudly.
|
|
103
|
+
for (const [key, rec] of pairs) {
|
|
104
|
+
const abOnly = [...rec.ab].filter((t) => !rec.ba.has(t));
|
|
105
|
+
const baOnly = [...rec.ba].filter((t) => !rec.ab.has(t));
|
|
106
|
+
if (abOnly.length > 0 && baOnly.length > 0) {
|
|
107
|
+
const [a, b] = key.split("␟");
|
|
108
|
+
throw new Error(`impact: counter-directional reciprocal pair (M-ADR 0206 §4.4): ` +
|
|
109
|
+
`${abOnly[0]} propagates ${a} -> ${b} while ${baOnly[0]} propagates ` +
|
|
110
|
+
`${b} -> ${a}, and neither declares the pair bidirectional. The ` +
|
|
111
|
+
`declared tokens have re-created an undirected pair; fix the ` +
|
|
112
|
+
`classification before trusting any closure.`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { edges: out, flowNone, byFlow };
|
|
116
|
+
}
|
|
117
|
+
function sqlStringLiteral(value) {
|
|
118
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Closure of `seed` over resolved propagation edges, on the engine.
|
|
122
|
+
* Exported for the fixture controls; the verb goes through runCatalogueImpact.
|
|
123
|
+
*/
|
|
124
|
+
export async function computeImpactClosure(propagation, seed) {
|
|
125
|
+
const { DuckDBInstance } = await import("@duckdb/node-api");
|
|
126
|
+
const instance = await DuckDBInstance.create(":memory:");
|
|
127
|
+
const conn = await instance.connect();
|
|
128
|
+
try {
|
|
129
|
+
await conn.run(`CREATE TABLE prop (f VARCHAR, t VARCHAR);`);
|
|
130
|
+
const CHUNK = 500;
|
|
131
|
+
for (let i = 0; i < propagation.length; i += CHUNK) {
|
|
132
|
+
const tuples = propagation
|
|
133
|
+
.slice(i, i + CHUNK)
|
|
134
|
+
.map((e) => `(${sqlStringLiteral(e.from)}, ${sqlStringLiteral(e.to)})`)
|
|
135
|
+
.join(",\n");
|
|
136
|
+
await conn.run(`INSERT INTO prop VALUES\n${tuples};`);
|
|
137
|
+
}
|
|
138
|
+
const seedLit = sqlStringLiteral(seed);
|
|
139
|
+
// CYCLE GUARD: the recursion carries NO depth column, deliberately.
|
|
140
|
+
// Recursive UNION deduplicates whole rows; a depth column makes every
|
|
141
|
+
// lap around a cycle a "new" row (same id, bigger depth), so the query
|
|
142
|
+
// that looks more informative is the query that never terminates — the
|
|
143
|
+
// catalogue graph is not a DAG. As a bare id-set the closure saturates
|
|
144
|
+
// and stops.
|
|
145
|
+
const reader = await conn.runAndReadAll(`WITH RECURSIVE reach(id) AS (\n` +
|
|
146
|
+
` SELECT t FROM prop WHERE f = ${seedLit}\n` +
|
|
147
|
+
` UNION\n` +
|
|
148
|
+
` SELECT p.t FROM prop p JOIN reach r ON p.f = r.id\n` +
|
|
149
|
+
`)\n` +
|
|
150
|
+
`SELECT DISTINCT id FROM reach WHERE id <> ${seedLit} ORDER BY id;`);
|
|
151
|
+
return reader.getRows().map((row) => String(row[0]));
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
conn.disconnectSync();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export async function runCatalogueImpact(inputs, write) {
|
|
158
|
+
const sddRoot = resolve(inputs.sddPath ?? process.cwd());
|
|
159
|
+
const tier = inputs.tier ?? "member";
|
|
160
|
+
const dir = resolve(sddRoot, inputs.cataloguesDir ?? defaultCataloguesDir(tier));
|
|
161
|
+
if (!existsSync(dir)) {
|
|
162
|
+
write(`catalogues directory not found at ${dir}\n` +
|
|
163
|
+
` Run \`sdd-cli catalogue build\` (or aggregate) first.\n`);
|
|
164
|
+
return { exitCode: 2 };
|
|
165
|
+
}
|
|
166
|
+
const schema = await loadCanonicalSchema();
|
|
167
|
+
const kinds = await loadCatalogueKinds();
|
|
168
|
+
const semantics = await loadRelationshipSemantics();
|
|
169
|
+
const rowsByCatalogue = new Map();
|
|
170
|
+
for (const cat of schema.catalogues) {
|
|
171
|
+
const filePath = join(dir, `${cat.name}.csv`);
|
|
172
|
+
if (!existsSync(filePath))
|
|
173
|
+
continue;
|
|
174
|
+
rowsByCatalogue.set(cat.name, (await readCatalogueCsv(filePath)).rows);
|
|
175
|
+
}
|
|
176
|
+
const projection = projectGraph(schema, kinds, rowsByCatalogue, tier);
|
|
177
|
+
const lines = projection.text.trimEnd().split("\n");
|
|
178
|
+
const nodeIds = new Set();
|
|
179
|
+
const edges = [];
|
|
180
|
+
for (const line of lines) {
|
|
181
|
+
const o = JSON.parse(line);
|
|
182
|
+
if (o.t === "node")
|
|
183
|
+
nodeIds.add(o.id);
|
|
184
|
+
else if (o.t === "edge")
|
|
185
|
+
edges.push({
|
|
186
|
+
source: o.source,
|
|
187
|
+
target: o.target,
|
|
188
|
+
type: o.type,
|
|
189
|
+
...(o.properties !== undefined ? { properties: o.properties } : {}),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (!nodeIds.has(inputs.of)) {
|
|
193
|
+
write(`node not found in the projection: ${inputs.of}\n` +
|
|
194
|
+
` Node ids have the form "<catalogue>/<id>", e.g. contracts/orders-api.\n`);
|
|
195
|
+
return { exitCode: 2 };
|
|
196
|
+
}
|
|
197
|
+
const prop = buildImpactPropagation(edges, semantics);
|
|
198
|
+
const closure = await computeImpactClosure(prop.edges, inputs.of);
|
|
199
|
+
const byCatalogue = new Map();
|
|
200
|
+
for (const id of closure) {
|
|
201
|
+
const cat = id.slice(0, id.indexOf("/"));
|
|
202
|
+
byCatalogue.set(cat, (byCatalogue.get(cat) ?? 0) + 1);
|
|
203
|
+
}
|
|
204
|
+
write(`impact closure of ${inputs.of} (${tier} tier, schema ${schema.schemaVersion})\n`);
|
|
205
|
+
// Required stated lines (M-ADR 0206 §4.6): what the number means, and what
|
|
206
|
+
// was traversed under each declared flow — visible per run, not
|
|
207
|
+
// discoverable from the code.
|
|
208
|
+
write(` semantics: change impact over classified relationships (M-ADR 0206) —\n` +
|
|
209
|
+
` each relationship traversed per its declared impact-flow; the\n` +
|
|
210
|
+
` co-directionality invariant is asserted on every run\n`);
|
|
211
|
+
const flowCounts = [...prop.byFlow.entries()]
|
|
212
|
+
.sort()
|
|
213
|
+
.map(([f, n]) => `${f}=${n}`)
|
|
214
|
+
.join(" ");
|
|
215
|
+
write(` declared edges by resolved flow: ${flowCounts}\n`);
|
|
216
|
+
write(` reachable nodes: ${closure.length}\n`);
|
|
217
|
+
for (const [cat, n] of [...byCatalogue.entries()].sort()) {
|
|
218
|
+
write(` ${cat}: ${n}\n`);
|
|
219
|
+
}
|
|
220
|
+
for (const id of closure) {
|
|
221
|
+
write(` ${id}\n`);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
exitCode: 0,
|
|
225
|
+
result: { seed: inputs.of, closure, byCatalogue },
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=impact-run.js.map
|