@sdd-method/author-cli 0.1.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.
Files changed (73) hide show
  1. package/NOTICE +1 -1
  2. package/dist/author-program.js +1 -1
  3. package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +4 -4
  4. package/dist/lib/api-surface/analyzers/serverless.js +1 -1
  5. package/dist/lib/api-surface/analyzers/spring.js +1 -1
  6. package/dist/lib/briefing/render.js +1 -2
  7. package/dist/lib/briefing/variables.js +1 -2
  8. package/dist/lib/catalogue/archetype/wireframe.js +1 -1
  9. package/dist/lib/catalogue/build.js +17 -23
  10. package/dist/lib/catalogue/builders/application-conformance-coverage.js +1 -1
  11. package/dist/lib/catalogue/builders/c4-components.js +4 -5
  12. package/dist/lib/catalogue/builders/c4-containers.js +4 -4
  13. package/dist/lib/catalogue/builders/c4-external-systems.js +1 -1
  14. package/dist/lib/catalogue/builders/c4-l1-candidates.js +2 -2
  15. package/dist/lib/catalogue/builders/capabilities.js +2 -2
  16. package/dist/lib/catalogue/builders/capability-edges.js +4 -4
  17. package/dist/lib/catalogue/builders/capability-spec-walker.js +3 -3
  18. package/dist/lib/catalogue/builders/container-dependency.js +19 -20
  19. package/dist/lib/catalogue/builders/contracts.js +5 -6
  20. package/dist/lib/catalogue/builders/data-store-mapping.js +4 -4
  21. package/dist/lib/catalogue/builders/edge-label-merge.js +2 -2
  22. package/dist/lib/catalogue/builders/entities-manifest.js +1 -1
  23. package/dist/lib/catalogue/builders/entities-mermaid.js +3 -3
  24. package/dist/lib/catalogue/builders/entity-slug.js +11 -12
  25. package/dist/lib/catalogue/builders/event-catalog-walker.js +3 -3
  26. package/dist/lib/catalogue/builders/evidence-class.js +2 -2
  27. package/dist/lib/catalogue/builders/external-system-exemption.js +1 -1
  28. package/dist/lib/catalogue/builders/feature-contract.js +1 -3
  29. package/dist/lib/catalogue/builders/feature-grounding.js +1 -2
  30. package/dist/lib/catalogue/builders/feature-groups.js +0 -1
  31. package/dist/lib/catalogue/builders/feature-subscription-channels.js +2 -3
  32. package/dist/lib/catalogue/builders/feature-tree-walker.js +1 -1
  33. package/dist/lib/catalogue/builders/features.js +3 -4
  34. package/dist/lib/catalogue/builders/glossary-xref.js +1 -1
  35. package/dist/lib/catalogue/builders/persona-c4system.js +7 -7
  36. package/dist/lib/catalogue/builders/persona-container.js +14 -14
  37. package/dist/lib/catalogue/builders/personas.js +1 -2
  38. package/dist/lib/catalogue/builders/product-platform-dependencies.js +2 -2
  39. package/dist/lib/catalogue/builders/quality-scores.js +1 -1
  40. package/dist/lib/catalogue/builders/repositories.js +2 -2
  41. package/dist/lib/catalogue/builders/screen-tree.js +2 -2
  42. package/dist/lib/catalogue/builders/screens.js +3 -4
  43. package/dist/lib/catalogue/builders/system-external-dependency.js +7 -7
  44. package/dist/lib/catalogue/canonical-schema.yaml +23 -23
  45. package/dist/lib/catalogue/coverage-predicates.js +4 -4
  46. package/dist/lib/catalogue/coverage-run.js +1 -1
  47. package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
  48. package/dist/lib/catalogue/ext-id.js +4 -4
  49. package/dist/lib/catalogue/feature-grounding-aliases-loader.js +1 -1
  50. package/dist/lib/catalogue/finding-identity.js +3 -3
  51. package/dist/lib/catalogue/freshness.js +1 -1
  52. package/dist/lib/catalogue/manifest-loader.js +2 -2
  53. package/dist/lib/catalogue/scaffold-delta.js +1 -1
  54. package/dist/lib/catalogue/schema-compat.js +1 -2
  55. package/dist/lib/catalogue/scores-coverage.js +1 -1
  56. package/dist/lib/catalogue/screen-validation-loader.js +1 -1
  57. package/dist/lib/catalogue/service-contract-aliases-loader.js +1 -1
  58. package/dist/lib/catalogue/staleness-verdicts.js +2 -2
  59. package/dist/lib/catalogue/traceability-loader.js +2 -3
  60. package/dist/lib/conformance/index.js +29 -8
  61. package/dist/lib/lock-file.js +11 -1
  62. package/dist/lib/package-version.js +1 -1
  63. package/dist/lib/snapshot/manifest.js +1 -1
  64. package/dist/lib/snapshot/targets.js +2 -2
  65. package/dist/lib/validate/index.js +1 -1
  66. package/dist/lib/validate/plan.js +7 -4
  67. package/dist/lib/validate/yaml-parse.js +1 -1
  68. package/dist/maintain-version.json +2 -2
  69. package/dist/verbs/plan.js +3 -3
  70. package/dist/verbs/validate.js +6 -1
  71. package/dist/verify-program.js +1 -1
  72. package/dist/verify-version.json +2 -2
  73. package/package.json +2 -6
