@sdd-method/author-cli 0.2.0 → 0.3.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/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +4 -4
- package/dist/lib/api-surface/analyzers/serverless.js +1 -1
- package/dist/lib/api-surface/analyzers/spring.js +1 -1
- package/dist/lib/briefing/render.js +1 -2
- package/dist/lib/briefing/variables.js +1 -2
- package/dist/lib/catalogue/archetype/wireframe.js +1 -1
- package/dist/lib/catalogue/build.js +17 -23
- package/dist/lib/catalogue/builders/application-conformance-coverage.js +1 -1
- package/dist/lib/catalogue/builders/c4-components.js +4 -5
- package/dist/lib/catalogue/builders/c4-containers.js +3 -3
- package/dist/lib/catalogue/builders/c4-external-systems.js +1 -1
- package/dist/lib/catalogue/builders/c4-l1-candidates.js +1 -1
- package/dist/lib/catalogue/builders/capabilities.js +2 -2
- package/dist/lib/catalogue/builders/capability-edges.js +4 -4
- package/dist/lib/catalogue/builders/capability-spec-walker.js +3 -3
- package/dist/lib/catalogue/builders/container-dependency.js +18 -19
- package/dist/lib/catalogue/builders/contracts.js +5 -6
- package/dist/lib/catalogue/builders/data-store-mapping.js +3 -3
- package/dist/lib/catalogue/builders/edge-label-merge.js +2 -2
- package/dist/lib/catalogue/builders/entities-manifest.js +1 -1
- package/dist/lib/catalogue/builders/entities-mermaid.js +3 -3
- package/dist/lib/catalogue/builders/entity-slug.js +11 -12
- package/dist/lib/catalogue/builders/event-catalog-walker.js +3 -3
- package/dist/lib/catalogue/builders/evidence-class.js +2 -2
- package/dist/lib/catalogue/builders/external-system-exemption.js +1 -1
- package/dist/lib/catalogue/builders/feature-contract.js +1 -3
- package/dist/lib/catalogue/builders/feature-grounding.js +1 -2
- package/dist/lib/catalogue/builders/feature-groups.js +0 -1
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +2 -3
- package/dist/lib/catalogue/builders/feature-tree-walker.js +1 -1
- package/dist/lib/catalogue/builders/features.js +3 -4
- package/dist/lib/catalogue/builders/glossary-xref.js +1 -1
- package/dist/lib/catalogue/builders/persona-c4system.js +7 -7
- package/dist/lib/catalogue/builders/persona-container.js +14 -14
- package/dist/lib/catalogue/builders/personas.js +1 -2
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +2 -2
- package/dist/lib/catalogue/builders/quality-scores.js +1 -1
- package/dist/lib/catalogue/builders/repositories.js +2 -2
- package/dist/lib/catalogue/builders/screen-tree.js +1 -1
- package/dist/lib/catalogue/builders/screens.js +3 -4
- package/dist/lib/catalogue/builders/system-external-dependency.js +7 -7
- package/dist/lib/catalogue/canonical-schema.yaml +23 -23
- package/dist/lib/catalogue/coverage-predicates.js +4 -4
- package/dist/lib/catalogue/coverage-run.js +1 -1
- package/dist/lib/catalogue/dedupe-keyed-tables.js +2 -3
- package/dist/lib/catalogue/ext-id.js +4 -4
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +1 -1
- package/dist/lib/catalogue/finding-identity.js +3 -3
- package/dist/lib/catalogue/freshness.js +1 -1
- package/dist/lib/catalogue/manifest-loader.js +2 -2
- package/dist/lib/catalogue/scaffold-delta.js +1 -1
- package/dist/lib/catalogue/schema-compat.js +1 -2
- package/dist/lib/catalogue/screen-validation-loader.js +1 -1
- package/dist/lib/catalogue/staleness-verdicts.js +2 -2
- package/dist/lib/catalogue/traceability-loader.js +2 -3
- package/dist/lib/conformance/index.js +29 -8
- package/dist/lib/lock-file.js +1 -1
- package/dist/lib/package-version.js +1 -1
- package/dist/lib/snapshot/manifest.js +1 -1
- package/dist/lib/snapshot/targets.js +1 -1
- package/dist/lib/validate/index.js +1 -1
- package/dist/lib/validate/plan.js +1 -1
- package/dist/lib/validate/yaml-parse.js +1 -1
- package/dist/maintain-version.json +2 -2
- package/dist/verbs/plan.js +3 -3
- package/dist/verify-version.json +2 -2
- package/package.json +2 -2
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
// pairs are deduplicated with a `[finding] inferred` log line recording that
|
|
8
8
|
// both paths produced the same edge.
|
|
9
9
|
//
|
|
10
|
-
// Added in schema 2.3.0 — see catalogue-metamodel.md §"Edge catalogue status"
|
|
11
|
-
// and the companion plan
|
|
12
|
-
// `content/plans/document-non-catalogued-artefacts-and-add-feature-contract.md`.
|
|
10
|
+
// Added in schema 2.3.0 — see catalogue-metamodel.md §"Edge catalogue status".
|
|
13
11
|
export function buildFeatureContract(inputs) {
|
|
14
12
|
const seen = new Map();
|
|
15
13
|
// Path 1 (forward): capability_feature ⋈ capability_contract on capability_id.
|
|
@@ -107,8 +107,7 @@ export async function buildFeatureGrounding(inputs) {
|
|
|
107
107
|
// feature_repo and feature_service edges: emit unconditionally per
|
|
108
108
|
// ADR 0143 §2 (these are the source's unique contribution; nothing
|
|
109
109
|
// else carries per-row code-mapping). Pre-0.30.2 the collision skip
|
|
110
|
-
// dropped these edges too — bug fixed
|
|
111
|
-
// `coordination/feature-grounding-collision-edges-coordination.md`.
|
|
110
|
+
// dropped these edges too — bug fixed.
|
|
112
111
|
if (featureRowCollides) {
|
|
113
112
|
// ONE CHECK, TWO MEANINGS — split them, because they are not the same
|
|
114
113
|
// event and only one of them is a defect.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// FeatureGroups discoverer — emits `feature_groups.csv` rows from
|
|
2
2
|
// hierarchical `feature-tree.md` files.
|
|
3
3
|
//
|
|
4
|
-
// Per Q7.2 resolution (adopt-canonical-catalogue-builder.md §7.2),
|
|
5
4
|
// FeatureGroups are first-class entities. This builder consumes the
|
|
6
5
|
// shared `feature-tree-walker` and emits one row per H2/H3 heading
|
|
7
6
|
// outside fenced code blocks.
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// `subscription_channels:` entry in any `reverse-evidence/feature-grounding-evidence.yaml`
|
|
3
3
|
// file in the SDD. Added in schema 2.9.0.
|
|
4
4
|
//
|
|
5
|
-
// Per
|
|
6
|
-
//
|
|
7
|
-
// per-feature slot vocabulary): `subscription_channels:` is a peer of
|
|
5
|
+
// Per the per-feature slot vocabulary in
|
|
6
|
+
// `docs/method/methods/reverse/rubrics/reverse-application-evidence-quality.md`: `subscription_channels:` is a peer of
|
|
8
7
|
// `graphql_operations:` in the per-feature evidence shape — `graphql_operations:`
|
|
9
8
|
// carries the operation names declared in the GraphQL schema; this builder
|
|
10
9
|
// captures the channel/topic layer (AppSync WebSocket, Pekko WS, MQTT, SSE,
|
|
@@ -162,7 +162,7 @@ const SERVICE_ID_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
|
162
162
|
/**
|
|
163
163
|
* Parse the `## Feature Services` section of a `feature-tree.md` body (the
|
|
164
164
|
* application-product authoring surface for `feature_service`, per M-ADR
|
|
165
|
-
* 0157
|
|
165
|
+
* 0157). Table shape:
|
|
166
166
|
*
|
|
167
167
|
* | Feature | Services |
|
|
168
168
|
* |---------|----------|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// Folder→capability mapping uses (a) the traceability manifest's
|
|
5
5
|
// `capability.feature_directory` field — authoritative when a
|
|
6
6
|
// capability spec uses a folder name that differs from the
|
|
7
|
-
// canonical capability_id (e.g. capability_id "
|
|
8
|
-
// with feature_directory pointing at "features/
|
|
7
|
+
// canonical capability_id (e.g. capability_id "gift-card-rules-engine"
|
|
8
|
+
// with feature_directory pointing at "features/gift-card-rules/") —
|
|
9
9
|
// then (b) the folder name itself when it equals a known capability_id.
|
|
10
10
|
// id format: `{capability_id}-{feature-slug}`.
|
|
11
11
|
//
|
|
@@ -26,8 +26,7 @@
|
|
|
26
26
|
// then walker (2) (standalone specs), then walker (3) (bullets). When
|
|
27
27
|
// a bullet's id is already in `seen` from a standalone spec the spec
|
|
28
28
|
// wins — divergence finding fires for the bullet. This matches the
|
|
29
|
-
// "standalone is more authoritative than bullet inventory" rule
|
|
30
|
-
// plan extract-feature-tree-bullets.md §1.4.
|
|
29
|
+
// "standalone is more authoritative than bullet inventory" rule.
|
|
31
30
|
import { readdir, readFile } from "node:fs/promises";
|
|
32
31
|
import { basename, join, relative, sep } from "node:path";
|
|
33
32
|
import { featureGroupId, findFeatureTreeFile, walkFeatureTree, } from "./feature-tree-walker.js";
|
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
// are skipped (no dangling FK). The optional interaction label comes from a
|
|
10
10
|
// `Rel(person, system, "label")` line.
|
|
11
11
|
//
|
|
12
|
-
// `Person_Ext(...)` is discriminated by EVIDENCE, not notation (
|
|
13
|
-
//
|
|
12
|
+
// `Person_Ext(...)` is discriminated by EVIDENCE, not notation (a
|
|
13
|
+
// discriminator keyed on notation breaks the external-human case): the
|
|
14
14
|
// notation is idiomatic both for a non-human external actor (a device) and
|
|
15
15
|
// for a human legitimately outside the boundary. Whether a persona row
|
|
16
16
|
// resolves is the declaration of humanity — a Person_Ext that resolves
|
|
17
17
|
// emits its edge; one that doesn't is silent (a device is not an authoring
|
|
18
|
-
// gap, and a typo'd human still surfaces through
|
|
18
|
+
// gap, and a typo'd human still surfaces through the reverse finding on
|
|
19
19
|
// the edgeless persona row).
|
|
20
20
|
//
|
|
21
21
|
// `Person(...)` asserts a human, so an unresolved one stays a forward
|
|
22
22
|
// coverage finding.
|
|
23
23
|
//
|
|
24
|
-
// Both directions report (
|
|
24
|
+
// Both directions report (M-ADR 0157 parity + M-ADR 0133 §4.2c
|
|
25
25
|
// no-silent-drop): a Person with no persona row is a coverage finding
|
|
26
26
|
// (forward), and a persona with no edge is a coverage finding stating WHY —
|
|
27
27
|
// no system row, no identity-sourcing L1, no Person actors, or no actor
|
|
@@ -31,7 +31,7 @@ import { readFile } from "node:fs/promises";
|
|
|
31
31
|
import { join } from "node:path";
|
|
32
32
|
import { resolveIdentityL1 } from "./c4-l1-candidates.js";
|
|
33
33
|
// `Person(id, "name", "desc")` / `Person_Ext(id, "name", "desc")` — the
|
|
34
|
-
// _Ext capture drives the evidence discriminator (
|
|
34
|
+
// _Ext capture drives the evidence discriminator (see header).
|
|
35
35
|
const PERSON_RE = /Person(_Ext)?\s*\(\s*(\w+)\s*,\s*"([^"]+)"\s*(?:,\s*"([^"]*)")?\s*\)/;
|
|
36
36
|
const REL_RE = /^(?:Bi)?Rel(?:_[A-Za-z]+)?\s*\(\s*(\w+)\s*,\s*(\w+)\s*(?:,\s*"([^"]*)")?/;
|
|
37
37
|
export async function buildPersonaC4System(inputs) {
|
|
@@ -65,7 +65,7 @@ export async function buildPersonaC4System(inputs) {
|
|
|
65
65
|
if (personaId === undefined) {
|
|
66
66
|
// Person_Ext with no persona row: a device or other non-human external
|
|
67
67
|
// actor — silence is correct, and a typo'd human surfaces through the
|
|
68
|
-
// reverse finding on its edgeless persona row
|
|
68
|
+
// reverse finding on its edgeless persona row.
|
|
69
69
|
if (person.external)
|
|
70
70
|
continue;
|
|
71
71
|
inputs.findings.add("coverage", `persona_c4system: Person "${person.name}" on the L1 diagram has no matching persona row — edge skipped`);
|
|
@@ -87,7 +87,7 @@ export async function buildPersonaC4System(inputs) {
|
|
|
87
87
|
rows.sort((a, b) => String(a["id"]).localeCompare(String(b["id"])));
|
|
88
88
|
return rows;
|
|
89
89
|
}
|
|
90
|
-
//
|
|
90
|
+
// The reverse direction (M-ADR 0157 parity): every persona that ends
|
|
91
91
|
// the build without an edge is reported, with the reason. A persona with
|
|
92
92
|
// neither an edge nor a finding is indistinguishable from one that was
|
|
93
93
|
// never considered.
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
// persona_container edge builder — M-ADR 0189
|
|
1
|
+
// persona_container edge builder — M-ADR 0189 (schema 2.34.0).
|
|
2
2
|
//
|
|
3
3
|
// The L2-granularity companion to persona_c4system: a Rel(...) between a
|
|
4
|
-
// Person and a container had
|
|
4
|
+
// Person and a container had neither a catalogue nor a finding —
|
|
5
5
|
// eleven of one estate's L2s and the bulk of another's 81 unroutable Rels
|
|
6
|
-
// were exactly this class.
|
|
6
|
+
// were exactly this class. The unroutable-Rel finding made the silence loud; this gives the
|
|
7
7
|
// loudness a destination.
|
|
8
8
|
//
|
|
9
9
|
// Person entries are file-local (a Person declared in the same diagram as
|
|
10
|
-
// the Rel); containers resolve SDD-wide (
|
|
10
|
+
// the Rel); containers resolve SDD-wide (container-first). Either Rel direction
|
|
11
11
|
// normalises to persona→container. A Person that matches no persona row is
|
|
12
12
|
// a coverage finding, never a silent skip (the persona_c4system precedent).
|
|
13
|
-
// The syndicated platform L1 is skipped for non-root SDDs
|
|
14
|
-
// labels concatenate loudly
|
|
13
|
+
// The syndicated platform L1 is skipped for non-root SDDs; parallel
|
|
14
|
+
// labels concatenate loudly.
|
|
15
15
|
import { readFile } from "node:fs/promises";
|
|
16
16
|
import { collectC4DiagramFiles } from "./c4-external-systems.js";
|
|
17
17
|
import { mergeEdgeLabel } from "./edge-label-merge.js";
|
|
@@ -37,11 +37,11 @@ export async function buildPersonaContainer(inputs) {
|
|
|
37
37
|
}
|
|
38
38
|
if (personNameByEntry.size === 0)
|
|
39
39
|
continue;
|
|
40
|
-
//
|
|
40
|
+
// Only the SDD's OWN system entry is persona_c4system's domain —
|
|
41
41
|
// that catalogue emits one edge per persona to THE system, nothing
|
|
42
42
|
// else. The 0.89.0 skip silenced person Rels to ANY System(...) entry,
|
|
43
43
|
// a silence delegated to a catalogue that never held those edges
|
|
44
|
-
// (
|
|
44
|
+
// (a delegated silence one tier over): four of an estate's best-authored
|
|
45
45
|
// persona edges vanished, while the same file's person→external edge
|
|
46
46
|
// was announced. Own entry = the first System(...) line, mirroring
|
|
47
47
|
// c4-systems' parseSystemLine; every other target takes exits 1/3/4.
|
|
@@ -58,17 +58,17 @@ export async function buildPersonaContainer(inputs) {
|
|
|
58
58
|
if (!m)
|
|
59
59
|
continue;
|
|
60
60
|
const [, fromEntry, toEntry, label] = m;
|
|
61
|
-
//
|
|
61
|
+
// The person tier's exits, designed once. Identify the
|
|
62
62
|
// person endpoint (file-local; container resolution wins on a
|
|
63
|
-
// dual-use id
|
|
63
|
+
// dual-use id), then route by the OTHER endpoint's kind:
|
|
64
64
|
// local container → persona_container row
|
|
65
65
|
// own System(...) → persona_c4system's domain; silent here
|
|
66
|
-
// external → announced skip
|
|
66
|
+
// external → announced skip: a human using an
|
|
67
67
|
// external system has no persona-tier
|
|
68
68
|
// catalogue YET — it must never transmute
|
|
69
69
|
// into sed/ssd, whose semantics are runtime
|
|
70
70
|
// calls between software
|
|
71
|
-
// anything else → announced skip
|
|
71
|
+
// anything else → announced skip: claimed by no
|
|
72
72
|
// catalogue is said, never silent
|
|
73
73
|
const fromIsContainer = inputs.containerIdByEntryId.has(fromEntry ?? "");
|
|
74
74
|
const toIsContainer = inputs.containerIdByEntryId.has(toEntry ?? "");
|
|
@@ -100,8 +100,8 @@ export async function buildPersonaContainer(inputs) {
|
|
|
100
100
|
if (targetContainer === undefined) {
|
|
101
101
|
const isExternal = inputs.externalIdByEntryId?.has(targetEntry ?? "") ?? false;
|
|
102
102
|
inputs.findings.add("coverage", isExternal
|
|
103
|
-
? `persona_container: persona "${personaId}" → external "${inputs.externalIdByEntryId.get(targetEntry ?? "")}"${label ? ` ("${label}")` : ""} — a human using an external system has no persona-tier catalogue; skipped, never transmuted into a system edge
|
|
104
|
-
: `persona_container: persona "${personaId}" → "${targetEntry}"${label ? ` ("${label}")` : ""} resolves to no container — claimed by no catalogue; skipped
|
|
103
|
+
? `persona_container: persona "${personaId}" → external "${inputs.externalIdByEntryId.get(targetEntry ?? "")}"${label ? ` ("${label}")` : ""} — a human using an external system has no persona-tier catalogue; skipped, never transmuted into a system edge`
|
|
104
|
+
: `persona_container: persona "${personaId}" → "${targetEntry}"${label ? ` ("${label}")` : ""} resolves to no container — claimed by no catalogue; skipped`);
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
107
|
const id = `${personaId}__${targetContainer}`;
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
// ## Summary
|
|
10
10
|
// {1-2 sentences}
|
|
11
11
|
//
|
|
12
|
-
// Cross-profile path
|
|
13
|
-
// coordination.md, 2026-05-29): the walker reads `personas/` at SDD root
|
|
12
|
+
// Cross-profile path: the walker reads `personas/` at SDD root
|
|
14
13
|
// for every profile. Each SDD's personas/ carries personas at that SDD's
|
|
15
14
|
// scope — for application-product that's app-specific personas, for
|
|
16
15
|
// platform-product that's platform-wide / cross-app personas surfaced as
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
// The aggregator subsequently joins synchronous rows against platform-side
|
|
33
33
|
// `capability_service` to derive `cross_sdd_platform_capability` edges (per
|
|
34
34
|
// ADR 0134, federation); async rows resolve via the capability leg (no
|
|
35
|
-
// realiser service to join on) and skip the
|
|
35
|
+
// realiser service to join on) and skip the realiser-join.
|
|
36
36
|
//
|
|
37
37
|
// Cross-SDD references (target_capability_id, target_service_id) are
|
|
38
38
|
// declared `external: true` in the schema — per-SDD FK validation
|
|
@@ -62,7 +62,7 @@ const FORWARD_REL_PATH = "app-platform-dependencies.yaml";
|
|
|
62
62
|
* Contract types that denote an ASYNC shared-stream dependency (M-ADR 0192).
|
|
63
63
|
* A dependency of one of these kinds binds to a stream contract published by a
|
|
64
64
|
* capability, not to a synchronous realiser service — so it characteristically
|
|
65
|
-
* has no `service_id`, and the
|
|
65
|
+
* has no `service_id`, and the realiser-join does not apply to it at
|
|
66
66
|
* aggregation. Kept lowercase; callers normalise before testing.
|
|
67
67
|
*/
|
|
68
68
|
export const ASYNC_CONTRACT_TYPES = new Set([
|
|
@@ -468,7 +468,7 @@ export async function buildQualityScores(inputs) {
|
|
|
468
468
|
}
|
|
469
469
|
// Flat headline keys beside a `current:` block are a compatibility
|
|
470
470
|
// shim, and a shim that DISAGREES with the effective record is the
|
|
471
|
-
// silent-failure shape found live
|
|
471
|
+
// silent-failure shape found live: an ingestion updated
|
|
472
472
|
// the flat score and left `current:` stale, and the catalogue published
|
|
473
473
|
// the stale value with the right one inches away in the same entry —
|
|
474
474
|
// no complaint anywhere. The disagreement is loud now. scored_at is
|
|
@@ -83,7 +83,7 @@ const ServiceCapabilityExemptionSchema = z
|
|
|
83
83
|
const ServicesBlockSchema = z.unknown().optional();
|
|
84
84
|
// `additional_context` accepts strings OR loosely-parsed key:value
|
|
85
85
|
// objects (which arise when adopters leave colons unquoted in their
|
|
86
|
-
// YAML — e.g. `- docs/.../ADR 0017: Use
|
|
86
|
+
// YAML — e.g. `- docs/.../ADR 0017: Use Acme IdP for Auth.md`). The
|
|
87
87
|
// canonical builder reconstructs the original string downstream.
|
|
88
88
|
const RepoConfigSchema = z.object({
|
|
89
89
|
repo: z.string(),
|
|
@@ -117,7 +117,7 @@ const RepoConfigSchema = z.object({
|
|
|
117
117
|
* Reconstruct a string from a possibly-mis-parsed YAML list entry.
|
|
118
118
|
*
|
|
119
119
|
* Adopters sometimes write list entries with unquoted colons like:
|
|
120
|
-
* - docs/.../ADR 0017: Use
|
|
120
|
+
* - docs/.../ADR 0017: Use Acme IdP for Auth.md
|
|
121
121
|
*
|
|
122
122
|
* The YAML parser reads each such entry as a single-key object whose key
|
|
123
123
|
* is the part before the first colon and whose value is the part after.
|
|
@@ -112,7 +112,7 @@ function parseStatements(cleanBlock) {
|
|
|
112
112
|
const id = m[1];
|
|
113
113
|
const rhs = m[2];
|
|
114
114
|
// A component statement's RHS is a call (`Text("Q4")`) or a reference — never a
|
|
115
|
-
// bare string literal. A line like `action="
|
|
115
|
+
// bare string literal. A line like `action="basket-updated"` (the block-level
|
|
116
116
|
// screen_action messaging contract, 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).
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
// `action="..."` declarations -> screen_action. The frontmatter supplies the
|
|
24
24
|
// catalogue keys + FK lists. This is the forward / authored path; the reverse
|
|
25
25
|
// toolkit projects an app's router + component tree into the same shape (the
|
|
26
|
-
// upstream step that produces these artefacts
|
|
27
|
-
// research/adr-0166-prototypes/ for the Phase Pre validation).
|
|
26
|
+
// upstream step that produces these artefacts).
|
|
28
27
|
//
|
|
29
28
|
// Per ADR 0135 the rows are identical regardless of authoring direction; only
|
|
30
29
|
// the `source` discriminator differs (screen-spec vs screen-extraction).
|
|
@@ -305,8 +304,8 @@ function isTemplate(name) {
|
|
|
305
304
|
lower.endsWith("-spec-template.md"));
|
|
306
305
|
}
|
|
307
306
|
// entity-style slug: ALLCAPS-then-Pascal split so naming variation across
|
|
308
|
-
// `
|
|
309
|
-
// (
|
|
307
|
+
// `GiftCardHub` / `order_tracking_container` / `POS_checkout` resolves stably
|
|
308
|
+
// (a measured real-menu finding; mirrors entitySlug).
|
|
310
309
|
function screenSlug(s) {
|
|
311
310
|
return s
|
|
312
311
|
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
|
|
@@ -14,7 +14,7 @@ import { mergeEdgeLabel } from "./edge-label-merge.js";
|
|
|
14
14
|
// Any C4-PlantUML relationship macro: Rel, Rel_U/D/L/R, BiRel, Rel_Back, etc.
|
|
15
15
|
// Capture the first two positional args (from, to) and an optional label.
|
|
16
16
|
const REL_RE = /^(?:Bi)?Rel(?:_[A-Za-z]+)?\s*\(\s*(\w+)\s*,\s*(\w+)\s*(?:,\s*"([^"]*)")?/;
|
|
17
|
-
// Diagram files come from the shared collector
|
|
17
|
+
// Diagram files come from the shared collector — see
|
|
18
18
|
// c4-external-systems.ts / c4-l1-candidates.ts. This builder previously
|
|
19
19
|
// kept a private, pre-0.30.1-ordered copy missing the domain-scoped L1
|
|
20
20
|
// path, so Rel(...) lines there were never harvested.
|
|
@@ -30,7 +30,7 @@ export async function buildSystemExternalDependency(inputs) {
|
|
|
30
30
|
const rows = [];
|
|
31
31
|
const seen = new Set();
|
|
32
32
|
const rowsById = new Map();
|
|
33
|
-
//
|
|
33
|
+
// A Rel in the syndicated platform L1 is a statement about the
|
|
34
34
|
// PLATFORM. For any other SDD those lines minted the platform's
|
|
35
35
|
// dependencies (and even its persona interactions) as the sibling's own
|
|
36
36
|
// edges — measured at 28% of one estate's dependency graph and 35/35
|
|
@@ -42,11 +42,11 @@ export async function buildSystemExternalDependency(inputs) {
|
|
|
42
42
|
for (const { abs, syndicated } of files) {
|
|
43
43
|
if (syndicated && !isRoot)
|
|
44
44
|
continue;
|
|
45
|
-
//
|
|
45
|
+
// the non-external Rel endpoint may be a local container — carry
|
|
46
46
|
// its id so adjudication can rewrite this edge onto container_dependency
|
|
47
47
|
// instead of destroying it when the external endpoint proves internal.
|
|
48
48
|
const content = await readFile(abs, "utf-8");
|
|
49
|
-
//
|
|
49
|
+
// A Person-sourced Rel must never enter this catalogue — its
|
|
50
50
|
// semantics are runtime calls between software, and attributing a
|
|
51
51
|
// human's Rel to the system erases the person-ness (which adjudication
|
|
52
52
|
// then launders into system_system_dependency). The persona-container
|
|
@@ -60,14 +60,14 @@ export async function buildSystemExternalDependency(inputs) {
|
|
|
60
60
|
for (const rel of parseRels(content)) {
|
|
61
61
|
if (personEntries.has(rel.from) || personEntries.has(rel.to))
|
|
62
62
|
continue;
|
|
63
|
-
//
|
|
64
|
-
// container-dependency builder
|
|
63
|
+
// Classification is CONTAINER-FIRST, uniformly with the
|
|
64
|
+
// container-dependency builder. An entry-id that
|
|
65
65
|
// resolves to a local container IS a container, whatever a sibling
|
|
66
66
|
// diagram called it — the old external-first read here meant a
|
|
67
67
|
// dual-use SOURCE made the pair look external↔external to this
|
|
68
68
|
// builder while looking container→external to its sibling, and the
|
|
69
69
|
// edge fell between them: claimed by neither, reported by nothing.
|
|
70
|
-
//
|
|
70
|
+
// A fix applied to one builder must be swept into this one too.
|
|
71
71
|
const fromIsContainer = inputs.containerIdByEntryId?.has(rel.from) ?? false;
|
|
72
72
|
const toIsContainer = inputs.containerIdByEntryId?.has(rel.to) ?? false;
|
|
73
73
|
const fromExt = fromIsContainer
|
|
@@ -134,7 +134,7 @@ enums:
|
|
|
134
134
|
runtime: true
|
|
135
135
|
emits_service_row: false
|
|
136
136
|
description: |
|
|
137
|
-
Type of repository, declared in `orchestration/repo-config/*.yaml` as `service_type` and surfaced in `repositories.component_type` and `services.component_type`. The canonical builder uses this to decide which repos are runtime services (api_service, worker, gateway, frontend, mobile) and which are non-runtime (library, infrastructure, documentation); firmware runs, but on a device, so it is runtime without a service row (M-ADR 0215) — service emission follows emits_service_row, not runtime. Whether a repo is a vertical-domain application is conveyed by its SDD manifest's `profile: application-product`, not by a service_type tag — this enum is strictly about runtime artefact shape
|
|
137
|
+
Type of repository, declared in `orchestration/repo-config/*.yaml` as `service_type` and surfaced in `repositories.component_type` and `services.component_type`. The canonical builder uses this to decide which repos are runtime services (api_service, worker, gateway, frontend, mobile) and which are non-runtime (library, infrastructure, documentation); firmware runs, but on a device, so it is runtime without a service row (M-ADR 0215) — service emission follows emits_service_row, not runtime. Whether a repo is a vertical-domain application is conveyed by its SDD manifest's `profile: application-product`, not by a service_type tag — this enum is strictly about runtime artefact shape.
|
|
138
138
|
capability_exemption_reason:
|
|
139
139
|
values:
|
|
140
140
|
- value: infrastructure
|
|
@@ -174,11 +174,11 @@ enums:
|
|
|
174
174
|
- value: public-infrastructure
|
|
175
175
|
description: Public networks, registries, or services no party in the estate operates.
|
|
176
176
|
- value: estate-owned-unmodelled
|
|
177
|
-
description: "Operated by this estate but with no SDD or container home yet — the row is an accounted-for stand-in, and this population is the estate's modelling backlog, countable (
|
|
177
|
+
description: "Operated by this estate but with no SDD or container home yet — the row is an accounted-for stand-in, and this population is the estate's modelling backlog, countable (2.32.0). Not an assertion of externality: an assertion of accountability for a boundary row."
|
|
178
178
|
- value: organisation-internal
|
|
179
|
-
description: Owned elsewhere in the same organisation, outside this estate's modelling boundary (
|
|
179
|
+
description: Owned elsewhere in the same organisation, outside this estate's modelling boundary (2.32.0).
|
|
180
180
|
description: |
|
|
181
|
-
Why an external-system candidate is ACCOUNTED FOR at the estate boundary, per M-ADR 0189 §4.6 (
|
|
181
|
+
Why an external-system candidate is ACCOUNTED FOR at the estate boundary, per M-ADR 0189 §4.6 (wording corrected in 2.32.0): five values assert another party owns the system, and two assert the estate/organisation owns it but it has no model home yet — forcing the latter through the former would demand a false reason, and a false reason is worse than silence. The adjudicator can prove a candidate internal; it cannot prove one accounted-for — that is an assertion the author is accountable for, in the shape M-ADR 0188 ratified for repositories. Closed vocabulary, method-owned, extended only by ADR amendment, so adopters cannot locally widen the exemption surface to silence the unverified-candidate coverage finding.
|
|
182
182
|
ENFORCED, not advisory: the builder drops a row whose reason is outside this vocabulary (convention finding) and the candidate stays unverified — which is the point. An unrecognised reason must not succeed at the one thing an exemption does.
|
|
183
183
|
scaffold_status:
|
|
184
184
|
values:
|
|
@@ -1353,7 +1353,7 @@ catalogues:
|
|
|
1353
1353
|
nullable: true
|
|
1354
1354
|
description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
|
|
1355
1355
|
description: |
|
|
1356
|
-
Hierarchical grouping for features. First-class entity
|
|
1356
|
+
Hierarchical grouping for features. First-class entity — replaces the previous reverse-only synthetic `feature-tree-group` rows in features.csv. Both forward and reverse can use feature_groups: forward application-product SDDs whose `feature-tree.md` has H2/H3 structure, reverse SDDs whose extracted feature trees are inherently hierarchical, and any SDD that promotes groups to first-class with authored metadata. Groups can nest via `parent_group_id`.
|
|
1357
1357
|
foreign_keys:
|
|
1358
1358
|
- column: product_id
|
|
1359
1359
|
references: products.id
|
|
@@ -2062,8 +2062,8 @@ catalogues:
|
|
|
2062
2062
|
nullable: false
|
|
2063
2063
|
description: Mandatory, non-empty. The reason makes exemptions countable; the note makes them reviewable (M-ADR 0188 §4.2 pattern).
|
|
2064
2064
|
description: |
|
|
2065
|
-
A declared, reasoned assertion that an external-system candidate is GENUINELY external (M-ADR 0189 §4.6
|
|
2066
|
-
The invariant it completes: c4_external_systems was the one catalogue family with no coverage predicate — a candidate producing no adjudication outcome was silent, and that silence hid a 28% dependency -graph distortion
|
|
2065
|
+
A declared, reasoned assertion that an external-system candidate is GENUINELY external (M-ADR 0189 §4.6). Annotation catalogue (M-ADR 0133 §4.2b): one FK, declared never derived, closed reason enum. Sourced from `orchestration/external-system-exemptions.yaml`.
|
|
2066
|
+
The invariant it completes: c4_external_systems was the one catalogue family with no coverage predicate — a candidate producing no adjudication outcome was silent, and that silence hid a 28% dependency -graph distortion. Every candidate now either resolves at adjudication, or carries an exemption here, or is reported unverified. An exemption retires only the unverified finding — never an adjudication outcome (a converted candidate's exemption is a contradiction, reported and dropped). Shared entities may be exempted by multiple SDDs: identical rows dedup at aggregation (same exception class as c4_external_systems); conflicting rows keep first-seen with a convention finding.
|
|
2067
2067
|
foreign_keys:
|
|
2068
2068
|
- column: external_system_id
|
|
2069
2069
|
references: c4_external_systems.id
|
|
@@ -2172,7 +2172,7 @@ catalogues:
|
|
|
2172
2172
|
type: string
|
|
2173
2173
|
nullable: false
|
|
2174
2174
|
description: exposes | consumes.
|
|
2175
|
-
description: "Edge: service exposes or consumes contract. Both FKs are external (per M-ADR 0147): federated SDDs that walk per-repo service-architecture tables emit consumes rows where service_id resolves to the platform-root catalogue's services.csv (post-aggregation) and contract_id resolves to the platform-root contracts.csv. Internal builds (
|
|
2175
|
+
description: "Edge: service exposes or consumes contract. Both FKs are external (per M-ADR 0147): federated SDDs that walk per-repo service-architecture tables emit consumes rows where service_id resolves to the platform-root catalogue's services.csv (post-aggregation) and contract_id resolves to the platform-root contracts.csv. Internal builds (a single-SDD platform) still optimistically validate locally. Schema 2.8.0."
|
|
2176
2176
|
foreign_keys:
|
|
2177
2177
|
- column: service_id
|
|
2178
2178
|
references: services.id
|
|
@@ -2303,7 +2303,7 @@ catalogues:
|
|
|
2303
2303
|
nullable: true
|
|
2304
2304
|
description: Where the coupling is evidenced — a path or anchor in the SDD, from `evidence:` on the services[] entry. Prose about the coupling stays in dependency_notes; this is the pointer a reviewer follows.
|
|
2305
2305
|
description: |
|
|
2306
|
-
Edge: capability is realised by services. Sourced from the capability traceability manifest services[] block. service_id is an external FK (schema 2.29.0
|
|
2306
|
+
Edge: capability is realised by services. Sourced from the capability traceability manifest services[] block. service_id is an external FK (schema 2.29.0): a traceability entry marked `owner_sdd:` declares a cross-SDD reference (e.g. the service moved to a sibling SDD at an M-ADR 0170 §4.2 carve) — the per-SDD builder emits the edge, logs it `[finding] external`, and aggregation validates it hard against the owning sibling's rows. An UNMARKED id that does not resolve locally is NOT emitted — it stays a member-tier orphan finding (or convention, when it names a known external system).
|
|
2307
2307
|
foreign_keys:
|
|
2308
2308
|
- column: capability_id
|
|
2309
2309
|
references: capabilities.id
|
|
@@ -2365,8 +2365,8 @@ catalogues:
|
|
|
2365
2365
|
- name: source
|
|
2366
2366
|
type: string
|
|
2367
2367
|
nullable: false
|
|
2368
|
-
description: "declared — the only emitted value. `derived` is reserved vocabulary: the reverse derivation it anticipated was never built (0 rows across 105 measured units) and the claim was corrected at 3.0.0 (M-ADR 0203 §4.5
|
|
2369
|
-
description: "Edge: capability is bound to a contract. Whether the capability exposes or depends on it is not stated by the source (traceability.contracts), so this description asserts no direction (M-ADR 0211 §1). Forward-declared only. Forward-declared only (traceability.contracts). The reverse derivation this description long claimed (service_contract ⋈ capability_service) was measured nonexistent — 0 `source: derived` rows across 105 units, builder references neither catalogue — and the CLAIM was corrected at 3.0.0 (M-ADR 0203 §4.5
|
|
2368
|
+
description: "declared — the only emitted value. `derived` is reserved vocabulary: the reverse derivation it anticipated was never built (0 rows across 105 measured units) and the claim was corrected at 3.0.0 (M-ADR 0203 §4.5); the value re-enters if a decision builds a reverse source."
|
|
2369
|
+
description: "Edge: capability is bound to a contract. Whether the capability exposes or depends on it is not stated by the source (traceability.contracts), so this description asserts no direction (M-ADR 0211 §1). Forward-declared only. Forward-declared only (traceability.contracts). The reverse derivation this description long claimed (service_contract ⋈ capability_service) was measured nonexistent — 0 `source: derived` rows across 105 units, builder references neither catalogue — and the CLAIM was corrected at 3.0.0 (M-ADR 0203 §4.5, gap class absent-derivation per M-ADR 0196 §4.1). The edge is forward-one-directional until a decision builds a reverse source."
|
|
2370
2370
|
foreign_keys:
|
|
2371
2371
|
- column: capability_id
|
|
2372
2372
|
references: capabilities.id
|
|
@@ -2546,7 +2546,7 @@ catalogues:
|
|
|
2546
2546
|
type: string
|
|
2547
2547
|
nullable: true
|
|
2548
2548
|
description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
|
|
2549
|
-
description: "Edge: feature delivers async push via a named channel/topic. Peer of `graphql_operations:` in the per-feature evidence shape — `graphql_operations:` carries the GraphQL operation names declared in the schema (Queries / Mutations / Subscriptions); `feature_subscription_channels` rows carry the channel/topic layer that physically delivers the push events (AppSync WebSocket endpoints, Pekko WS channels, MQTT topics, server-sent-event streams).
|
|
2549
|
+
description: "Edge: feature delivers async push via a named channel/topic. Peer of `graphql_operations:` in the per-feature evidence shape — `graphql_operations:` carries the GraphQL operation names declared in the schema (Queries / Mutations / Subscriptions); `feature_subscription_channels` rows carry the channel/topic layer that physically delivers the push events (AppSync WebSocket endpoints, Pekko WS channels, MQTT topics, server-sent-event streams). A single GraphQL Subscription operation may listen on multiple channels; one channel may be consumed by multiple operations — modelling as a separate edge catalogue (not as a feature column) accommodates the many-to-many. Built per-SDD from `reverse-evidence/feature-grounding-evidence.yaml` `subscription_channels:` entries on each feature. Added in schema 2.9.0."
|
|
2550
2550
|
foreign_keys:
|
|
2551
2551
|
- column: feature_id
|
|
2552
2552
|
references: features.id
|
|
@@ -2672,9 +2672,9 @@ catalogues:
|
|
|
2672
2672
|
- name: interaction
|
|
2673
2673
|
type: string
|
|
2674
2674
|
nullable: true
|
|
2675
|
-
description: Optional interaction label from the Rel(...) line. May hold multiple '; '-joined labels
|
|
2675
|
+
description: Optional interaction label from the Rel(...) line. May hold multiple '; '-joined labels.
|
|
2676
2676
|
description: |
|
|
2677
|
-
Edge: a persona uses a C4 container — the L2-granularity companion to persona_c4system (M-ADR 0189
|
|
2677
|
+
Edge: a persona uses a C4 container — the L2-granularity companion to persona_c4system (M-ADR 0189, ratified 2026-07-10). Eleven of one estate's L2 diagrams and the bulk of another's 81 unroutable Rels drew Person→Container relationships that had no catalogue and raised no finding. Harvested at member tier from Rel(...) lines whose endpoints resolve to a file-local Person and a local container (either direction; the row is normalised persona→container). A Person that matches no persona row is a coverage finding, never a silent skip. Both authoring directions feed from the same diagram surface (M-ADR 0157 parity). Added in schema 2.34.0.
|
|
2678
2678
|
foreign_keys:
|
|
2679
2679
|
- column: persona_id
|
|
2680
2680
|
references: personas.id
|
|
@@ -2698,11 +2698,11 @@ catalogues:
|
|
|
2698
2698
|
- name: interaction
|
|
2699
2699
|
type: string
|
|
2700
2700
|
nullable: true
|
|
2701
|
-
description: Optional integration label/protocol from the Rel(...) line. May hold multiple '; '-joined labels when one pair is declared with distinct labels (
|
|
2701
|
+
description: Optional integration label/protocol from the Rel(...) line. May hold multiple '; '-joined labels when one pair is declared with distinct labels (2.33.2).
|
|
2702
2702
|
- name: from_container_id
|
|
2703
2703
|
type: string
|
|
2704
2704
|
nullable: true
|
|
2705
|
-
description: The Rel's non-external endpoint resolved to a local container at member tier, when it is one (
|
|
2705
|
+
description: The Rel's non-external endpoint resolved to a local container at member tier, when it is one (2.33.0) — the datum that lets adjudication rewrite this edge onto container_dependency instead of destroying it. Empty when the endpoint is the system itself or unresolvable.
|
|
2706
2706
|
description: "Edge: a C4 system integrates with an external software system. Captures the L1 context 'System integrates with External System' relationship, extracted from System_Ext(...) + Rel(...) on L1/L2 diagrams. Per M-ADR 0155. Container-level external dependencies are out of scope (system-level only). Added in schema 2.10.0. From 2.30.0 (M-ADR 0189 §4.4): edges whose external endpoint the aggregator adjudicates as an INTERNAL system are rewritten onto system_system_dependency and removed from this catalogue."
|
|
2707
2707
|
foreign_keys:
|
|
2708
2708
|
- column: c4_system_id
|
|
@@ -2734,13 +2734,13 @@ catalogues:
|
|
|
2734
2734
|
- name: source_sdd_id
|
|
2735
2735
|
type: string
|
|
2736
2736
|
nullable: true
|
|
2737
|
-
description: SDD whose diagram declared the originating reference. Member-emitted rows set it directly
|
|
2737
|
+
description: SDD whose diagram declared the originating reference. Member-emitted rows set it directly; adjudication-derived rows are stamped by the aggregator.
|
|
2738
2738
|
- name: platform_sdd_id
|
|
2739
2739
|
type: string
|
|
2740
2740
|
nullable: true
|
|
2741
2741
|
description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
|
|
2742
2742
|
description: |
|
|
2743
|
-
Edge: a C4 container depends on another INTERNAL container (
|
|
2743
|
+
Edge: a C4 container depends on another INTERNAL container (the system-tier gap exactly one tier down: "no edge for internal" at L2). Derived by the aggregator at adjudication: an external-system candidate that resolves to an internal container was never external — each of its dependency edges whose member-resolved `from_container_id` is known is rewritten here, and a candidate with no remaining edges is dropped (it was a phantom duplicate of the container). A candidate with unconvertible edges is kept with the tier-contradiction finding, so no edge is ever destroyed to make a build quieter — the measured cost that stopped an adopter remediating 22 contradictions was eight real edges. ALSO emitted at member tier (2.33.1): the C4-correct authoring — Rel(container_a, container_b) between two Container(...) nodes — emits its edge directly; adjudication merges into a catalogue that already has content rather than being its only source, so the metamodel no longer rewards mis-declaration over correct authoring. Added in schema 2.33.0.
|
|
2744
2744
|
foreign_keys:
|
|
2745
2745
|
- column: from_container_id
|
|
2746
2746
|
references: c4_containers.id
|
|
@@ -2910,7 +2910,7 @@ catalogues:
|
|
|
2910
2910
|
- name: target_locus
|
|
2911
2911
|
type: string
|
|
2912
2912
|
nullable: true
|
|
2913
|
-
description: platform | external | legacy (M-ADR 0204; closed enum, absent/empty = platform). A statement about the TARGET only — never about the consuming product, and never a stability, lifecycle or quality claim. platform rows behave exactly as before this column existed. external/legacy rows carry empty target_capability_id and target_service_id and name their target in target_external_system_id; they skip platform resolution and the
|
|
2913
|
+
description: platform | external | legacy (M-ADR 0204; closed enum, absent/empty = platform). A statement about the TARGET only — never about the consuming product, and never a stability, lifecycle or quality claim. platform rows behave exactly as before this column existed. external/legacy rows carry empty target_capability_id and target_service_id and name their target in target_external_system_id; they skip platform resolution and the synchronous realiser-join, with every skip named.
|
|
2914
2914
|
- name: target_external_system_id
|
|
2915
2915
|
type: string
|
|
2916
2916
|
nullable: true
|
|
@@ -2926,7 +2926,7 @@ catalogues:
|
|
|
2926
2926
|
- name: contract_type
|
|
2927
2927
|
type: string
|
|
2928
2928
|
nullable: true
|
|
2929
|
-
description: rest | graphql | openapi | grpc | event | event-stream | stream | other. Optional; reflects what the consuming SDD declares. The async set (`event`/`event-stream`/`stream`) marks a shared-stream subscription that binds to a stream contract rather than a realiser service (M-ADR 0192) — such rows skip the
|
|
2929
|
+
description: rest | graphql | openapi | grpc | event | event-stream | stream | other. Optional; reflects what the consuming SDD declares. The async set (`event`/`event-stream`/`stream`) marks a shared-stream subscription that binds to a stream contract rather than a realiser service (M-ADR 0192) — such rows skip the synchronous realiser-join at aggregation.
|
|
2930
2930
|
- name: required_stability
|
|
2931
2931
|
type: string
|
|
2932
2932
|
nullable: true
|
|
@@ -2948,7 +2948,7 @@ catalogues:
|
|
|
2948
2948
|
nullable: true
|
|
2949
2949
|
description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
|
|
2950
2950
|
description: |
|
|
2951
|
-
Per-SDD edge: an application-side or integration-side SDD declares that its product depends on a platform-side capability + service pair. Built from `reverse-evidence/feature-grounding-evidence.yaml` `platform_dependencies:` block (product-level — the cohort source shape carries product-scoped dependency rows with a `consumed_by:` list naming which services within the consuming product depend on the target). Each row records one (consuming product, target capability, target service) triple. The aggregator then joins these rows against platform-side `capability_service` to emit `cross_sdd_platform_capability` edges at aggregation time. Cross- SDD references (target_capability_id, target_service_id) are external — they're not expected to resolve in the per-SDD build, only at aggregation. Added in schema 2.9.0
|
|
2951
|
+
Per-SDD edge: an application-side or integration-side SDD declares that its product depends on a platform-side capability + service pair. Built from `reverse-evidence/feature-grounding-evidence.yaml` `platform_dependencies:` block (product-level — the cohort source shape carries product-scoped dependency rows with a `consumed_by:` list naming which services within the consuming product depend on the target). Each row records one (consuming product, target capability, target service) triple. The aggregator then joins these rows against platform-side `capability_service` to emit `cross_sdd_platform_capability` edges at aggregation time. Cross- SDD references (target_capability_id, target_service_id) are external — they're not expected to resolve in the per-SDD build, only at aggregation. Added in schema 2.9.0.
|
|
2952
2952
|
foreign_keys:
|
|
2953
2953
|
- column: product_id
|
|
2954
2954
|
references: products.id
|
|
@@ -2993,7 +2993,7 @@ catalogues:
|
|
|
2993
2993
|
nullable: false
|
|
2994
2994
|
description: Service identifier the consuming SDD references and the platform SDD owns. The join key.
|
|
2995
2995
|
description: |
|
|
2996
|
-
Federated edge: an application-side or integration-side SDD's `product_platform_dependencies` reference resolves against a platform-side SDD's capability spec via the platform capability's `capability_service` roster. Surfaces ecosystem-wide platform- consumer relationships at product → capability granularity. Built **only at aggregation time** — `sdd-cli catalogue aggregate` joins each consuming SDD's `product_platform_dependencies` rows against every other SDD's `capability_service` rows on `target_service_id == service_id`, emitting one row per resolved (consuming-product, platform-capability) pair. Per-SDD builders emit nothing for this catalogue; the aggregator owns it entirely. A platform-dependency whose target_service_id doesn't resolve to any platform-side capability_service row is logged as an `orphan` finding and produces no row. Cross-SDD references are external by definition. Added in schema 2.9.0; see
|
|
2996
|
+
Federated edge: an application-side or integration-side SDD's `product_platform_dependencies` reference resolves against a platform-side SDD's capability spec via the platform capability's `capability_service` roster. Surfaces ecosystem-wide platform- consumer relationships at product → capability granularity. Built **only at aggregation time** — `sdd-cli catalogue aggregate` joins each consuming SDD's `product_platform_dependencies` rows against every other SDD's `capability_service` rows on `target_service_id == service_id`, emitting one row per resolved (consuming-product, platform-capability) pair. Per-SDD builders emit nothing for this catalogue; the aggregator owns it entirely. A platform-dependency whose target_service_id doesn't resolve to any platform-side capability_service row is logged as an `orphan` finding and produces no row. Cross-SDD references are external by definition. Added in schema 2.9.0; see M-ADR 0134.
|
|
2997
2997
|
foreign_keys:
|
|
2998
2998
|
- column: consuming_product_id
|
|
2999
2999
|
references: products.id
|
|
@@ -3131,7 +3131,7 @@ catalogues:
|
|
|
3131
3131
|
nullable: true
|
|
3132
3132
|
description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
|
|
3133
3133
|
description: |
|
|
3134
|
-
Application-product annotation (M-ADR 0191 §4.3 overlay, second element
|
|
3134
|
+
Application-product annotation (M-ADR 0191 §4.3 overlay, second element): one row per conformance tag the product asserts about its archetype's conformance expectations. Built per-SDD from the manifest's `conformance` block so the AGGREGATION-tier conformance GATE can read it. `status=covered` means the product's own conformance suite exercises the tag; `status=deviated` means the product declares (with a rationale) that it deliberately does not. At aggregation, an archetype-expected conformance tag that is neither covered nor deviated raises a deviation-visible WARN. The method gates the DECLARATION — it does not run the adopter's conformance suite. Added in schema 2.39.0.
|
|
3135
3135
|
foreign_keys:
|
|
3136
3136
|
- column: product_id
|
|
3137
3137
|
references: products.id
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
* §4.3 (`orphaned` is aggregate-only; member tier reports
|
|
14
14
|
* `unresolved-locally`, cause-agnostic); the Path-X leverage axis is
|
|
15
15
|
* reported separately and only when repo_capability.csv exists.
|
|
16
|
-
* - event_coverage
|
|
16
|
+
* - event_coverage: every catalogued event is attributed to a
|
|
17
17
|
* runtime — service_event edge or resolving publisher column — with
|
|
18
18
|
* the contract-twin alternative route, the column-vs-edge disagreement
|
|
19
19
|
* controls, and the chain-readout/consumer-enumerability metric lines
|
|
20
20
|
* that are never verdicts.
|
|
21
|
-
* - contract_coverage
|
|
21
|
+
* - contract_coverage: every catalogued contract is reachable —
|
|
22
22
|
* capability_contract or service_contract — with the three computed
|
|
23
23
|
* exclusions (deprecated / provider-surface / not-an-interface, the
|
|
24
24
|
* last a positive READ of the spec file), the covered-as-event
|
|
@@ -178,8 +178,8 @@ FROM "services" s
|
|
|
178
178
|
WHERE s."id" IS NOT NULL AND s."id" <> ''
|
|
179
179
|
ORDER BY service_id, primary_repo;`;
|
|
180
180
|
export async function runServiceCapabilityCoverage(session, options) {
|
|
181
|
-
// Known-bad probe, carried over from the reference implementation
|
|
182
|
-
//
|
|
181
|
+
// Known-bad probe, carried over from the reference implementation:
|
|
182
|
+
// a synthetic claimed service must classify
|
|
183
183
|
// as claimed BEFORE any real data is reported — a run whose
|
|
184
184
|
// classification never executed must not be able to report a healthy
|
|
185
185
|
// estate.
|
|
@@ -89,7 +89,7 @@ export async function runCatalogueCoverage(inputs, write) {
|
|
|
89
89
|
const available = new Set(schema.catalogues.map((c) => c.name).filter((n) => present(n)));
|
|
90
90
|
lines.push(...renderScoresCoverage(await runScoresCoverage(session, { tier, available, sddRoot }), tier));
|
|
91
91
|
// Staleness VERDICTS — the second half of the fourteenth check's §4.5
|
|
92
|
-
// scope
|
|
92
|
+
// scope, member-tier only with the aggregate skip stated.
|
|
93
93
|
// Same coverage class, same reporting-only staging as the rest of the
|
|
94
94
|
// scores family.
|
|
95
95
|
lines.push(...renderStalenessVerdicts(await runStalenessVerdicts(session, { tier, available, sddRoot }), tier));
|
|
@@ -20,10 +20,9 @@
|
|
|
20
20
|
* `build` (where the offending SDD is unambiguous) instead of only at
|
|
21
21
|
* `aggregate`.
|
|
22
22
|
*
|
|
23
|
-
* Background: the method's coordination record feature-edge-cross-source-duplicate.
|
|
24
23
|
* Sibling precedents (the unconditional-emit fixes whose per-walker dedup this
|
|
25
|
-
* generalises across sources):
|
|
26
|
-
* (0.30.2) and
|
|
24
|
+
* generalises across sources): the feature-grounding collision-edge fix
|
|
25
|
+
* (0.30.2) and the capability-spec collision-edge fix (0.30.3).
|
|
27
26
|
*/
|
|
28
27
|
/**
|
|
29
28
|
* Render a cell exactly as the CSV writer will, so two rows count as identical
|