@sdd-method/author-cli 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/author-program.js +1 -1
- package/dist/lib/agent-harness.js +2 -2
- package/dist/lib/briefing/index.js +1 -1
- package/dist/lib/briefing/render.js +1 -1
- package/dist/lib/briefing/variables.js +1 -1
- package/dist/lib/catalogue/application-archetype/vocabulary.js +2 -2
- package/dist/lib/catalogue/archetype/maps/android.yaml +2 -2
- package/dist/lib/catalogue/archetype/maps/swiftui.yaml +2 -2
- package/dist/lib/catalogue/archetype/maps/uikit.yaml +2 -2
- package/dist/lib/catalogue/archetype/maps/web-generic.yaml +2 -2
- package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +3 -3
- package/dist/lib/catalogue/archetype/resolver.js +4 -4
- package/dist/lib/catalogue/archetype/wireframe.js +1 -1
- package/dist/lib/catalogue/build.js +33 -33
- package/dist/lib/catalogue/builders/adr-supersedes.js +3 -3
- package/dist/lib/catalogue/builders/adrs.js +3 -3
- package/dist/lib/catalogue/builders/c4-containers.js +2 -2
- package/dist/lib/catalogue/builders/c4-entity-rollups.js +1 -1
- package/dist/lib/catalogue/builders/c4-external-systems.js +2 -2
- package/dist/lib/catalogue/builders/c4-systems.js +1 -1
- package/dist/lib/catalogue/builders/capabilities.js +1 -1
- package/dist/lib/catalogue/builders/capability-domain-model.js +1 -1
- package/dist/lib/catalogue/builders/capability-spec-walker.js +2 -2
- package/dist/lib/catalogue/builders/capability-spec.js +4 -4
- package/dist/lib/catalogue/builders/contracts.js +12 -6
- package/dist/lib/catalogue/builders/data-store-mapping.js +2 -2
- package/dist/lib/catalogue/builders/domain-models.js +2 -2
- package/dist/lib/catalogue/builders/domains.js +3 -3
- package/dist/lib/catalogue/builders/entities-manifest.js +8 -8
- package/dist/lib/catalogue/builders/entities-mermaid.js +4 -4
- package/dist/lib/catalogue/builders/entity-persistence.js +1 -1
- package/dist/lib/catalogue/builders/entity-slug.js +3 -3
- package/dist/lib/catalogue/builders/event-catalog-walker.js +6 -6
- package/dist/lib/catalogue/builders/event-edges.js +6 -6
- package/dist/lib/catalogue/builders/events.js +3 -3
- package/dist/lib/catalogue/builders/evidence-class.js +1 -1
- package/dist/lib/catalogue/builders/feature-domain-model.js +1 -1
- package/dist/lib/catalogue/builders/feature-grounding-walker.js +2 -2
- package/dist/lib/catalogue/builders/feature-grounding.js +5 -5
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +3 -3
- package/dist/lib/catalogue/builders/features.js +1 -1
- package/dist/lib/catalogue/builders/glossary-xref.js +4 -4
- package/dist/lib/catalogue/builders/integrations.js +4 -4
- package/dist/lib/catalogue/builders/persona-c4system.js +1 -1
- package/dist/lib/catalogue/builders/product-edges.js +1 -1
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +2 -2
- package/dist/lib/catalogue/builders/products.js +5 -5
- package/dist/lib/catalogue/builders/repo-edges.js +12 -12
- package/dist/lib/catalogue/builders/repositories.js +16 -11
- package/dist/lib/catalogue/builders/screen-tree.js +6 -6
- package/dist/lib/catalogue/builders/screens.js +18 -18
- package/dist/lib/catalogue/builders/service-contract-consumes.js +1 -1
- package/dist/lib/catalogue/builders/service-entity-tables.js +3 -3
- package/dist/lib/catalogue/builders/services.js +5 -5
- package/dist/lib/catalogue/builders/system-external-dependency.js +2 -2
- package/dist/lib/catalogue/canonical-schema.yaml +3 -2
- package/dist/lib/catalogue/contract-id.js +1 -1
- package/dist/lib/catalogue/coverage-render.js +15 -15
- package/dist/lib/catalogue/csv-writer.js +1 -1
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +1 -1
- package/dist/lib/catalogue/findings.js +1 -1
- package/dist/lib/catalogue/fk-validator.js +2 -2
- package/dist/lib/catalogue/manifest-loader.js +3 -3
- package/dist/lib/catalogue/schema-compat.js +2 -2
- package/dist/lib/catalogue/schema-loader.js +1 -1
- package/dist/lib/catalogue/screen-validation-loader.js +2 -2
- package/dist/lib/catalogue/service-architecture-parser.js +1 -1
- package/dist/lib/catalogue/traceability-loader.js +9 -4
- package/dist/lib/conformance/finding.js +1 -1
- package/dist/lib/conformance/index.js +5 -5
- package/dist/lib/contracts-availability/index.js +8 -2
- package/dist/lib/generate-briefing/index.js +2 -2
- package/dist/lib/metamodel/tql-subset-parser.js +280 -0
- package/dist/lib/pack/schema.js +2 -2
- package/dist/lib/pack/seed.js +2 -2
- package/dist/lib/pack/validate.js +1 -1
- package/dist/lib/plan/read-sdd.js +3 -2
- package/dist/lib/repo-kind.js +3 -3
- package/dist/lib/scaffold/archetype-behaviour.js +171 -0
- package/dist/lib/scaffold/domain.js +24 -6
- package/dist/lib/scaffold/registry.js +1 -1
- package/dist/lib/sdd-index/repo-config.js +1 -0
- package/dist/lib/sync/briefing-merge.js +2 -2
- package/dist/lib/sync/bundle-cache.js +4 -4
- package/dist/lib/sync/yaml-block-merge.js +1 -1
- package/dist/lib/validate/archetype-citations.js +134 -0
- package/dist/lib/validate/behaviour-store.js +25 -1
- package/dist/lib/validate/dispatch.js +8 -1
- package/dist/maintain-version.json +2 -2
- package/dist/verbs/conformance.js +3 -3
- package/dist/verbs/list.js +1 -1
- package/dist/verbs/render.js +1 -1
- package/dist/verbs/scaffold.js +7 -4
- package/dist/verify-program.js +2 -2
- package/dist/verify-version.json +2 -2
- package/package.json +2 -2
- package/scripts/validate-app-dependencies.sh +1 -1
- package/scripts/validate-integration-docs.sh +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Repo-edge builders — repo_capability, repo_contract, feature_repo.
|
|
2
2
|
//
|
|
3
|
-
// Per ADR 0157 (Forward-Declared / Reverse-Derived Parity for Edge Catalogues):
|
|
3
|
+
// Per M-ADR 0157 (Forward-Declared / Reverse-Derived Parity for Edge Catalogues):
|
|
4
4
|
// both edges ingest from forward-method declared sources AND reverse-method
|
|
5
5
|
// derived sources, merged by primary-key deduplication. The two paths are
|
|
6
6
|
// equal-status per the parity principle; neither displaces the other.
|
|
@@ -32,20 +32,20 @@
|
|
|
32
32
|
// services.primary_repo_id`. A service exposing a contract is implemented
|
|
33
33
|
// by a repo (its primary_repo_id) — the repo "owns" the contract by
|
|
34
34
|
// transitive inference. Works universally because primary_repo_id and
|
|
35
|
-
// service_contract are core-schema populated. Added per ADR 0157 §4.2
|
|
35
|
+
// service_contract are core-schema populated. Added per M-ADR 0157 §4.2
|
|
36
36
|
// amendment after an early-adopter 0/30 source_repo_id outcome.
|
|
37
37
|
// repo_contract Path Y — `contracts.source_repo_id` denormalisation
|
|
38
|
-
// (from OpenAPI `x-source-repo` hints per ADR 0110, or whatever
|
|
38
|
+
// (from OpenAPI `x-source-repo` hints per M-ADR 0110, or whatever
|
|
39
39
|
// populates the column). Augments Path X when adopters carry the hint.
|
|
40
40
|
// repo_capability Path X (universal) — `capability_service ⋈
|
|
41
41
|
// services.primary_repo_id`. Works in both forward and reverse method
|
|
42
42
|
// shapes because primary_repo_id is core-schema and capability_service
|
|
43
43
|
// is heavily authored.
|
|
44
|
-
// repo_capability Path Y (augments via ADR 0143 feature-grounding) —
|
|
44
|
+
// repo_capability Path Y (augments via M-ADR 0143 feature-grounding) —
|
|
45
45
|
// `capability_feature ⋈ feature_repo`. Adopters who have adopted the
|
|
46
46
|
// feature-grounding extractor get many additional rows.
|
|
47
47
|
//
|
|
48
|
-
// feature_repo: ADR 0143's feature-grounding extractor populates this in
|
|
48
|
+
// feature_repo: M-ADR 0143's feature-grounding extractor populates this in
|
|
49
49
|
// build.ts (10a). The forward declared `contributes_to.artifact_type:
|
|
50
50
|
// feature-set` path is documented in the metamodel but unused by any
|
|
51
51
|
// observed adopter — left empty here pending first-adopter authoring.
|
|
@@ -68,7 +68,7 @@ import { normaliseAdditionalContextEntry, REPO_CONFIG_CONTRACT_KINDS, } from "./
|
|
|
68
68
|
* identity-DEPENDENT, and so is this. A first review of D8 flagged the opposite
|
|
69
69
|
* and was wrong.
|
|
70
70
|
*
|
|
71
|
-
* Caller supplies `existingIds` so this only ever ADDS: the ADR 0143
|
|
71
|
+
* Caller supplies `existingIds` so this only ever ADDS: the M-ADR 0143
|
|
72
72
|
* feature-grounding extractor and the capability-spec walker own their rows and
|
|
73
73
|
* are authoritative where they overlap.
|
|
74
74
|
*/
|
|
@@ -109,7 +109,7 @@ export function deriveFeatureRepoFromServices(inputs) {
|
|
|
109
109
|
export function buildRepoEdges(inputs) {
|
|
110
110
|
const repoCapability = collectRepoCapability(inputs);
|
|
111
111
|
const repoContract = collectRepoContract(inputs);
|
|
112
|
-
// feature_repo is populated later in build.ts by the ADR 0143
|
|
112
|
+
// feature_repo is populated later in build.ts by the M-ADR 0143
|
|
113
113
|
// feature-grounding extractor; this builder doesn't contribute.
|
|
114
114
|
const featureRepo = [];
|
|
115
115
|
return { repoCapability, repoContract, featureRepo };
|
|
@@ -222,9 +222,9 @@ function collectRepoCapability(inputs) {
|
|
|
222
222
|
missingPrimaryRepoSvcs++;
|
|
223
223
|
}
|
|
224
224
|
if (missingPrimaryRepoSvcs > 0) {
|
|
225
|
-
inputs.findings.add("orphan", `repo_capability: ${missingPrimaryRepoSvcs} capability_service rows reference services with empty primary_repo_id — reverse-method Path X (capability_service ⋈ services.primary_repo_id) skipped those edges (per ADR 0157 §4.3)`);
|
|
225
|
+
inputs.findings.add("orphan", `repo_capability: ${missingPrimaryRepoSvcs} capability_service rows reference services with empty primary_repo_id — reverse-method Path X (capability_service ⋈ services.primary_repo_id) skipped those edges (per M-ADR 0157 §4.3)`);
|
|
226
226
|
}
|
|
227
|
-
// Reverse path Y — capability_feature ⋈ feature_repo (transitive via ADR 0143).
|
|
227
|
+
// Reverse path Y — capability_feature ⋈ feature_repo (transitive via M-ADR 0143).
|
|
228
228
|
const featuresToRepos = new Map();
|
|
229
229
|
for (const fr of featureRepoRows) {
|
|
230
230
|
const fid = String(fr["feature_id"] ?? "");
|
|
@@ -299,7 +299,7 @@ function collectRepoContract(inputs) {
|
|
|
299
299
|
}
|
|
300
300
|
// Reverse path X (universal) — service_contract ⋈ services.primary_repo_id.
|
|
301
301
|
// A service exposing a contract is implemented by a repo; the repo "owns"
|
|
302
|
-
// the contract by transitive inference. Added per ADR 0157 §4.2 amendment
|
|
302
|
+
// the contract by transitive inference. Added per M-ADR 0157 §4.2 amendment
|
|
303
303
|
// (an early adopter's source_repo_id was 0/30 populated, so
|
|
304
304
|
// a derivation that depends only on it leaves them empty).
|
|
305
305
|
const repoBySvcId = new Map();
|
|
@@ -323,10 +323,10 @@ function collectRepoContract(inputs) {
|
|
|
323
323
|
emit(repoId, ctrId);
|
|
324
324
|
}
|
|
325
325
|
if (missingPrimaryRepoSvcs > 0) {
|
|
326
|
-
inputs.findings.add("orphan", `repo_contract: ${missingPrimaryRepoSvcs} service_contract rows reference services with empty primary_repo_id — reverse-method Path X (service_contract ⋈ services.primary_repo_id) skipped those edges (per ADR 0157 §4.2)`);
|
|
326
|
+
inputs.findings.add("orphan", `repo_contract: ${missingPrimaryRepoSvcs} service_contract rows reference services with empty primary_repo_id — reverse-method Path X (service_contract ⋈ services.primary_repo_id) skipped those edges (per M-ADR 0157 §4.2)`);
|
|
327
327
|
}
|
|
328
328
|
// Reverse path Y — contracts.source_repo_id denormalisation.
|
|
329
|
-
// Adopters who carry OpenAPI x-source-repo hints (per ADR 0110) or whose
|
|
329
|
+
// Adopters who carry OpenAPI x-source-repo hints (per M-ADR 0110) or whose
|
|
330
330
|
// contracts builder otherwise populates source_repo_id get one row per
|
|
331
331
|
// (source_repo_id, contract.id) for free.
|
|
332
332
|
for (const c of contractRows) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Repositories discoverer — emits `repositories.csv` rows from
|
|
2
2
|
// `orchestration/repo-config/<domain>/<repo>.yaml` (recursive).
|
|
3
3
|
//
|
|
4
|
-
// Per ADR 0134 + Q7.1 resolution: `service_type` is constrained to the
|
|
4
|
+
// Per M-ADR 0134 + Q7.1 resolution: `service_type` is constrained to the
|
|
5
5
|
// canonical `enums.service_type` set (api_service, worker, gateway,
|
|
6
6
|
// frontend, mobile, library, infrastructure, documentation). Unknown
|
|
7
7
|
// values log a `[finding] convention` and are kept verbatim — the FK
|
|
@@ -33,16 +33,17 @@ const LOC_REPORT_REL = join("docs", "platform", "reference", "loc-report.csv");
|
|
|
33
33
|
const REPOS_DIR_REL = "repos";
|
|
34
34
|
const REVERSE_METADATA_BASENAME = "reverse-metadata.yaml";
|
|
35
35
|
// The contract kinds a repo-config `contracts:` block keys, under `contracts/<kind>/`. The sdd-index reader
|
|
36
|
-
// accepts the same
|
|
37
|
-
// so a declared `.zed` schema vanished without a finding.
|
|
38
|
-
// question, on this schema.
|
|
39
|
-
export const REPO_CONFIG_CONTRACT_KINDS = ["openapi", "proto", "avro", "spicedb"];
|
|
36
|
+
// accepts the same five; `spicedb` (an `authz` contract) was once missing here, and zod strips an unknown key,
|
|
37
|
+
// so a declared `.zed` schema vanished without a finding. `cli` (schema 4.1.0) declares the command manifest a
|
|
38
|
+
// command-line repository serves. GraphQL is not keyed in repo-config: that is its own question, on this schema.
|
|
39
|
+
export const REPO_CONFIG_CONTRACT_KINDS = ["openapi", "proto", "avro", "spicedb", "cli"];
|
|
40
40
|
const ContractsBlockSchema = z
|
|
41
41
|
.object({
|
|
42
42
|
openapi: z.array(z.string()).optional(),
|
|
43
43
|
proto: z.array(z.string()).optional(),
|
|
44
44
|
avro: z.array(z.string()).optional(),
|
|
45
45
|
spicedb: z.array(z.string()).optional(),
|
|
46
|
+
cli: z.array(z.string()).optional(),
|
|
46
47
|
})
|
|
47
48
|
.optional();
|
|
48
49
|
const EventEntrySchema = z.object({
|
|
@@ -172,13 +173,13 @@ export async function buildRepositories(inputs) {
|
|
|
172
173
|
inputs.findings.add("convention", `${cfg.relPath}: missing required \`domain\` field`);
|
|
173
174
|
}
|
|
174
175
|
else if (!inputs.knownDomainIds.has(domain)) {
|
|
175
|
-
// Per ADR 0136: leave domain_id empty when the declared domain
|
|
176
|
+
// Per M-ADR 0136: leave domain_id empty when the declared domain
|
|
176
177
|
// doesn't resolve locally (typical for app/integration SDDs whose
|
|
177
178
|
// repo-config declares "applications"/"integrations").
|
|
178
179
|
//
|
|
179
180
|
// NOT `orphan`, and the argument is about TIER rather than a relabelling
|
|
180
181
|
// of the same claim. A locally-unresolvable domain is two different
|
|
181
|
-
// things and THIS TIER CANNOT TELL THEM APART: ADR 0136 externality (the
|
|
182
|
+
// things and THIS TIER CANNOT TELL THEM APART: M-ADR 0136 externality (the
|
|
182
183
|
// domain is owned by a sibling SDD — correct, expected, nothing to fix)
|
|
183
184
|
// or a genuinely dangling domain name (a typo). The discriminator lives
|
|
184
185
|
// at estate scope, not here. Measured across two estates: 137
|
|
@@ -190,7 +191,7 @@ export async function buildRepositories(inputs) {
|
|
|
190
191
|
// §4.2 names. `inferred` records the builder's documented resolution
|
|
191
192
|
// (empty the column, keep the repository) without claiming a defect this
|
|
192
193
|
// build cannot see.
|
|
193
|
-
inputs.findings.add("inferred", `${cfg.relPath}: domain "${domain}" has no docs/domains/${domain}/ directory — domain_id left empty per ADR 0136. Whether that is external ownership or a dangling name is decidable only at estate scope`);
|
|
194
|
+
inputs.findings.add("inferred", `${cfg.relPath}: domain "${domain}" has no docs/domains/${domain}/ directory — domain_id left empty per M-ADR 0136. Whether that is external ownership or a dangling name is decidable only at estate scope`);
|
|
194
195
|
resolvedDomain = "";
|
|
195
196
|
}
|
|
196
197
|
const loc = locByRepo.get(repoId);
|
|
@@ -251,19 +252,23 @@ async function loadRepoConfigs(sddPath, findings) {
|
|
|
251
252
|
raw = parseYaml(text);
|
|
252
253
|
}
|
|
253
254
|
catch (err) {
|
|
254
|
-
findings.add("convention", `failed to parse YAML at ${relative(sddPath, path)}: ${err.message}
|
|
255
|
+
findings.add("convention", `failed to parse YAML at ${relative(sddPath, path)}: ${err.message} — repository dropped`, "error", relative(sddPath, path));
|
|
255
256
|
continue;
|
|
256
257
|
}
|
|
257
258
|
if (!raw || typeof raw !== "object") {
|
|
258
|
-
findings.add("convention", `${relative(sddPath, path)}: not an object —
|
|
259
|
+
findings.add("convention", `${relative(sddPath, path)}: not an object — repository dropped`, "error", relative(sddPath, path));
|
|
259
260
|
continue;
|
|
260
261
|
}
|
|
262
|
+
// A repo-config that cannot be read takes its repository, services and
|
|
263
|
+
// capability edges out of the catalogue with it. That is an error, not
|
|
264
|
+
// advice: as a warning it passed validation while the rows vanished, and
|
|
265
|
+
// only a downstream aggregate's FK check noticed.
|
|
261
266
|
const parsed = RepoConfigSchema.safeParse(raw);
|
|
262
267
|
if (!parsed.success) {
|
|
263
268
|
const issues = parsed.error.issues
|
|
264
269
|
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
265
270
|
.join("; ");
|
|
266
|
-
findings.add("convention", `${relative(sddPath, path)}: schema violation — ${issues}
|
|
271
|
+
findings.add("convention", `${relative(sddPath, path)}: schema violation — ${issues} — repository dropped`, "error", relative(sddPath, path));
|
|
267
272
|
continue;
|
|
268
273
|
}
|
|
269
274
|
const repoFull = parsed.data.repo;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
// Screen composition-tree analysis for openui-lang blocks (per ADR 0168).
|
|
1
|
+
// Screen composition-tree analysis for openui-lang blocks (per M-ADR 0168).
|
|
2
2
|
//
|
|
3
3
|
// The screens builder (screens.ts) lowers a ```openui-lang block into catalogue
|
|
4
|
-
// rows by flat token-scanning. ADR 0168 adds a *fidelity floor*: the block must
|
|
4
|
+
// rows by flat token-scanning. M-ADR 0168 adds a *fidelity floor*: the block must
|
|
5
5
|
// be a faithful tree, not a flat / empty / mis-rooted "widget bag". That needs
|
|
6
6
|
// the structural shape the flat scan throws away — so this module parses the
|
|
7
7
|
// block into a tree and computes the metrics the depth findings reason about.
|
|
8
8
|
//
|
|
9
9
|
// Pure functions, no I/O. The metrics reason about TREE SHAPE only — never about
|
|
10
|
-
// any component vocabulary (adopter-agnostic by construction, ADR 0168 §4.5).
|
|
10
|
+
// any component vocabulary (adopter-agnostic by construction, M-ADR 0168 §4.5).
|
|
11
11
|
//
|
|
12
|
-
// Grammar recap (ADR 0166 / openui-lang-grammar-reference.md): one
|
|
12
|
+
// Grammar recap (M-ADR 0166 / openui-lang-grammar-reference.md): one
|
|
13
13
|
// `identifier = Expression` statement per line; the first statement is `root`;
|
|
14
14
|
// composition is by identifier reference inside list literals. A component is a
|
|
15
15
|
// Capitalised call head (`Name(`); local references are lowercase ids that are
|
|
@@ -73,7 +73,7 @@ function parseArgsActions(rhs) {
|
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* Strip openui-lang inline comments: from the first *unquoted* `#` to
|
|
76
|
-
* end-of-line, per line (ADR 0168 §4.4, defect g). A `Capitalised(` token inside
|
|
76
|
+
* end-of-line, per line (M-ADR 0168 §4.4, defect g). A `Capitalised(` token inside
|
|
77
77
|
* a comment must not be lexed as a component. `#` inside a string literal is
|
|
78
78
|
* preserved.
|
|
79
79
|
*/
|
|
@@ -113,7 +113,7 @@ function parseStatements(cleanBlock) {
|
|
|
113
113
|
const rhs = m[2];
|
|
114
114
|
// A component statement's RHS is a call (`Text("Q4")`) or a reference — never a
|
|
115
115
|
// bare string literal. A line like `action="basket-updated"` (the block-level
|
|
116
|
-
// screen_action messaging contract, ADR 0166) matches the statement shape with
|
|
116
|
+
// screen_action messaging contract, M-ADR 0166) matches the statement shape with
|
|
117
117
|
// id=`action`; skip it so it isn't lexed as a phantom component (and so a node
|
|
118
118
|
// carrying an `action=` attr doesn't pick it up as a spurious child).
|
|
119
119
|
if (/^["']/.test(rhs))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Screens builder — emits the UI screens catalogue family (per ADR 0166):
|
|
1
|
+
// Screens builder — emits the UI screens catalogue family (per M-ADR 0166):
|
|
2
2
|
// screens, ui_components, screen_component, screen_feature,
|
|
3
3
|
// persona_screen, screen_action.
|
|
4
4
|
//
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// ---
|
|
8
8
|
// screen: <slug> # optional; defaults to filename stem
|
|
9
9
|
// route: /path # optional
|
|
10
|
-
// status: working # optional (ADR 0165 maturity vocabulary)
|
|
10
|
+
// status: working # optional (M-ADR 0165 maturity vocabulary)
|
|
11
11
|
// persona_access: [<persona-slug>, ...] # -> persona_screen
|
|
12
12
|
// realises: [<feature-slug>, ...] # -> screen_feature
|
|
13
13
|
// ---
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// toolkit projects an app's router + component tree into the same shape (the
|
|
26
26
|
// upstream step that produces these artefacts).
|
|
27
27
|
//
|
|
28
|
-
// Per ADR 0135 the rows are identical regardless of authoring direction; only
|
|
28
|
+
// Per M-ADR 0135 the rows are identical regardless of authoring direction; only
|
|
29
29
|
// the `source` discriminator differs (screen-spec vs screen-extraction).
|
|
30
30
|
//
|
|
31
31
|
// Application-product profile. Like personas, the walker reads `screens/` at
|
|
@@ -36,9 +36,9 @@ import { basename, join } from "node:path";
|
|
|
36
36
|
import { parse as parseYaml } from "yaml";
|
|
37
37
|
import { analyzeComposition, maskStringLiterals, stripBlockComments } from "./screen-tree.js";
|
|
38
38
|
import { isContainerArchetype } from "../archetype/resolver.js";
|
|
39
|
-
/** Method default for the flat-composition threshold (ADR 0168 §4.2). */
|
|
39
|
+
/** Method default for the flat-composition threshold (M-ADR 0168 §4.2). */
|
|
40
40
|
const DEFAULT_FLAT_THRESHOLD = 6;
|
|
41
|
-
/** The only maturity value that asserts a faithful, complete composition (ADR 0168 §4.3). */
|
|
41
|
+
/** The only maturity value that asserts a faithful, complete composition (M-ADR 0168 §4.3). */
|
|
42
42
|
const COMPLETENESS_STATUS = "working";
|
|
43
43
|
const SCREENS_DIR_REL = "screens";
|
|
44
44
|
/** Subdir of screens/ holding rendered wireframe artifacts — never screen specs. */
|
|
@@ -72,7 +72,7 @@ async function collectScreenSpecFiles(dir) {
|
|
|
72
72
|
await walk("");
|
|
73
73
|
return out.sort();
|
|
74
74
|
}
|
|
75
|
-
// Recognised standard maturity vocabulary (ADR 0165); other values pass through.
|
|
75
|
+
// Recognised standard maturity vocabulary (M-ADR 0165); other values pass through.
|
|
76
76
|
const STANDARD_MATURITY = new Set(["working", "provisional", "unknown", "absent"]);
|
|
77
77
|
export async function buildScreens(inputs) {
|
|
78
78
|
const screens = [];
|
|
@@ -105,7 +105,7 @@ export async function buildScreens(inputs) {
|
|
|
105
105
|
}
|
|
106
106
|
const name = extractH1(text) || (typeof fm.name === "string" ? fm.name : stem);
|
|
107
107
|
const status = typeof fm.status === "string" && fm.status !== "" ? maturity(fm.status, inputs.findings, screenId) : "";
|
|
108
|
-
// Direction discriminator (ADR 0135): forward authoring is `screen-spec`,
|
|
108
|
+
// Direction discriminator (M-ADR 0135): forward authoring is `screen-spec`,
|
|
109
109
|
// reverse extraction `screen-extraction`. Read from frontmatter, default
|
|
110
110
|
// forward; only these two are recognised (others pass through, flagged).
|
|
111
111
|
const source = screenSource(fm.source, inputs.findings, screenId);
|
|
@@ -130,7 +130,7 @@ export async function buildScreens(inputs) {
|
|
|
130
130
|
evidence_class: evidenceClass(fm),
|
|
131
131
|
source_sdd_id: "",
|
|
132
132
|
});
|
|
133
|
-
// Evidence integrity (ADR 0168 §4.2, defect f): a reverse-extracted screen
|
|
133
|
+
// Evidence integrity (M-ADR 0168 §4.2, defect f): a reverse-extracted screen
|
|
134
134
|
// must cite its route + component evidence with a `[^…]` footnote. Forward-
|
|
135
135
|
// authored screens make no such claim, so the check is reverse-scoped.
|
|
136
136
|
if (source === "screen-extraction" && !/\[\^[^\]]+\]/.test(text)) {
|
|
@@ -142,7 +142,7 @@ export async function buildScreens(inputs) {
|
|
|
142
142
|
}
|
|
143
143
|
else {
|
|
144
144
|
// Strip inline `#` comments before any scan so a `Capitalised(` token in a
|
|
145
|
-
// comment can't emit a phantom component (ADR 0168 §4.4, defect g).
|
|
145
|
+
// comment can't emit a phantom component (M-ADR 0168 §4.4, defect g).
|
|
146
146
|
const clean = stripBlockComments(block);
|
|
147
147
|
// Component invocations: Capitalised call heads (e.g. Stack(, StatCard().
|
|
148
148
|
// Local id references (lowercase, no parens) and list literals are skipped.
|
|
@@ -168,12 +168,12 @@ export async function buildScreens(inputs) {
|
|
|
168
168
|
const aKey = `${screenId}__${screenSlug(action)}`;
|
|
169
169
|
if (!screenAction.has(aKey)) {
|
|
170
170
|
// event_id left empty: external-capable, resolved against the event
|
|
171
|
-
// catalogue when an explicit mapping exists (best-effort per ADR 0166).
|
|
171
|
+
// catalogue when an explicit mapping exists (best-effort per M-ADR 0166).
|
|
172
172
|
screenAction.set(aKey, { id: aKey, screen_id: screenId, action, event_id: "", source_sdd_id: "" });
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
// Composition-depth validation (ADR 0168): structural fidelity floor +
|
|
176
|
-
// status gate + the archetype-aware root check (ADR 0169). The vocabulary-
|
|
175
|
+
// Composition-depth validation (M-ADR 0168): structural fidelity floor +
|
|
176
|
+
// status gate + the archetype-aware root check (M-ADR 0169). The vocabulary-
|
|
177
177
|
// free checks reason about tree shape + status only; root-is-leaf consults
|
|
178
178
|
// the archetype map when the screen declares a `platform:`.
|
|
179
179
|
const platform = typeof fm.platform === "string" ? fm.platform : undefined;
|
|
@@ -192,7 +192,7 @@ export async function buildScreens(inputs) {
|
|
|
192
192
|
if (!fid) {
|
|
193
193
|
// App/integration screens can realise a platform-resident feature
|
|
194
194
|
// (MFE-over-platform: the platform owns the capability tree and its
|
|
195
|
-
// features — ADR 0177 single-slug). Emit a deferred cross-SDD edge
|
|
195
|
+
// features — M-ADR 0177 single-slug). Emit a deferred cross-SDD edge
|
|
196
196
|
// carrying the bare realises slug; the aggregator resolves it to the
|
|
197
197
|
// platform feature id, and the FK validator logs it `[finding] external`
|
|
198
198
|
// for this build (screen_feature.feature_id is external_for_profiles).
|
|
@@ -261,24 +261,24 @@ function emitDepthFindings(findings, screenId, status, m, ctx) {
|
|
|
261
261
|
findings.add("coverage", `screen ${screenId}: composition is flat — root has ${m.rootChildCount} children at depth ${m.maxDepth} (screen-flat-composition)`);
|
|
262
262
|
}
|
|
263
263
|
// Status gate (keystone): `working` is the only maturity that asserts a
|
|
264
|
-
// faithful, complete composition (ADR 0168 §4.3). provisional/unknown/absent
|
|
264
|
+
// faithful, complete composition (M-ADR 0168 §4.3). provisional/unknown/absent
|
|
265
265
|
// make no completeness claim and are not gated.
|
|
266
266
|
if (status === COMPLETENESS_STATUS && (empty || flat)) {
|
|
267
267
|
findings.add("convention", `screen ${screenId}: status "${COMPLETENESS_STATUS}" exceeds composition depth — block is ${empty ? "empty" : "flat"} (screen-status-exceeds-depth)`, "error");
|
|
268
268
|
}
|
|
269
|
-
// Mis-rooted (ADR 0168 §4.2 screen-root-is-leaf, archetype-aware per ADR 0169):
|
|
269
|
+
// Mis-rooted (M-ADR 0168 §4.2 screen-root-is-leaf, archetype-aware per M-ADR 0169):
|
|
270
270
|
// root is a leaf/control rather than a screen container. isContainer combines
|
|
271
271
|
// the appears-with-children heuristic (containers) with the archetype map.
|
|
272
272
|
// Only fires when the screen declares a known `platform:` — without a map the
|
|
273
273
|
// heuristic alone can't distinguish an empty container root (Stack([])) from a
|
|
274
|
-
// genuine leaf root (Button([])), so the check no-ops (ADR 0168 §4.5).
|
|
274
|
+
// genuine leaf root (Button([])), so the check no-ops (M-ADR 0168 §4.5).
|
|
275
275
|
if (m.rootComponent !== "" && m.rootChildCount === 0) {
|
|
276
276
|
const byMap = isContainerArchetype(m.rootComponent, platform, overlay);
|
|
277
277
|
if (byMap === false && !m.containers.has(m.rootComponent)) {
|
|
278
278
|
findings.add("convention", `screen ${screenId}: root component "${m.rootComponent}" is a leaf, not a container (screen-root-is-leaf)`, "error");
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
|
-
// Styling-primitive leakage (ADR 0168 §4.2 screen-styling-primitive-as-structure):
|
|
281
|
+
// Styling-primitive leakage (M-ADR 0168 §4.2 screen-styling-primitive-as-structure):
|
|
282
282
|
// an adopter-flagged decorative primitive composed as a structural component.
|
|
283
283
|
// No-ops when the adopter declares no decorative set.
|
|
284
284
|
if (decorativePrimitives !== undefined && decorativePrimitives.size > 0) {
|
|
@@ -317,7 +317,7 @@ function screenSlug(s) {
|
|
|
317
317
|
function normalise(s) {
|
|
318
318
|
return s.trim().toLowerCase().replace(/\s+/g, " ");
|
|
319
319
|
}
|
|
320
|
-
// Recognised direction discriminators (ADR 0135). Others pass through flagged.
|
|
320
|
+
// Recognised direction discriminators (M-ADR 0135). Others pass through flagged.
|
|
321
321
|
const SCREEN_SOURCES = new Set(["screen-spec", "screen-extraction"]);
|
|
322
322
|
function screenSource(value, findings, screenId) {
|
|
323
323
|
if (typeof value !== "string" || value.trim() === "")
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// the platform-root SDD validates internally first per the fk-validator
|
|
23
23
|
// optimistic-resolve rule.
|
|
24
24
|
//
|
|
25
|
-
// Pattern documented in sdd-method ADR 0147.
|
|
25
|
+
// Pattern documented in sdd-method M-ADR 0147.
|
|
26
26
|
import { readFile, readdir } from "node:fs/promises";
|
|
27
27
|
import { join } from "node:path";
|
|
28
28
|
const REPOS_DIR_REL = "repos";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Primary reverse-method walker for the service_entity edge catalogue per
|
|
2
|
-
// ADR 0159 §4.5. Reads per-domain `## Data Store Mapping` markdown tables in
|
|
2
|
+
// M-ADR 0159 §4.5. Reads per-domain `## Data Store Mapping` markdown tables in
|
|
3
3
|
// `docs/domains/{domain}/architecture/data-model.mermaid.md` files.
|
|
4
4
|
//
|
|
5
|
-
// Prototype findings (per ADR 0159 — data-store-mapping table variations):
|
|
5
|
+
// Prototype findings (per M-ADR 0159 — data-store-mapping table variations):
|
|
6
6
|
// - Two heading variants: `## Data Store Mapping` and `## Data Store Inventory`
|
|
7
7
|
// (the latter is store-centric — different schema, no Entity column — and
|
|
8
8
|
// gets skipped with a convention finding).
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// - Cross-domain entity references in tables resolve against entities.csv
|
|
15
15
|
// from any domain (not just the table's own domain) — same-domain first.
|
|
16
16
|
//
|
|
17
|
-
// Service-name resolution rules (ADR 0159 §4.7):
|
|
17
|
+
// Service-name resolution rules (M-ADR 0159 §4.7):
|
|
18
18
|
// 1. Try alias-map exact match (case-insensitive on display name).
|
|
19
19
|
// 2. Try entitySlug(display_name) ⋈ entitySlug(services.name).
|
|
20
20
|
// 3. Try entitySlug(display_name) ⋈ last_segment(services.id).
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// 0197 §4.1 — a dangling reference to it resolves to an orphan finding
|
|
11
11
|
// elsewhere, not a crash here).
|
|
12
12
|
//
|
|
13
|
-
// Per ADR 0134 + Q7.1 resolution, runtime kinds are
|
|
13
|
+
// Per M-ADR 0134 + Q7.1 resolution, runtime kinds are
|
|
14
14
|
// {api_service, worker, gateway, frontend, mobile} — read from the
|
|
15
15
|
// canonical schema's enums.service_type block where
|
|
16
16
|
// `emits_service_row: true`. The runtime-kind gate applies only to the
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
// service-architecture-parser module.
|
|
30
30
|
//
|
|
31
31
|
// The `description` column has two authoring sources, honoured per the
|
|
32
|
-
// forward/reverse parity principle (ADR 0157, extended to node columns by
|
|
33
|
-
// ADR 0163):
|
|
32
|
+
// forward/reverse parity principle (M-ADR 0157, extended to node columns by
|
|
33
|
+
// M-ADR 0163):
|
|
34
34
|
// - forward: `orchestration/repo-config/<repo>.yaml` `description:` —
|
|
35
35
|
// the deliberate spec, authored by forward-method adopters.
|
|
36
36
|
// - reverse: the arch doc's `## Purpose` lead paragraph.
|
|
37
37
|
// When both are present for the same service the forward repo-config
|
|
38
|
-
// description wins (matching the ADR 0156 / 0162 merge convention) and a
|
|
38
|
+
// description wins (matching the M-ADR 0156 / 0162 merge convention) and a
|
|
39
39
|
// `divergence` finding records the conflict. In practice an adopter has
|
|
40
40
|
// only one of the two, so this is usually a plain coalesce.
|
|
41
41
|
//
|
|
@@ -111,7 +111,7 @@ export async function buildServices(inputs) {
|
|
|
111
111
|
// repos/<group>/<repo>/docs/..., so a recursive walk is required.
|
|
112
112
|
// Services whose architecture doc lives in a federated member SDD (rather
|
|
113
113
|
// than this SDD's own repos/) resolve to no entry here — their metadata is
|
|
114
|
-
// an aggregation-time concern (ADR 0163 P2), not a local build gap.
|
|
114
|
+
// an aggregation-time concern (M-ADR 0163 P2), not a local build gap.
|
|
115
115
|
const archIndex = await indexServiceArchitectures(inputs.sddPath);
|
|
116
116
|
const repoDescriptions = inputs.repoDescriptions ?? new Map();
|
|
117
117
|
const declaredByRepo = inputs.declaredServicesByRepo ?? new Map();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// system_external_dependency edge builder — emits one row per
|
|
2
2
|
// `Rel(...)` line on an L1/L2 diagram that connects the SDD's own system
|
|
3
|
-
// (or one of its containers) to an external system (per ADR 0155).
|
|
3
|
+
// (or one of its containers) to an external system (per M-ADR 0155).
|
|
4
4
|
//
|
|
5
5
|
// Resolution: the c4-external-systems builder supplies `externalIdByEntryId`
|
|
6
6
|
// (diagram entry-id → ext-... row id). For each `Rel(a, b, "label")` where
|
|
7
7
|
// exactly one endpoint is a known external entry-id, we emit an edge from
|
|
8
8
|
// the SDD's c4_systems row to that external system. Container-level external
|
|
9
|
-
// dependencies are intentionally collapsed to the system level — ADR 0155
|
|
9
|
+
// dependencies are intentionally collapsed to the system level — M-ADR 0155
|
|
10
10
|
// §4.2 keeps the edge system-scoped.
|
|
11
11
|
import { readFile } from "node:fs/promises";
|
|
12
12
|
import { collectC4DiagramFiles } from "./c4-external-systems.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Emitted by sdd-cli's YAML emitter with no line folding; the three header scalars are quoted for the
|
|
8
8
|
# textual readers downstream. The one deliberate difference from the authored form is schema_status:
|
|
9
9
|
# the authored form says canonical; a projection never does.
|
|
10
|
-
schema_version: "4.
|
|
10
|
+
schema_version: "4.1.0"
|
|
11
11
|
schema_owner: "sdd-method"
|
|
12
12
|
schema_status: "generated"
|
|
13
13
|
enums:
|
|
@@ -361,6 +361,7 @@ enums:
|
|
|
361
361
|
- value: graphql
|
|
362
362
|
- value: rpc
|
|
363
363
|
- value: authz
|
|
364
|
+
- value: cli
|
|
364
365
|
description: Contract kind. Bound at 3.13.0 from the column descriptions; measured against both reference estates before binding (M-ADR 0205 §4.4).
|
|
365
366
|
contract_status:
|
|
366
367
|
values:
|
|
@@ -1552,7 +1553,7 @@ catalogues:
|
|
|
1552
1553
|
- name: kind
|
|
1553
1554
|
type: string
|
|
1554
1555
|
nullable: false
|
|
1555
|
-
description: rest | graphql | rpc | event | authz. rpc covers Proto/gRPC; event covers Avro schemas; authz covers authorization/policy schemas (SpiceDB .zed and equivalents), added because an adopter contract agreement gate certified .zed schemas as contracts while no kind could represent them.
|
|
1556
|
+
description: rest | graphql | rpc | event | authz | cli. rpc covers Proto/gRPC; event covers Avro schemas; authz covers authorization/policy schemas (SpiceDB .zed and equivalents), added because an adopter contract agreement gate certified .zed schemas as contracts while no kind could represent them; cli covers a command-line interface's command manifest (its commands, options and exit codes), added at 4.1.0 because a command-line tool often serves no REST, RPC, event or authorization contract, so its behaviour baselines had nothing to name as their surface.
|
|
1556
1557
|
- name: version
|
|
1557
1558
|
type: string
|
|
1558
1559
|
nullable: true
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// M-ADR 0203 §4.4, the cut's acid test.
|
|
3
3
|
//
|
|
4
4
|
// From 3.0.0 a contract id carries its KIND component: `{domain}-{stem}-{kind}`
|
|
5
|
-
// (kind ∈ rest | graphql | rpc | event | authz). The kindless 2.x form made one
|
|
5
|
+
// (kind ∈ rest | graphql | rpc | event | authz | cli). The kindless 2.x form made one
|
|
6
6
|
// measured collision class unrepresentable: a service published as two contract
|
|
7
7
|
// families (`calendar-service.yaml` + `calendar_service.proto`) minted one id,
|
|
8
8
|
// costing a live estate one row and two edges permanently, and mislabelling
|
|
@@ -281,47 +281,47 @@ export function renderContractCoverage(res, tier) {
|
|
|
281
281
|
}
|
|
282
282
|
// ─── the nine Phase-0 gate checks ────────────────────────────────────────
|
|
283
283
|
export function renderEventCatalogueCheck(gaps) {
|
|
284
|
-
return gaps.map((g) => `[finding] coverage: domain ${g.domain} event-catalog.yaml declares ${g.declared} events but events.csv contains ${g.emitted} rows for that domain (per ADR 0156 Phase 0)`);
|
|
284
|
+
return gaps.map((g) => `[finding] coverage: domain ${g.domain} event-catalog.yaml declares ${g.declared} events but events.csv contains ${g.emitted} rows for that domain (per M-ADR 0156 Phase 0)`);
|
|
285
285
|
}
|
|
286
286
|
export function renderRepoEdgesCheck(res) {
|
|
287
287
|
const lines = [];
|
|
288
288
|
if (res.repoCapabilityGap) {
|
|
289
289
|
const g = res.repoCapabilityGap;
|
|
290
|
-
lines.push(`[finding] coverage: repo_capability.csv is empty but ${g.capabilityService} capability_service rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method derivation likely missing (per ADR 0157 §4.3, Phase 0)`);
|
|
290
|
+
lines.push(`[finding] coverage: repo_capability.csv is empty but ${g.capabilityService} capability_service rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method derivation likely missing (per M-ADR 0157 §4.3, Phase 0)`);
|
|
291
291
|
}
|
|
292
292
|
if (res.repoContractForwardGap) {
|
|
293
293
|
const g = res.repoContractForwardGap;
|
|
294
|
-
lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.contracts} contracts and ${g.repoConfigsWithContracts} repo-configs with contracts: blocks present — forward-method derivation likely missing (per ADR 0157 §4.2, Phase 0)`);
|
|
294
|
+
lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.contracts} contracts and ${g.repoConfigsWithContracts} repo-configs with contracts: blocks present — forward-method derivation likely missing (per M-ADR 0157 §4.2, Phase 0)`);
|
|
295
295
|
}
|
|
296
296
|
if (res.repoContractPathXGap) {
|
|
297
297
|
const g = res.repoContractPathXGap;
|
|
298
|
-
lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.serviceContracts} service_contract rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method Path X (service_contract ⋈ services.primary_repo_id) derivation likely missing (per ADR 0157 §4.2, Phase 0)`);
|
|
298
|
+
lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.serviceContracts} service_contract rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method Path X (service_contract ⋈ services.primary_repo_id) derivation likely missing (per M-ADR 0157 §4.2, Phase 0)`);
|
|
299
299
|
}
|
|
300
300
|
if (res.repoContractReverseGap) {
|
|
301
301
|
const g = res.repoContractReverseGap;
|
|
302
|
-
lines.push(`[finding] coverage: repo_contract.csv is empty with ${g.contracts} contracts present and no repo-config.contracts: blocks or service_contract rows — reverse-method derivation via contracts.source_repo_id likely missing (per ADR 0157 §4.2, Phase 0)`);
|
|
302
|
+
lines.push(`[finding] coverage: repo_contract.csv is empty with ${g.contracts} contracts present and no repo-config.contracts: blocks or service_contract rows — reverse-method derivation via contracts.source_repo_id likely missing (per M-ADR 0157 §4.2, Phase 0)`);
|
|
303
303
|
}
|
|
304
304
|
return lines;
|
|
305
305
|
}
|
|
306
306
|
export function renderGlossaryXrefCheck(finding) {
|
|
307
307
|
return finding
|
|
308
308
|
? [
|
|
309
|
-
`[finding] coverage: docs/platform/reference/glossary-alignment-manifest.yaml exists but glossary_xref.csv emits 0 rows — multi-source derivation likely missing (per ADR 0158 §4.5, Phase 0)`,
|
|
309
|
+
`[finding] coverage: docs/platform/reference/glossary-alignment-manifest.yaml exists but glossary_xref.csv emits 0 rows — multi-source derivation likely missing (per M-ADR 0158 §4.5, Phase 0)`,
|
|
310
310
|
]
|
|
311
311
|
: [];
|
|
312
312
|
}
|
|
313
313
|
export function renderDataModelEntitiesCheck(res) {
|
|
314
314
|
const lines = [];
|
|
315
315
|
if (res.entitiesGap) {
|
|
316
|
-
lines.push(`[finding] coverage: ${res.entitiesGap.domainModels} domain-model files catalogued but entities.csv emits 0 rows — reverse-method entity extraction missing (per ADR 0159 Phase 0)`);
|
|
316
|
+
lines.push(`[finding] coverage: ${res.entitiesGap.domainModels} domain-model files catalogued but entities.csv emits 0 rows — reverse-method entity extraction missing (per M-ADR 0159 Phase 0)`);
|
|
317
317
|
}
|
|
318
318
|
if (res.serviceEntityGap) {
|
|
319
319
|
const g = res.serviceEntityGap;
|
|
320
|
-
lines.push(`[finding] coverage: ${g.entities} entities + ${g.services} services catalogued but service_entity.csv emits 0 rows — Data Store Mapping ingestion missing (per ADR 0159 Phase 0)`);
|
|
320
|
+
lines.push(`[finding] coverage: ${g.entities} entities + ${g.services} services catalogued but service_entity.csv emits 0 rows — Data Store Mapping ingestion missing (per M-ADR 0159 Phase 0)`);
|
|
321
321
|
}
|
|
322
322
|
if (res.containerEntityGap) {
|
|
323
323
|
const g = res.containerEntityGap;
|
|
324
|
-
lines.push(`[finding] coverage: ${g.containersWithService} containers map to services + ${g.serviceEntity} service_entity edges present but c4_container_entity.csv emits 0 rows — Phase 3 derivation missing (per ADR 0159 Phase 0)`);
|
|
324
|
+
lines.push(`[finding] coverage: ${g.containersWithService} containers map to services + ${g.serviceEntity} service_entity edges present but c4_container_entity.csv emits 0 rows — Phase 3 derivation missing (per M-ADR 0159 Phase 0)`);
|
|
325
325
|
}
|
|
326
326
|
return lines;
|
|
327
327
|
}
|
|
@@ -329,24 +329,24 @@ export function renderCapabilityEventCheck(res) {
|
|
|
329
329
|
const lines = [];
|
|
330
330
|
if (res.derivableGap) {
|
|
331
331
|
const g = res.derivableGap;
|
|
332
|
-
lines.push(`[finding] coverage: service_event (${g.serviceEvent}) + capability_service (${g.capabilityService}) populated but capability_event emits 0 rows — reverse-derivable edge missing (per ADR 0160 Phase 0)`);
|
|
332
|
+
lines.push(`[finding] coverage: service_event (${g.serviceEvent}) + capability_service (${g.capabilityService}) populated but capability_event emits 0 rows — reverse-derivable edge missing (per M-ADR 0160 Phase 0)`);
|
|
333
333
|
}
|
|
334
334
|
if (res.authoredGap) {
|
|
335
|
-
lines.push(`[finding] coverage: capability_event has ${res.authoredGap.derived} derived rows but 0 authored rows — no traceability.events[] or traceability.contracts.{published,consumed}_events declarations are landing in the catalogue. Governance queries filtering to source=authored will return empty (per ADR 0160 Phase 0)`);
|
|
335
|
+
lines.push(`[finding] coverage: capability_event has ${res.authoredGap.derived} derived rows but 0 authored rows — no traceability.events[] or traceability.contracts.{published,consumed}_events declarations are landing in the catalogue. Governance queries filtering to source=authored will return empty (per M-ADR 0160 Phase 0)`);
|
|
336
336
|
}
|
|
337
337
|
return lines;
|
|
338
338
|
}
|
|
339
339
|
export function renderFeatureEventCheck(res) {
|
|
340
340
|
return res.gap
|
|
341
341
|
? [
|
|
342
|
-
`[finding] coverage: service_event (${res.gap.serviceEvent}) + feature_service (${res.gap.featureService}) populated but feature_event emits 0 rows — reverse-derivable edge missing (per ADR 0161 Phase 0)`,
|
|
342
|
+
`[finding] coverage: service_event (${res.gap.serviceEvent}) + feature_service (${res.gap.featureService}) populated but feature_event emits 0 rows — reverse-derivable edge missing (per M-ADR 0161 Phase 0)`,
|
|
343
343
|
]
|
|
344
344
|
: [];
|
|
345
345
|
}
|
|
346
346
|
export function renderPlatformDependenciesCheck(finding) {
|
|
347
347
|
return finding
|
|
348
348
|
? [
|
|
349
|
-
`[finding] coverage: app-platform-dependencies.yaml declares a platform_dependencies block but product_platform_dependencies.csv emits 0 rows — forward-method ingestion missing (per ADR 0162 Phase 0). Resolves once the builder reads the forward source.`,
|
|
349
|
+
`[finding] coverage: app-platform-dependencies.yaml declares a platform_dependencies block but product_platform_dependencies.csv emits 0 rows — forward-method ingestion missing (per M-ADR 0162 Phase 0). Resolves once the builder reads the forward source.`,
|
|
350
350
|
]
|
|
351
351
|
: [];
|
|
352
352
|
}
|
|
@@ -358,7 +358,7 @@ export function renderFeatureSpecPromotionCheck(res) {
|
|
|
358
358
|
`[finding] coverage: ${f.intermediate} of ${f.total} features are reverse-intermediate ` +
|
|
359
359
|
`(capability-spec/feature-grounding) not yet promoted to feature-spec — ` +
|
|
360
360
|
`unresolvable as realises:/FK targets (${f.pctResolvable}% resolvable, ${f.note}; ` +
|
|
361
|
-
`per ADR 0177 §4.2)`,
|
|
361
|
+
`per M-ADR 0177 §4.2)`,
|
|
362
362
|
];
|
|
363
363
|
}
|
|
364
364
|
export function renderFeatureSurfaceDuplicationCheck(res) {
|
|
@@ -370,7 +370,7 @@ export function renderFeatureSurfaceDuplicationCheck(res) {
|
|
|
370
370
|
`[finding] coverage: ${f.pairs} near-duplicate feature surface(s) across ` +
|
|
371
371
|
`${f.capabilities} ${capWord} — a capability-spec (rollup) and feature-grounding ` +
|
|
372
372
|
`(detailed) slug naming the same surface; reconcile to one slug per surface, ` +
|
|
373
|
-
`detailed slug canonical (per ADR 0177 §4.3)`,
|
|
373
|
+
`detailed slug canonical (per M-ADR 0177 §4.3)`,
|
|
374
374
|
];
|
|
375
375
|
}
|
|
376
376
|
// ─── Scores coverage (M-ADR 0205 §4.5, B2 — the fourteenth check) ─────────
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Deterministic CSV writer for catalogues.
|
|
3
3
|
*
|
|
4
|
-
* Emits headers + sorted rows per ADR 0134 builder contract:
|
|
4
|
+
* Emits headers + sorted rows per M-ADR 0134 builder contract:
|
|
5
5
|
* - column order matches the canonical schema exactly,
|
|
6
6
|
* - rows sorted by primary-key value,
|
|
7
7
|
* - cells with embedded comma/quote/newline are RFC-4180 quoted,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Loader for the optional `.sdd/feature-grounding-aliases.yaml` config
|
|
3
|
-
* per ADR 0143 (schema 2.5.0).
|
|
3
|
+
* per M-ADR 0143 (schema 2.5.0).
|
|
4
4
|
*
|
|
5
5
|
* Adopters use this map to translate display names in
|
|
6
6
|
* `feature-grounding.md` tables (class names, file basenames, route
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Findings collector for the canonical builder per ADR 0134 / builder-spec
|
|
2
|
+
* Findings collector for the canonical builder per M-ADR 0134 / builder-spec
|
|
3
3
|
* §"Emit findings as it parses".
|
|
4
4
|
*
|
|
5
5
|
* Findings are human-readable AND machine-parseable. Format per finding:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* FK validator per ADR 0134 builder-spec §"Run internal FK validation
|
|
2
|
+
* FK validator per M-ADR 0134 builder-spec §"Run internal FK validation
|
|
3
3
|
* before exiting" + §"External FK handling".
|
|
4
4
|
*
|
|
5
5
|
* For each catalogue's foreign keys:
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - External FKs (external=true, or external for the build's profile via
|
|
10
10
|
* `externalForProfiles` — schema 2.23.0): every non-empty value is logged
|
|
11
11
|
* as a `[finding] external` line and skipped from validation. The
|
|
12
|
-
* aggregator resolves these post-merge per ADR 0134 §4.5. A required
|
|
12
|
+
* aggregator resolves these post-merge per M-ADR 0134 §4.5. A required
|
|
13
13
|
* (non-nullable) external FK still fails when EMPTY — the value must be
|
|
14
14
|
* named even if it resolves cross-SDD.
|
|
15
15
|
*
|