@@ -7,18 +7,17 @@
7
7
  // authoring resolves to a single canonical slug.
8
8
  //
9
9
  // Examples:
10
- // entitySlug("OBUConfigurationTemplate") → "obu-configuration-template"
11
- // entitySlug("OBU_CONFIGURATION_TEMPLATE") → "obu-configuration-template"
10
+ // entitySlug("POSTerminalConfig") → "pos-terminal-config"
11
+ // entitySlug("POS_TERMINAL_CONFIG") → "pos-terminal-config"
12
12
  // entitySlug("Vehicle") → "vehicle"
13
- // entitySlug("APIKeyRotationRecord") → "api-key-rotation-record"
13
+ // entitySlug("GTINBarcodeRecord") → "gtin-barcode-record"
14
14
  // entitySlug("customer") → "customer" (lowercase Mermaid)
15
- // entitySlug("accountFeatures") → "account-features"
15
+ // entitySlug("basketItems") → "basket-items"
16
16
  //
17
- // Phase Pre finding (research/adr-0159-prototypes/PHASE-PRE-SYNTHESIS.md):
18
- // without the ALLCAPS-then-Pascal rule, the Data Store Mapping table form
19
- // (`OBUConfigurationTemplate`) never resolves to the Mermaid form
20
- // (`OBU_CONFIGURATION_TEMPLATE`), breaking ~50% of expected service_entity
21
- // edges in Adopter A.
17
+ // Measured finding: without the ALLCAPS-then-Pascal rule, the Data Store
18
+ // Mapping table form (`POSTerminalConfig`) never resolves to the Mermaid form
19
+ // (`POS_TERMINAL_CONFIG`), breaking ~50% of expected service_entity
20
+ // edges in one reference estate.
22
21
  //
23
22
  // Acronym-run rule (ADR 0164 wave-2 follow-on): the first replace requires the
24
23
  // leading uppercase run to be >=2 chars (`[A-Z]{2,}`). A *single* leading
@@ -29,9 +28,9 @@
29
28
  // Before the >=2 rule these produced "o-auth-client" vs "oauth-client".
30
29
  // Verified zero PK churn across both reference adopters (263 + 414 entities).
31
30
  // This cannot reconcile names whose PascalCase and UPPER_SNAKE forms genuinely
32
- // disagree on word boundaries — e.g. "SigLink" (two words) vs "SIGLINK" (one)
33
- // -> "sig-link" vs "siglink". That is adopter authoring drift, resolved by
34
- // aligning the spellings (author `SIG_LINK`), not by this normaliser.
31
+ // disagree on word boundaries — e.g. "KeyHub" (two words) vs "KEYHUB" (one)
32
+ // -> "key-hub" vs "keyhub". That is adopter authoring drift, resolved by
33
+ // aligning the spellings (author `KEY_HUB`), not by this normaliser.
35
34
  //
36
35
  // Kept local to the entity walkers to avoid affecting events / contracts /
37
36
  // capability id generation. Promote to a shared module only if a fourth
