@sdd-method/author-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/author-bin.js +8 -0
- package/dist/author-program.js +99 -0
- package/dist/lib/adr-origin.js +59 -0
- package/dist/lib/agent-harness.js +37 -0
- package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +18 -0
- package/dist/lib/api-surface/analyzers/library.js +95 -0
- package/dist/lib/api-surface/analyzers/serverless.js +336 -0
- package/dist/lib/api-surface/analyzers/spring.js +237 -0
- package/dist/lib/api-surface/analyzers/terraform.js +109 -0
- package/dist/lib/api-surface/detect.js +25 -0
- package/dist/lib/api-surface/emit.js +109 -0
- package/dist/lib/api-surface/file-set.js +43 -0
- package/dist/lib/api-surface/index.js +134 -0
- package/dist/lib/api-surface/types.js +17 -0
- package/dist/lib/briefing/index.js +13 -0
- package/dist/lib/briefing/render.js +132 -0
- package/dist/lib/briefing/variables.js +92 -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/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-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 +70 -0
- package/dist/lib/catalogue/gate-checks.js +338 -0
- package/dist/lib/catalogue/graph-projection.js +346 -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/conformance/binding.js +70 -0
- package/dist/lib/conformance/finding.js +69 -0
- package/dist/lib/conformance/index.js +578 -0
- package/dist/lib/context-pull/index.js +191 -0
- package/dist/lib/context-pull/resolve.js +70 -0
- package/dist/lib/context-pull/retired-mcp-setup.js +7 -0
- package/dist/lib/contracts-availability/index.js +267 -0
- package/dist/lib/generate-briefing/index.js +77 -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/pack/log.js +18 -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/plan/index.js +69 -0
- package/dist/lib/plan/read-sdd.js +102 -0
- package/dist/lib/plan/render-template.js +84 -0
- package/dist/lib/repo-kind.js +129 -0
- package/dist/lib/scaffold/domain.js +234 -0
- package/dist/lib/scaffold/registry.js +136 -0
- package/dist/lib/scripts.js +7 -0
- package/dist/lib/sdd-index/repo-config.js +245 -0
- package/dist/lib/sdd-reader.js +91 -0
- package/dist/lib/snapshot/advance.js +454 -0
- package/dist/lib/snapshot/diff.js +190 -0
- package/dist/lib/snapshot/fold.js +208 -0
- package/dist/lib/snapshot/land.js +178 -0
- package/dist/lib/snapshot/manifest.js +206 -0
- package/dist/lib/snapshot/targets.js +169 -0
- package/dist/lib/snapshot/verify.js +91 -0
- package/dist/lib/sync/briefing-merge.js +106 -0
- package/dist/lib/sync/bundle-cache.js +109 -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-version.json +4 -0
- package/dist/verbs/api-surface.js +71 -0
- package/dist/verbs/catalogue-check.js +28 -0
- package/dist/verbs/catalogue-verify.js +114 -0
- package/dist/verbs/conformance.js +34 -0
- package/dist/verbs/context.js +39 -0
- package/dist/verbs/contracts.js +34 -0
- package/dist/verbs/generate-briefing.js +29 -0
- package/dist/verbs/list.js +45 -0
- package/dist/verbs/pack-validate.js +17 -0
- package/dist/verbs/path-option.js +25 -0
- package/dist/verbs/plan.js +42 -0
- package/dist/verbs/render.js +87 -0
- package/dist/verbs/scaffold.js +83 -0
- package/dist/verbs/snapshot.js +134 -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,256 @@
|
|
|
1
|
+
// Edge builders for capability_* catalogues — capability_service,
|
|
2
|
+
// capability_adr, capability_feature, capability_contract.
|
|
3
|
+
//
|
|
4
|
+
// All four read traceability YAMLs as the authoritative source. The
|
|
5
|
+
// canonical schema declares capability_id and the target column as
|
|
6
|
+
// required FKs — so each builder skips entries whose target doesn't
|
|
7
|
+
// resolve in the known-target set passed in.
|
|
8
|
+
// capability_service is the exception (F4, schema 2.29.0): its service_id
|
|
9
|
+
// FK is external, and an entry marked `owner_sdd:` declares a cross-SDD
|
|
10
|
+
// reference — the edge is emitted despite not resolving locally and is
|
|
11
|
+
// validated hard at aggregation against the owning sibling's rows.
|
|
12
|
+
// Every other skipped `services[].id` still surfaces as a finding (orphan,
|
|
13
|
+
// or convention when the id names a known external system) — unresolved
|
|
14
|
+
// edges must never drop silently.
|
|
15
|
+
//
|
|
16
|
+
// capability_event is intentionally not built here: its source per the
|
|
17
|
+
// metamodel is `## Related Events` markdown tables in capability specs,
|
|
18
|
+
// which depends on a spec-formatting convention that's only partial in
|
|
19
|
+
// the field. Defer to a later phase.
|
|
20
|
+
import { basename, sep } from "node:path";
|
|
21
|
+
import { declaredContractPaths } from "../traceability-loader.js";
|
|
22
|
+
import { ORIGIN_PREFIX_SRC } from "../../adr-origin.js";
|
|
23
|
+
export function buildCapabilityService(inputs) {
|
|
24
|
+
const rows = [];
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
for (const m of inputs.manifests) {
|
|
27
|
+
const capId = m.capability.id;
|
|
28
|
+
if (!inputs.knownCapabilityIds.has(capId)) {
|
|
29
|
+
// M-ADR 0211 §4.3 — the second bare `continue` in this file, now named. A manifest
|
|
30
|
+
// whose capability produced no row loses every services[] entry; that used to
|
|
31
|
+
// happen in silence, the same shape as the spec-only stem collision.
|
|
32
|
+
const n = (m.traceability.services ?? []).length;
|
|
33
|
+
if (n > 0) {
|
|
34
|
+
inputs.findings.add("coverage", `capability_service: ${m.relPath} declares ${n} services[] entr${n === 1 ? "y" : "ies"} but its capability "${capId}" produced no capabilities row — all ${n} dropped (M-ADR 0211 §4.3)`);
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
for (const entry of m.traceability.services ?? []) {
|
|
39
|
+
const sid = entry.id;
|
|
40
|
+
if (!inputs.knownServiceIds.has(sid)) {
|
|
41
|
+
// Never drop silently (61 missing edges sat invisible in the field
|
|
42
|
+
// until a manual sweep). Resolution order:
|
|
43
|
+
// - `owner_sdd:` marked (F4, schema 2.29.0) — a DECLARED cross-SDD
|
|
44
|
+
// reference (e.g. the service moved to a sibling at a carve).
|
|
45
|
+
// Emit the edge; the external FK logs it and aggregation
|
|
46
|
+
// validates it hard against the sibling's rows.
|
|
47
|
+
// - id naming a known external system — a modelling convention.
|
|
48
|
+
// - anything else — a dangling reference, member-tier orphan
|
|
49
|
+
// finding. NOT emitted: measured in the field, almost all
|
|
50
|
+
// unmarked danglings are local authoring gaps, and emitting
|
|
51
|
+
// them would fail aggregation on a defect that isn't theirs.
|
|
52
|
+
if (entry.owner_sdd !== undefined) {
|
|
53
|
+
// Emit and fall through — the external FK validation logs the
|
|
54
|
+
// single `[finding] external` line for it (one fact, one finding).
|
|
55
|
+
}
|
|
56
|
+
else if (inputs.knownExternalSystemIds?.has(sid)) {
|
|
57
|
+
inputs.findings.add("convention", `capability_service: ${capId} services[] id "${sid}" resolves to an external system, not a service — edge skipped`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Name the remedy the branch above already implements (M-ADR 0199
|
|
62
|
+
// §4.4: a remedy must be true at the emitting tier). Measured cost
|
|
63
|
+
// of not naming it: an adopter read this line, concluded a
|
|
64
|
+
// cross-SDD capability_service edge was unrepresentable, and
|
|
65
|
+
// reported two permanently-unresolvable orphans that were one
|
|
66
|
+
// authored line from resolving.
|
|
67
|
+
inputs.findings.add("orphan", `capability_service: ${capId} services[] id "${sid}" — no matching service (edge skipped). ` +
|
|
68
|
+
`If that service is owned by a SIBLING SDD, declare \`owner_sdd:\` on this entry ` +
|
|
69
|
+
`(schema 2.29.0) — the edge is then emitted and validated at aggregation. ` +
|
|
70
|
+
`Otherwise it is a local authoring gap: fix the id, or add the service.`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const role = entry.role ?? "";
|
|
75
|
+
const id = `${capId}__${sid}__${role !== "" ? role : "related"}`;
|
|
76
|
+
if (seen.has(id))
|
|
77
|
+
continue;
|
|
78
|
+
seen.add(id);
|
|
79
|
+
rows.push({
|
|
80
|
+
id,
|
|
81
|
+
capability_id: capId,
|
|
82
|
+
service_id: sid,
|
|
83
|
+
role,
|
|
84
|
+
// M-ADR 0211 §4.1 / schema 3.9.0 — the dependency's contract and wire coupling as
|
|
85
|
+
// catalogued fields. Empty when the entry declares none; the validator has already
|
|
86
|
+
// held `interaction_pattern` to the bound vocabulary and `contract` to resolution.
|
|
87
|
+
contract_id: entry.contract ?? "",
|
|
88
|
+
interaction_pattern: entry.interaction_pattern ?? "",
|
|
89
|
+
qualifier: entry.qualifier ?? "",
|
|
90
|
+
evidence_ref: entry.evidence ?? "",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return rows;
|
|
95
|
+
}
|
|
96
|
+
export function buildCapabilityAdr(inputs) {
|
|
97
|
+
const rows = [];
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
for (const m of inputs.manifests) {
|
|
100
|
+
const capId = m.capability.id;
|
|
101
|
+
if (!inputs.knownCapabilityIds.has(capId))
|
|
102
|
+
continue;
|
|
103
|
+
for (const entry of m.traceability.adrs ?? []) {
|
|
104
|
+
const adrId = canonicaliseAdrId(entry.id);
|
|
105
|
+
if (adrId === "")
|
|
106
|
+
continue;
|
|
107
|
+
if (!inputs.knownAdrIds.has(adrId))
|
|
108
|
+
continue;
|
|
109
|
+
const id = `${capId}__${adrId}`;
|
|
110
|
+
if (seen.has(id))
|
|
111
|
+
continue;
|
|
112
|
+
seen.add(id);
|
|
113
|
+
rows.push({
|
|
114
|
+
id,
|
|
115
|
+
capability_id: capId,
|
|
116
|
+
adr_id: adrId,
|
|
117
|
+
confidence: "spec",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return rows;
|
|
122
|
+
}
|
|
123
|
+
export function buildCapabilityFeature(inputs) {
|
|
124
|
+
const rows = [];
|
|
125
|
+
const seen = new Set();
|
|
126
|
+
for (const m of inputs.manifests) {
|
|
127
|
+
const capId = m.capability.id;
|
|
128
|
+
if (!inputs.knownCapabilityIds.has(capId))
|
|
129
|
+
continue;
|
|
130
|
+
for (const path of m.traceability.runway?.enables ?? []) {
|
|
131
|
+
const featureId = resolveFeaturePath(path, inputs.featureIdByPath);
|
|
132
|
+
if (featureId === "")
|
|
133
|
+
continue;
|
|
134
|
+
const id = `${capId}__${featureId}`;
|
|
135
|
+
if (seen.has(id))
|
|
136
|
+
continue;
|
|
137
|
+
seen.add(id);
|
|
138
|
+
rows.push({
|
|
139
|
+
id,
|
|
140
|
+
capability_id: capId,
|
|
141
|
+
feature_id: featureId,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return rows;
|
|
146
|
+
}
|
|
147
|
+
export function buildCapabilityContract(inputs) {
|
|
148
|
+
const rows = [];
|
|
149
|
+
const seen = new Set();
|
|
150
|
+
for (const m of inputs.manifests) {
|
|
151
|
+
const capId = m.capability.id;
|
|
152
|
+
if (!inputs.knownCapabilityIds.has(capId))
|
|
153
|
+
continue;
|
|
154
|
+
const block = m.traceability.contracts;
|
|
155
|
+
if (!block)
|
|
156
|
+
continue;
|
|
157
|
+
const all = [];
|
|
158
|
+
// Each list holds bare string paths AND object entries ({path,
|
|
159
|
+
// external} — the D13 declared-external form). Both contribute their
|
|
160
|
+
// path here: a declared-external entry's path resolves against the
|
|
161
|
+
// merged id index (which includes the rows buildDeclaredExternalContracts
|
|
162
|
+
// just emitted), which is exactly how an external contract gains its
|
|
163
|
+
// capability_contract edge. spicedb joins the walk with the loader —
|
|
164
|
+
// .zed declarations previously never reached this builder at all.
|
|
165
|
+
// Iterates the SHARED kind list, not a hand-written one. This site is why
|
|
166
|
+
// that helper exists: it carried a four-kind array while discovery, mapping
|
|
167
|
+
// and the loader all knew five, so an estate's 9 correct graphql declarations
|
|
168
|
+
// produced ZERO capability_contract edges and left 7 orphans standing. The
|
|
169
|
+
// comment below already recorded the same bug one kind earlier.
|
|
170
|
+
for (const path of declaredContractPaths(block)) {
|
|
171
|
+
all.push(path);
|
|
172
|
+
}
|
|
173
|
+
for (const path of all) {
|
|
174
|
+
const contractId = resolveContractPath(path, inputs.contractIdByPath);
|
|
175
|
+
if (contractId === "") {
|
|
176
|
+
// Never drop silently. This guard dropped every unresolvable entry with no
|
|
177
|
+
// trace until WS2 of the coverage-invariants work measured it: 62 of 188
|
|
178
|
+
// declared entries across two estates, and NOT ONE of them dangling — every
|
|
179
|
+
// one named a real artefact. So the resolution failure is the interesting
|
|
180
|
+
// fact, and the reader needs it.
|
|
181
|
+
//
|
|
182
|
+
// WORDING IS DELIBERATELY CAUSE-AGNOSTIC (M-ADR 0189 §8 / M-ADR 0199 §4.4).
|
|
183
|
+
// The tempting remedy — "declare the contract" or "fix the path" — is FALSE
|
|
184
|
+
// for the measured majority: 56 of the 62 name a spec that exists at exactly
|
|
185
|
+
// that path in a sibling code repository (the M-ADR 0110 sibling-repo
|
|
186
|
+
// pattern), which the contracts discoverer does not walk, so there is
|
|
187
|
+
// nothing for the author to fix. 4 more name a contract row owned by a
|
|
188
|
+
// sibling SDD, which this edge has no cross-SDD form to express. State the
|
|
189
|
+
// fact, name the candidate causes, prescribe nothing.
|
|
190
|
+
inputs.findings.add("orphan", `capability_contract: ${m.relPath} traceability.contracts entry "${path}" `
|
|
191
|
+
+ `resolved to no contract row — edge dropped. The spec may sit outside `
|
|
192
|
+
+ `this SDD's contract-discovery paths (contracts/{openapi,proto,avro}/ or `
|
|
193
|
+
+ `docs/domains/*/contracts/{openapi,proto,avro}/), or name a contract owned `
|
|
194
|
+
+ `by a sibling SDD — capability_contract has no cross-SDD form, unlike `
|
|
195
|
+
+ `capability_service's owner_sdd`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const id = `${capId}__${contractId}`;
|
|
199
|
+
if (seen.has(id))
|
|
200
|
+
continue;
|
|
201
|
+
seen.add(id);
|
|
202
|
+
rows.push({
|
|
203
|
+
id,
|
|
204
|
+
capability_id: capId,
|
|
205
|
+
contract_id: contractId,
|
|
206
|
+
source: "declared",
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return rows;
|
|
211
|
+
}
|
|
212
|
+
// ─── helpers ────────────────────────────────────────────────────────────
|
|
213
|
+
/**
|
|
214
|
+
* Normalise an ADR ID from a traceability entry. Accepts bare numerics
|
|
215
|
+
* (`"0037"`, `"0058"`) and full forms (`"ADR-0037"`, `"ADR 0037"`); emits
|
|
216
|
+
* `ADR-XXXX` (4-digit zero-padded) which is the canonical adrs.id form.
|
|
217
|
+
*/
|
|
218
|
+
function canonicaliseAdrId(raw) {
|
|
219
|
+
// Accept a bare number, `ADR-NNNN`/`ADR NNNN`, or any canonical origin prefix
|
|
220
|
+
// (`M-ADR`/`RA-ADR`/`P-ADR`/`D-ADR NNNN`) — all resolve by number.
|
|
221
|
+
const m = new RegExp(`^(?:${ORIGIN_PREFIX_SRC}ADR[-\\s]+)?(\\d+)`, "i").exec(raw);
|
|
222
|
+
if (!m || m[1] === undefined)
|
|
223
|
+
return "";
|
|
224
|
+
const num = parseInt(m[1], 10);
|
|
225
|
+
if (!Number.isFinite(num))
|
|
226
|
+
return "";
|
|
227
|
+
return `ADR-${String(num).padStart(4, "0")}`;
|
|
228
|
+
}
|
|
229
|
+
function resolveFeaturePath(path, index) {
|
|
230
|
+
const cleaned = path.replace(/^\/+/, "");
|
|
231
|
+
return index.get(cleaned) ?? "";
|
|
232
|
+
}
|
|
233
|
+
function resolveContractPath(path, index) {
|
|
234
|
+
const cleaned = path.replace(/^\/+/, "");
|
|
235
|
+
// Try exact path, then suffix matches.
|
|
236
|
+
const direct = index.get(cleaned);
|
|
237
|
+
if (direct !== undefined)
|
|
238
|
+
return direct;
|
|
239
|
+
// Suffix: last two parts (e.g. "asset/asset-twin.yaml")
|
|
240
|
+
const parts = cleaned.split(sep);
|
|
241
|
+
if (parts.length >= 2) {
|
|
242
|
+
const suffix = parts.slice(-2).join("/");
|
|
243
|
+
for (const [k, v] of index) {
|
|
244
|
+
if (k.endsWith(suffix))
|
|
245
|
+
return v;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Basename fallback
|
|
249
|
+
const base = basename(cleaned);
|
|
250
|
+
for (const [k, v] of index) {
|
|
251
|
+
if (basename(k) === base)
|
|
252
|
+
return v;
|
|
253
|
+
}
|
|
254
|
+
return "";
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=capability-edges.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walker for capability spec markdown files per ADR 0145 (schema 2.7.0).
|
|
3
|
+
*
|
|
4
|
+
* Reads `## Feature Grounding (code-path trace)` markdown tables of the
|
|
5
|
+
* canonical shape:
|
|
6
|
+
*
|
|
7
|
+
* | Feature | Implementation | Grounding Doc |
|
|
8
|
+
* |---|---|---|
|
|
9
|
+
* | `alerts-module` | `alerts` Vuex module + `src/components/alerts` | docs/.../feature-grounding.md |
|
|
10
|
+
*
|
|
11
|
+
* Path convention (per ADR 0145 §4.3):
|
|
12
|
+
* - platform-profile: `docs/domains/<dom>/product/<capability>.md`
|
|
13
|
+
*
|
|
14
|
+
* Capability id resolves from the filename stem. Tolerant of column
|
|
15
|
+
* re-ordering and missing optional columns. Reuses the table-parsing
|
|
16
|
+
* logic from `feature-grounding-walker.ts` (any markdown table whose
|
|
17
|
+
* first header normalises to `feature` is parsed).
|
|
18
|
+
*
|
|
19
|
+
* Recovery is row-local — a malformed row emits a finding and the
|
|
20
|
+
* walker continues with the next row.
|
|
21
|
+
*/
|
|
22
|
+
import { readdir } from "node:fs/promises";
|
|
23
|
+
import { basename, join, relative, sep } from "node:path";
|
|
24
|
+
import { parseGroundingTable, } from "./feature-grounding-walker.js";
|
|
25
|
+
const DOMAINS_DIR_REL = join("docs", "domains");
|
|
26
|
+
const PRODUCT_DIR_NAME = "product";
|
|
27
|
+
/**
|
|
28
|
+
* Find all `docs/domains/<dom>/product/<capability>.md` files under the
|
|
29
|
+
* SDD root. Excludes `.traceability.yaml` sidecars. Sorted for
|
|
30
|
+
* deterministic output.
|
|
31
|
+
*/
|
|
32
|
+
export async function findCapabilitySpecFiles(sddPath) {
|
|
33
|
+
const out = [];
|
|
34
|
+
const domainsDir = join(sddPath, DOMAINS_DIR_REL);
|
|
35
|
+
let domains;
|
|
36
|
+
try {
|
|
37
|
+
domains = await readdir(domainsDir, { withFileTypes: true });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
for (const d of domains) {
|
|
43
|
+
if (!d.isDirectory())
|
|
44
|
+
continue;
|
|
45
|
+
const productDir = join(domainsDir, d.name, PRODUCT_DIR_NAME);
|
|
46
|
+
let files;
|
|
47
|
+
try {
|
|
48
|
+
files = await readdir(productDir, { withFileTypes: true });
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
for (const f of files) {
|
|
54
|
+
if (!f.isFile())
|
|
55
|
+
continue;
|
|
56
|
+
if (!f.name.endsWith(".md"))
|
|
57
|
+
continue;
|
|
58
|
+
// Skip the README that lives in some product/ dirs.
|
|
59
|
+
if (f.name === "README.md")
|
|
60
|
+
continue;
|
|
61
|
+
out.push(join(productDir, f.name));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out.sort();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the capability id from a capability spec file's absolute
|
|
68
|
+
* path. Returns the filename stem (`.md` stripped).
|
|
69
|
+
*
|
|
70
|
+
* docs/domains/applications/product/alerts-and-notifications.md
|
|
71
|
+
* → "alerts-and-notifications"
|
|
72
|
+
*/
|
|
73
|
+
export function capabilityIdFromSpecPath(absPath, sddPath) {
|
|
74
|
+
const rel = relative(sddPath, absPath);
|
|
75
|
+
const parts = rel.split(sep);
|
|
76
|
+
// Expected: ["docs", "domains", "<dom>", "product", "<file>.md"]
|
|
77
|
+
if (parts.length < 5)
|
|
78
|
+
return "";
|
|
79
|
+
const file = parts[parts.length - 1] ?? "";
|
|
80
|
+
if (!file.endsWith(".md"))
|
|
81
|
+
return "";
|
|
82
|
+
return file.slice(0, -3);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parse the markdown content of a capability spec file and return the
|
|
86
|
+
* rows from its `## Feature Grounding (code-path trace)` table.
|
|
87
|
+
*
|
|
88
|
+
* Implementation detail: we delegate to `parseGroundingTable`, which
|
|
89
|
+
* picks the first table whose first column normalises to `feature`.
|
|
90
|
+
* Capability specs typically have one such table under
|
|
91
|
+
* `## Feature Grounding (code-path trace)`; the parser tolerates
|
|
92
|
+
* variations in the heading text.
|
|
93
|
+
*/
|
|
94
|
+
export function parseCapabilitySpecTable(text) {
|
|
95
|
+
return parseGroundingTable(text);
|
|
96
|
+
}
|
|
97
|
+
/** Re-export basename for convenience in tests. */
|
|
98
|
+
export { basename };
|
|
99
|
+
//# sourceMappingURL=capability-spec-walker.js.map
|