@@ -80,8 +80,8 @@ export async function walkEventCatalogs(inputs) {
80
80
  const explicitId = String(e["id"] ?? "").trim();
81
81
  const name = String(e["name"] ?? "").trim();
82
82
  // Derive id: explicit id wins when present (allows future override);
83
- // otherwise slug from name. Original ADR 0156 coordination doc spec:
84
- // "derive `id` from `name` (slugged)". Some adopters' authoring uses
83
+ // otherwise slug from name, per ADR 0156: "derive `id` from `name`
84
+ // (slugged)". Some adopters' authoring uses
85
85
  // `name:` only (no `id:` field).
86
86
  let id;
87
87
  if (explicitId !== "") {
@@ -141,7 +141,7 @@ export async function walkEventCatalogs(inputs) {
141
141
  /**
142
142
  * Slugify an event name into an id.
143
143
  *
144
- * - Inserts a hyphen at camelCase boundaries (`AssetTwin` → `Asset-Twin`).
144
+ * - Inserts a hyphen at camelCase boundaries (`AssetRegister` → `Asset-Register`).
145
145
  * - Lowercases.
146
146
  * - Replaces any run of non-alphanumeric characters with a single hyphen.
147
147
  * - Trims leading/trailing hyphens.
@@ -7,8 +7,8 @@
7
7
  //
8
8
  // ```yaml
9
9
  // evidence-class: vendor-doc
10
- // vendor: workos
11
- // vendor-version: workos-docs@2026-06-10
10
+ // vendor: acme-idp
11
+ // vendor-version: acme-idp-docs@2026-01-01
12
12
  // anchors: [...]
13
13
  // ```
14
14
  //
@@ -1,4 +1,4 @@
1
- // external_system_exemption builder — M-ADR 0189 §4.6 (gov WS15).
1
+ // external_system_exemption builder — M-ADR 0189 §4.6.
2
2
  //
3
3
  // Emits one row per entry in `orchestration/external-system-exemptions.yaml`:
4
4
  //
@@ -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 per
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 resolved decision §1 of `content/plans/v0-9-0-cohort-sweep-derived-bundle.md`
6
- // (see also `docs/method/methods/reverse/rubrics/reverse-application-evidence-quality.md`
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 — see WS0c of `catalogue-coverage-invariants.md`). Table shape:
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 "alerts-rules-engine"
8
- // with feature_directory pointing at "features/alerts-rules/") —
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 from
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";
@@ -342,7 +342,7 @@ function collectPairs(raw) {
342
342
  return out;
343
343
  }
344
344
  function resolveTerm(raw, inputs) {
345
- // Direct id (e.g. "asset-vehicle")
345
+ // Direct id (e.g. "stock-sku")
346
346
  if (inputs.knownGlossaryIds.has(raw))
347
347
  return raw;
348
348
  // domain:term lookup
@@ -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 (F10; F9's
13
- // first fix keyed on notation and broke the external-human case): the
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 F8's reverse finding on
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 (F8, M-ADR 0157 parity + M-ADR 0133 §4.2c
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 (F10; see header).
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 (F10).
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
- // F8 — the reverse direction (M-ADR 0157 parity): every persona that ends
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 F26 §3 (schema 2.34.0).
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 no catalogue and, until F26, no finding —
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. F26 made the silence loud; this gives the
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 (F20's rule). Either Rel direction
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 (F14); parallel
14
- // labels concatenate loudly (F25).
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
- // F31: only the SDD's OWN system entry is persona_c4system's domain —
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
- // (F5's shape, one tier over): four of an estate's best-authored
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
- // F29/F30 — the person tier's exits, designed once. Identify the
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 per F20), then route by the OTHER endpoint's kind:
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 (F30): a human using an
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 (F29): claimed by no
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 (F30)`
104
- : `persona_container: persona "${personaId}" → "${targetEntry}"${label ? ` ("${label}")` : ""} resolves to no container — claimed by no catalogue; skipped (F29)`);
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 (per coordination/persona-walker-platform-product-gap-
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 F18 realiser-join.
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 F18 realiser-join does not apply to it at
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 (ledger §3am): an ingestion updated
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 WorkOS for Auth.md`). The
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 WorkOS for Auth.md
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.
@@ -35,7 +35,7 @@ const STRING_RE = /"[^"]*"|'[^']*'/g;
35
35
  * line-structure-preserving) so call-head lexing can never fire on quoted
36
36
  * content — `Table("Odometer (km)")` must not mint a phantom `Odometer`
37
37
  * component, and `"total (sum)"` must not false-fire the lowercase-call-head
38
- * finding (tomfosteruk/sdd-cli#1). Quote characters are kept; newlines inside
38
+ * finding (sdd-cli issue 1). Quote characters are kept; newlines inside
39
39
  * a span survive. The grammar defines no escape sequences (a quote always
40
40
  * closes the literal), matching STRING_RE and stripLineComment.
41
41
  *
@@ -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="user-details-changed"` (the block-level
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 — see
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
- // `MyEcoDrive` / `map_tracking_container` / `TE_driver` resolves stably
309
- // (per the Phase Pre real-menu finding; mirrors entitySlug).
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 (F11c) — see
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
- // F14: a Rel in the syndicated platform L1 is a statement about the
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
- // (F17) the non-external Rel endpoint may be a local container — carry
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
- // F30: a Person-sourced Rel must never enter this catalogue — its
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
- // F28: classification is CONTAINER-FIRST, uniformly with the
64
- // container-dependency builder (F20 / ledger L6). An entry-id that
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
- // L6's fix was applied to one builder and not swept into this one.
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