@sdd-method/author-cli 0.12.1 → 0.14.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/README.md +1 -1
- package/dist/author-program.js +10 -2
- package/dist/lib/catalogue/build.js +72 -23
- package/dist/lib/catalogue/builders/adr-governs.js +1 -1
- package/dist/lib/catalogue/builders/adrs.js +2 -2
- package/dist/lib/catalogue/builders/c4-containers.js +6 -2
- package/dist/lib/catalogue/builders/contract-revisions.js +45 -7
- package/dist/lib/catalogue/builders/contracts.js +4 -11
- package/dist/lib/catalogue/builders/data-model.js +1 -1
- package/dist/lib/catalogue/builders/entities-manifest.js +10 -4
- package/dist/lib/catalogue/builders/entity-archetype.js +121 -0
- package/dist/lib/catalogue/builders/entity-merge.js +90 -0
- package/dist/lib/catalogue/builders/features.js +1 -1
- package/dist/lib/catalogue/builders/glossary-xref.js +11 -5
- package/dist/lib/catalogue/builders/journey-requirement.js +5 -5
- package/dist/lib/catalogue/builders/quality-scores.js +1 -1
- package/dist/lib/catalogue/builders/requirements.js +1 -1
- package/dist/lib/catalogue/builders/service-contract-consumes.js +2 -2
- package/dist/lib/catalogue/builders/service-entity-tables.js +1 -1
- package/dist/lib/catalogue/canonical-schema.yaml +50 -30
- package/dist/lib/catalogue/contract-id.js +49 -12
- package/dist/lib/catalogue/manifest-loader.js +1 -1
- package/dist/lib/catalogue/retired-catalogues.js +1 -1
- package/dist/lib/catalogue/rubric-coverage-map.yaml +7 -3
- package/dist/lib/catalogue/schema-compat.js +1 -1
- package/dist/lib/catalogue/staleness-verdicts.js +3 -3
- package/dist/lib/structure/apply.js +71 -0
- package/dist/lib/structure/archive.js +330 -0
- package/dist/lib/structure/catalogue.js +31 -10
- package/dist/lib/structure/compose.js +1 -1
- package/dist/lib/structure/effects.js +1 -1
- package/dist/lib/structure/extract.js +123 -0
- package/dist/lib/structure/intended.js +194 -0
- package/dist/lib/structure/model.js +1 -1
- package/dist/lib/structure/render.js +1 -1
- package/dist/lib/structure/view.js +8 -3
- package/dist/lib/validate/agreement-evidence.js +2 -2
- package/dist/lib/validate/behaviour-store.js +49 -12
- package/dist/lib/validate/dispatch.js +2 -2
- package/dist/lib/validate/journey-impact.js +7 -7
- package/dist/lib/validate/structural-model.js +48 -35
- package/dist/maintain-version.json +2 -2
- package/dist/verbs/change.js +15 -0
- package/dist/verbs/migrate-structure.js +36 -0
- package/dist/verify-version.json +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// entities: the data-model walker's rows merged with the glossary-alignment manifest's (M-ADR 0159 §4.4).
|
|
2
|
+
//
|
|
3
|
+
// Field by field: a manifest value wins where it is non-empty, and the data model's value stands where the
|
|
4
|
+
// manifest is silent, so source_domain_model_id and the columns= note survive a manifest entry naming the
|
|
5
|
+
// same entity. Notes from both are kept.
|
|
6
|
+
//
|
|
7
|
+
// A platform-wide manifest entry (`_platform-wide-<slug>`, M-ADR 0159 §4.2) whose slug names exactly one
|
|
8
|
+
// domain entity (`<domain>-<slug>`) IS that entity: it fills the domain entity's empty fields and emits no row
|
|
9
|
+
// of its own, so one entity is one element. Its glossary term moves with it, and the entity_glossary step
|
|
10
|
+
// derives the edge from the domain entity. An entry matching no domain entity, or several, stays platform-wide
|
|
11
|
+
// and is reported.
|
|
12
|
+
import { PLATFORM_WIDE_PK_PREFIX } from "./entities-manifest.js";
|
|
13
|
+
const MANIFEST_REL_PATH = "glossary-alignment-manifest.yaml";
|
|
14
|
+
function filled(v) {
|
|
15
|
+
return v !== undefined && v !== null && String(v) !== "";
|
|
16
|
+
}
|
|
17
|
+
function joinNotes(a, b) {
|
|
18
|
+
const parts = [];
|
|
19
|
+
for (const v of [a, b]) {
|
|
20
|
+
for (const p of String(v ?? "").split(";"))
|
|
21
|
+
if (p !== "" && !parts.includes(p))
|
|
22
|
+
parts.push(p);
|
|
23
|
+
}
|
|
24
|
+
return parts.join(";");
|
|
25
|
+
}
|
|
26
|
+
/** `over`'s non-empty values on top of `base`; notes from both. */
|
|
27
|
+
function overlay(base, over) {
|
|
28
|
+
const out = { ...base };
|
|
29
|
+
for (const [k, v] of Object.entries(over)) {
|
|
30
|
+
if (k === "notes")
|
|
31
|
+
out[k] = joinNotes(base[k], v);
|
|
32
|
+
else if (filled(v))
|
|
33
|
+
out[k] = v;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
/** `extra`'s values only where `base` is empty; notes from both. */
|
|
38
|
+
function fillEmpty(base, extra) {
|
|
39
|
+
const out = { ...base };
|
|
40
|
+
for (const [k, v] of Object.entries(extra)) {
|
|
41
|
+
if (k === "id" || k === "name" || k === "domain_id")
|
|
42
|
+
continue;
|
|
43
|
+
if (k === "notes")
|
|
44
|
+
out[k] = joinNotes(base[k], v);
|
|
45
|
+
else if (!filled(base[k]) && filled(v))
|
|
46
|
+
out[k] = v;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
export function mergeEntities(fromDataModels, fromManifest, findings,
|
|
51
|
+
/** The sentinel ids from a platform-wide domain hint (entities-manifest's out-set): the only ones that fold. */
|
|
52
|
+
platformWideIds) {
|
|
53
|
+
const byId = new Map();
|
|
54
|
+
for (const r of fromDataModels)
|
|
55
|
+
byId.set(String(r["id"]), r);
|
|
56
|
+
for (const r of fromManifest) {
|
|
57
|
+
const id = String(r["id"]);
|
|
58
|
+
const existing = byId.get(id);
|
|
59
|
+
byId.set(id, existing ? overlay(existing, r) : r);
|
|
60
|
+
}
|
|
61
|
+
const prefix = `${PLATFORM_WIDE_PK_PREFIX}-`;
|
|
62
|
+
for (const [id, row] of [...byId]) {
|
|
63
|
+
if (!id.startsWith(prefix) || !platformWideIds.has(id))
|
|
64
|
+
continue;
|
|
65
|
+
const slug = id.slice(prefix.length);
|
|
66
|
+
const matches = [...byId.values()].filter((e) => {
|
|
67
|
+
const dom = String(e["domain_id"] ?? "");
|
|
68
|
+
return dom !== "" && String(e["id"]) === `${dom}-${slug}`;
|
|
69
|
+
});
|
|
70
|
+
if (matches.length === 1) {
|
|
71
|
+
const target = matches[0];
|
|
72
|
+
const own = String(target["glossary_term"] ?? "");
|
|
73
|
+
const theirs = String(row["glossary_term"] ?? "");
|
|
74
|
+
if (own !== "" && theirs !== "" && own !== theirs) {
|
|
75
|
+
findings.add("divergence", `${MANIFEST_REL_PATH}: platform-wide entity ${row["name"]} names glossary term ${theirs}, but ${target["id"]} ` +
|
|
76
|
+
`already names ${own} — kept ${own}`);
|
|
77
|
+
}
|
|
78
|
+
byId.set(String(target["id"]), fillEmpty(target, row));
|
|
79
|
+
byId.delete(id);
|
|
80
|
+
}
|
|
81
|
+
else if (matches.length === 0) {
|
|
82
|
+
findings.add("coverage", `${MANIFEST_REL_PATH}: platform-wide entity ${row["name"]} names no domain entity — kept as ${id}`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
findings.add("divergence", `${MANIFEST_REL_PATH}: platform-wide entity ${row["name"]} names ${matches.length} domain entities (${matches.map((m) => m["id"]).join(", ")}) — kept as ${id}; give it a domain`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return [...byId.values()];
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=entity-merge.js.map
|
|
@@ -158,7 +158,7 @@ export function featureStatus(text) {
|
|
|
158
158
|
}
|
|
159
159
|
/**
|
|
160
160
|
* A feature spec's kind (schema 4.4.0): the value of its `**Kind:**` line, lower-cased, else `feature`. An
|
|
161
|
-
* obligation (M-ADR 0226, Proposed) and a journey feature (M-ADR 0229 §4.2
|
|
161
|
+
* obligation (M-ADR 0226, Proposed) and a journey feature (M-ADR 0229 §4.2) say so; any other value is written as given, so
|
|
162
162
|
* the catalogue's vocabulary gate names the spec rather than a default hiding the mistake.
|
|
163
163
|
*/
|
|
164
164
|
export function featureKind(text) {
|
|
@@ -27,8 +27,9 @@
|
|
|
27
27
|
// 4. Forward-method supersedes (new).
|
|
28
28
|
// models[].entities[] with status: superseded + superseded_by
|
|
29
29
|
// One row per supersedes mapping after resolving both endpoints'
|
|
30
|
-
// glossary_term against glossary_items.csv
|
|
31
|
-
//
|
|
30
|
+
// glossary_term against glossary_items.csv: the successor (superseded_by)
|
|
31
|
+
// is the source and supersedes the superseded term, the target. Orphan
|
|
32
|
+
// finding when an endpoint doesn't resolve.
|
|
32
33
|
//
|
|
33
34
|
// 5. Forward-method entity-mapping (new, writes to entity_glossary).
|
|
34
35
|
// models[].entities[] with status ∈ {aligned, matched_direct} and
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
import { readFile } from "node:fs/promises";
|
|
41
42
|
import { join } from "node:path";
|
|
42
43
|
import { parse as parseYaml } from "yaml";
|
|
44
|
+
import { NON_CANONICAL_DOMAINS } from "./entities-manifest.js";
|
|
43
45
|
const MANIFEST_REL_PATH = join("docs", "platform", "reference", "glossary-alignment-manifest.yaml");
|
|
44
46
|
export async function buildGlossaryXref(inputs) {
|
|
45
47
|
// Profile gate per M-ADR 0158 §amendment: the manifest is platform-owned;
|
|
@@ -259,14 +261,18 @@ export async function buildGlossaryXref(inputs) {
|
|
|
259
261
|
inputs.findings.add("orphan", `${MANIFEST_REL_PATH}: supersedes edge can't emit — target "${supersededBy}" (→ glossary "${targetEntity?.glossaryTerm ?? "?"}") doesn't resolve to a glossary item`);
|
|
260
262
|
continue;
|
|
261
263
|
}
|
|
262
|
-
|
|
264
|
+
// The successor supersedes the superseded term: `source supersedes target`, as adr_supersedes reads.
|
|
265
|
+
emitXref(targetId, sourceId, "supersedes");
|
|
263
266
|
continue;
|
|
264
267
|
}
|
|
265
268
|
if ((status === "aligned" || status === "matched_direct") && glossaryTerm !== "") {
|
|
266
269
|
// Entity-mapping walker writes to entity_glossary.csv.
|
|
267
270
|
if (domain === "" || !inputs.knownDomainIds.has(domain)) {
|
|
268
|
-
//
|
|
269
|
-
//
|
|
271
|
+
// A platform-wide entry is the domain entity its slug names (entity-merge.ts), and the entity_glossary
|
|
272
|
+
// edge is derived from that entity after the merge; the merge reports one it cannot place. Any other
|
|
273
|
+
// domain not in this SDD's domains.csv is skipped with a coverage finding.
|
|
274
|
+
if (NON_CANONICAL_DOMAINS.has(domain))
|
|
275
|
+
continue;
|
|
270
276
|
inputs.findings.add("coverage", `${MANIFEST_REL_PATH}: entity ${name} domain "${domain}" not in known domains — entity_glossary row skipped`);
|
|
271
277
|
continue;
|
|
272
278
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// journey_requirement: the requirements a user journey cites for its steps (M-ADR 0229 §4.4
|
|
1
|
+
// journey_requirement: the requirements a user journey cites for its steps (M-ADR 0229 §4.4; schema 4.4.0).
|
|
2
2
|
//
|
|
3
3
|
// Derived, never authored, from either master (§4.2):
|
|
4
4
|
// tag option A: the end-to-end `.feature` files under the manifest's `journeys.test_paths`. A file tagged
|
|
@@ -64,13 +64,13 @@ export async function buildJourneyRequirement(inputs) {
|
|
|
64
64
|
for (const m of text.matchAll(REQ_TAG)) {
|
|
65
65
|
const quiet = atAggregate && inputs.reportedFeatureIds.has(m[1]);
|
|
66
66
|
if (!knownFeatures.has(m[1])) {
|
|
67
|
-
findings.add("external", `${where}: @req:${m[1]}/${m[2]} names a feature this catalogue does not hold; resolves at aggregation (M-ADR 0229 §4.4
|
|
67
|
+
findings.add("external", `${where}: @req:${m[1]}/${m[2]} names a feature this catalogue does not hold; resolves at aggregation (M-ADR 0229 §4.4)`);
|
|
68
68
|
continue;
|
|
69
69
|
}
|
|
70
70
|
const ids = bySlug.get(`${m[1]}/${m[2]}`) ?? [];
|
|
71
71
|
if (ids.length !== 1) {
|
|
72
72
|
if (!quiet)
|
|
73
|
-
findings.add("orphan", `${where}: @req:${m[1]}/${m[2]} ${ids.length === 0 ? "names no requirement of that feature" : `matches ${ids.length} requirements of that feature`}; no journey_requirement row (M-ADR 0229 §4.4
|
|
73
|
+
findings.add("orphan", `${where}: @req:${m[1]}/${m[2]} ${ids.length === 0 ? "names no requirement of that feature" : `matches ${ids.length} requirements of that feature`}; no journey_requirement row (M-ADR 0229 §4.4)`);
|
|
74
74
|
continue;
|
|
75
75
|
}
|
|
76
76
|
const id = `${journeyId}__${ids[0]}`;
|
|
@@ -95,7 +95,7 @@ export async function buildJourneyRequirement(inputs) {
|
|
|
95
95
|
continue;
|
|
96
96
|
if (journeys.length > 1) {
|
|
97
97
|
if (!atAggregate)
|
|
98
|
-
findings.add("divergence", `${where}: tagged with ${journeys.length} journeys (${journeys.join(", ")}); one file is one journey's master (M-ADR 0229 §4.2
|
|
98
|
+
findings.add("divergence", `${where}: tagged with ${journeys.length} journeys (${journeys.join(", ")}); one file is one journey's master (M-ADR 0229 §4.2)`);
|
|
99
99
|
continue;
|
|
100
100
|
}
|
|
101
101
|
const journeyId = journeys[0];
|
|
@@ -129,7 +129,7 @@ export async function buildJourneyRequirement(inputs) {
|
|
|
129
129
|
const journeys = [...new Set([...spec.matchAll(JOURNEY_TAG)].map((m) => m[1]))];
|
|
130
130
|
if (journeys.length !== 1) {
|
|
131
131
|
if (!atAggregate)
|
|
132
|
-
findings.add(journeys.length === 0 ? "orphan" : "divergence", `${where}: a journey feature's living spec ${journeys.length === 0 ? "names no journey (@journey:<journey-id>)" : `names ${journeys.length} journeys`}; one journey feature masters one journey (M-ADR 0229 §4.2
|
|
132
|
+
findings.add(journeys.length === 0 ? "orphan" : "divergence", `${where}: a journey feature's living spec ${journeys.length === 0 ? "names no journey (@journey:<journey-id>)" : `names ${journeys.length} journeys`}; one journey feature masters one journey (M-ADR 0229 §4.2)`);
|
|
133
133
|
continue;
|
|
134
134
|
}
|
|
135
135
|
const journeyId = journeys[0];
|
|
@@ -119,7 +119,7 @@ const num = (v) => {
|
|
|
119
119
|
return null;
|
|
120
120
|
};
|
|
121
121
|
/**
|
|
122
|
-
* The review's other assessed inputs (M-ADR 0230 §4.9
|
|
122
|
+
* The review's other assessed inputs (M-ADR 0230 §4.9): `inputs:` as a list of paths, or of `{path}`
|
|
123
123
|
* entries, each read at the review's corpus_ref. ';'-joined; null when the review names none.
|
|
124
124
|
*/
|
|
125
125
|
function inputPaths(v) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// change_work_order per (change, repository): the record's work order, agreed or completed.
|
|
19
19
|
// change_work_item per (change, work item): the ids its proposal's `## Work Items` section lists, keyed by
|
|
20
20
|
// the undated name so the row survives archive.
|
|
21
|
-
// requirement_revisions one row per distinct text of a requirement (schema 6.0.0, M-ADR 0230 §4.7
|
|
21
|
+
// requirement_revisions one row per distinct text of a requirement (schema 6.0.0, M-ADR 0230 §4.7): the
|
|
22
22
|
// living spec's is current, an agreed open change's ADDED or MODIFIED block intended, an
|
|
23
23
|
// archived change's block the living spec no longer holds history. Identified by its digest.
|
|
24
24
|
// requirement_assertions per (revision, repository): observed from a baseline's `**Observed in:**` line;
|
|
@@ -59,13 +59,13 @@ export async function buildServiceContractConsumes(inputs) {
|
|
|
59
59
|
const mapped = inputs.legacyContractIdMap?.get(contractId);
|
|
60
60
|
if (mapped !== undefined && mapped.length === 1) {
|
|
61
61
|
inputs.findings.add("inferred", `service_contract: alias target "${contractId}" (${serviceId} ← "${iface.interfaceText}") re-keyed to ` +
|
|
62
|
-
`"${mapped[0]}" — 2.x kindless
|
|
62
|
+
`"${mapped[0]}" — an earlier-form contract id (2.x kindless, or a pre-7.0.0 unsplit stem) migrated per the contract-id map (regenerable, not drift); ` +
|
|
63
63
|
`update .sdd/service-contract-aliases.yaml to retire this finding`);
|
|
64
64
|
contractId = mapped[0];
|
|
65
65
|
}
|
|
66
66
|
else if (mapped !== undefined && mapped.length > 1) {
|
|
67
67
|
inputs.findings.add("convention", `service_contract: alias target "${contractId}" (${serviceId} ← "${iface.interfaceText}") maps to ` +
|
|
68
|
-
`${mapped.length} contracts
|
|
68
|
+
`${mapped.length} current contracts [${mapped.join(", ")}] — a cross-family collision under the earlier form; ` +
|
|
69
69
|
`qualify the alias with the kind-suffixed id; edge dropped`);
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
@@ -128,7 +128,7 @@ export async function buildServiceEntityFromTables(inputs) {
|
|
|
128
128
|
}
|
|
129
129
|
return [...rowsByPk.values()];
|
|
130
130
|
}
|
|
131
|
-
async function loadAliasMap(sddPath, findings) {
|
|
131
|
+
export async function loadAliasMap(sddPath, findings) {
|
|
132
132
|
const empty = { byDisplayLower: new Map() };
|
|
133
133
|
const path = join(sddPath, ALIAS_REL_PATH);
|
|
134
134
|
let text;
|
|
@@ -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: "
|
|
10
|
+
schema_version: "7.1.0"
|
|
11
11
|
schema_owner: "sdd-method"
|
|
12
12
|
schema_status: "generated"
|
|
13
13
|
enums:
|
|
@@ -645,7 +645,7 @@ enums:
|
|
|
645
645
|
description: Its change archived on a passing judge, with this repository's work order implementing it.
|
|
646
646
|
- value: observed
|
|
647
647
|
description: Independent evidence from this repository's code matches (a baseline's characterization tests or a reverse snapshot).
|
|
648
|
-
description: The kind of evidence an assertion is about one revision, in one repository (M-ADR 0230 §4.7
|
|
648
|
+
description: The kind of evidence an assertion is about one revision, in one repository (M-ADR 0230 §4.7).
|
|
649
649
|
change_status:
|
|
650
650
|
values:
|
|
651
651
|
- value: proposed
|
|
@@ -685,15 +685,15 @@ enums:
|
|
|
685
685
|
- value: obligation
|
|
686
686
|
description: Behaviour every feature in a population depends on, owned as a feature of its own (M-ADR 0226, Proposed).
|
|
687
687
|
- value: journey
|
|
688
|
-
description: A user journey's joint behaviour, held in the behaviour store (M-ADR 0229 §4.2, option B
|
|
689
|
-
description: What a catalogued feature is (M-ADR 0226 and M-ADR 0229
|
|
688
|
+
description: A user journey's joint behaviour, held in the behaviour store (M-ADR 0229 §4.2, option B).
|
|
689
|
+
description: What a catalogued feature is (M-ADR 0226, Proposed, and M-ADR 0229).
|
|
690
690
|
journey_requirement_basis:
|
|
691
691
|
values:
|
|
692
692
|
- value: tag
|
|
693
693
|
description: A @req tag in the journey's end-to-end test file (option A).
|
|
694
694
|
- value: citation
|
|
695
695
|
description: A citation in the journey feature's living spec (option B).
|
|
696
|
-
description: Where a journey's citation of a requirement was read (M-ADR 0229 §4.4
|
|
696
|
+
description: Where a journey's citation of a requirement was read (M-ADR 0229 §4.4).
|
|
697
697
|
source_of_truth:
|
|
698
698
|
values:
|
|
699
699
|
- value: structure
|
|
@@ -714,14 +714,14 @@ enums:
|
|
|
714
714
|
description: Observations, scores and findings, about a named revision.
|
|
715
715
|
- value: execution
|
|
716
716
|
description: "The change records and the build itself: changes, work orders, lineage, and the provenance the build or aggregation stamps."
|
|
717
|
-
description: Where a catalogue's facts, or a column's, are authored (M-ADR 0230 §4.1
|
|
717
|
+
description: Where a catalogue's facts, or a column's, are authored (M-ADR 0230 §4.1). Each catalogue carries one; a column carries one only where it differs.
|
|
718
718
|
relationship_end_cardinality:
|
|
719
719
|
values:
|
|
720
720
|
- value: exactly-one
|
|
721
721
|
- value: zero-or-one
|
|
722
722
|
- value: one-or-more
|
|
723
723
|
- value: zero-or-more
|
|
724
|
-
description: "How many entities one end of a relationship holds for one entity at the other end (M-ADR 0230 §4.3
|
|
724
|
+
description: "How many entities one end of a relationship holds for one entity at the other end (M-ADR 0230 §4.3): a Mermaid ER end marker (|| exactly one, |o or o| zero or one, }| or |{ one or more, }o or o{ zero or more). A TypeQL relationship is zero-or-more at both ends, because the subset admits no participation cardinality; a role's @card is a cardinality invariant (domain_invariants). Replaced entity_relationship_cardinality in schema 6.0.0."
|
|
725
725
|
domain_invariant_kind:
|
|
726
726
|
values:
|
|
727
727
|
- value: unique
|
|
@@ -732,7 +732,7 @@ enums:
|
|
|
732
732
|
description: A bound on how many entities the named relationship relates, stated in the statement.
|
|
733
733
|
- value: rule
|
|
734
734
|
description: Any other rule, stated in the statement.
|
|
735
|
-
description: The type of a domain invariant (M-ADR 0230 §4.3
|
|
735
|
+
description: The type of a domain invariant (M-ADR 0230 §4.3).
|
|
736
736
|
revision_state:
|
|
737
737
|
values:
|
|
738
738
|
- value: current
|
|
@@ -741,14 +741,14 @@ enums:
|
|
|
741
741
|
description: An agreed open change states it.
|
|
742
742
|
- value: history
|
|
743
743
|
description: Once published or stated, and no longer current.
|
|
744
|
-
description: Which view a revision belongs to (M-ADR 0230 §4.7
|
|
744
|
+
description: Which view a revision belongs to (M-ADR 0230 §4.7).
|
|
745
745
|
change_effect_element_kind:
|
|
746
746
|
values:
|
|
747
747
|
- value: requirement
|
|
748
748
|
description: A requirement, written by a behaviour delta.
|
|
749
749
|
- value: structure
|
|
750
750
|
description: An element of the structural model, written by a structural delta.
|
|
751
|
-
description: What kind of element a change effect writes (M-ADR 0230 §4.7
|
|
751
|
+
description: What kind of element a change effect writes (M-ADR 0230 §4.7).
|
|
752
752
|
adr_governs:
|
|
753
753
|
values:
|
|
754
754
|
- value: current
|
|
@@ -757,7 +757,7 @@ enums:
|
|
|
757
757
|
description: "The system as the agreed changes will leave it: the decision applies from its change's archive."
|
|
758
758
|
- value: none
|
|
759
759
|
description: "Neither: proposed, rejected, deprecated, superseded by a decision now in force, or its change withdrawn."
|
|
760
|
-
description: Which view a decision governs (M-ADR 0230 §4.2
|
|
760
|
+
description: Which view a decision governs (M-ADR 0230 §4.2).
|
|
761
761
|
contract_revision_state:
|
|
762
762
|
values:
|
|
763
763
|
- value: agreed
|
|
@@ -768,7 +768,22 @@ enums:
|
|
|
768
768
|
description: Applicable, with a sunset.
|
|
769
769
|
- value: retired
|
|
770
770
|
description: "No longer applicable: an archived change's revision the file no longer holds."
|
|
771
|
-
description: A contract revision's applicability in the published specification (M-ADR 0230 §4.2
|
|
771
|
+
description: A contract revision's applicability in the published specification (M-ADR 0230 §4.2).
|
|
772
|
+
ppt_archetype:
|
|
773
|
+
values:
|
|
774
|
+
- value: party
|
|
775
|
+
description: A person or an organisation.
|
|
776
|
+
- value: place
|
|
777
|
+
description: A location where things happen or are kept.
|
|
778
|
+
- value: thing
|
|
779
|
+
description: An object that is tracked individually.
|
|
780
|
+
- value: role
|
|
781
|
+
description: A way a party, place or thing takes part in something.
|
|
782
|
+
- value: moment-interval
|
|
783
|
+
description: Something that happens at a moment or over an interval, and is recorded.
|
|
784
|
+
- value: description
|
|
785
|
+
description: A catalogue-entry description that many things share.
|
|
786
|
+
description: An entity's Party-Place-Thing archetype, as the capability spec template classifies it (schema 7.1.0).
|
|
772
787
|
catalogues:
|
|
773
788
|
domains:
|
|
774
789
|
primary_key: id
|
|
@@ -1220,7 +1235,7 @@ catalogues:
|
|
|
1220
1235
|
- name: effective_change
|
|
1221
1236
|
type: string
|
|
1222
1237
|
nullable: true
|
|
1223
|
-
description: "From the ADR's **Effective:** line (M-ADR 0230 §4.2
|
|
1238
|
+
description: "From the ADR's **Effective:** line (M-ADR 0230 §4.2): the change whose archive makes the decision apply, by its undated name; empty when it applies on acceptance. Added in schema 6.0.0."
|
|
1224
1239
|
- name: governs
|
|
1225
1240
|
type: string
|
|
1226
1241
|
nullable: true
|
|
@@ -1318,7 +1333,7 @@ catalogues:
|
|
|
1318
1333
|
- name: relation
|
|
1319
1334
|
type: string
|
|
1320
1335
|
nullable: false
|
|
1321
|
-
description: alias-of | conflicts-with | references | supersedes (extended in schema 2.12.0 per M-ADR 0158).
|
|
1336
|
+
description: "alias-of | conflicts-with | references | supersedes (extended in schema 2.12.0 per M-ADR 0158). Read as source, relation, target: for supersedes the successor is the source and the superseded term the target."
|
|
1322
1337
|
description: "Edge: cross-domain glossary relationships from glossary-alignment-manifest.yaml. Per M-ADR 0158, four walkers ingest into this catalogue: (a) forward declared pair-form {source, target} entries under aliases/conflicts, (b) reverse-method cross_domain_terms (emits references edges per referencer→owner), (c) reverse-method multi-domain conflicts (pair-wise expansion across domains[]), (d) forward-method supersedes (status: superseded entries with superseded_by back-link)."
|
|
1323
1338
|
natural_key:
|
|
1324
1339
|
- source_id
|
|
@@ -1503,7 +1518,7 @@ catalogues:
|
|
|
1503
1518
|
- name: source
|
|
1504
1519
|
type: string
|
|
1505
1520
|
nullable: false
|
|
1506
|
-
description: "capability-tree | product-map | capability-spec | structural-model. As of schema 6.0.0, source: structural-model is emitted for a capability only the structural model holds (M-ADR 0230 §4.3
|
|
1521
|
+
description: "capability-tree | product-map | capability-spec | structural-model. As of schema 6.0.0, source: structural-model is emitted for a capability only the structural model holds (M-ADR 0230 §4.3)."
|
|
1507
1522
|
source_of_truth: derived
|
|
1508
1523
|
- name: runway_classification
|
|
1509
1524
|
type: string
|
|
@@ -1637,7 +1652,7 @@ catalogues:
|
|
|
1637
1652
|
- name: source
|
|
1638
1653
|
type: string
|
|
1639
1654
|
nullable: false
|
|
1640
|
-
description: "feature-tree | feature-set | capability-spec | feature-spec | feature-grounding | structural-model. As of schema 6.0.0, source: structural-model is emitted for a feature only the structural model holds (M-ADR 0230 §4.3
|
|
1655
|
+
description: "feature-tree | feature-set | capability-spec | feature-spec | feature-grounding | structural-model. As of schema 6.0.0, source: structural-model is emitted for a feature only the structural model holds (M-ADR 0230 §4.3). NOTE: feature-tree-group source removed in 1.3.0 — groups are now first-class in feature_groups.csv. As of schema 2.2.0, source: feature-tree is emitted by the canonical builder for rows derived from feature-tree.md bullets. As of schema 2.5.0, source: feature-grounding is emitted for rows derived from `docs/domains/<dom>/design/<capability>/feature-grounding.md` markdown tables (per M-ADR 0143). As of schema 2.7.0, source: capability-spec is emitted by the canonical builder for rows derived from `## Feature Grounding (code-path trace)` tables in `docs/domains/<dom>/product/<capability>.md` capability spec markdown (per M-ADR 0145). Feature-grounding and capability-spec rows are both capability-owned: product_id is empty; capability_id resolves from the file path."
|
|
1641
1656
|
source_of_truth: derived
|
|
1642
1657
|
- name: runway
|
|
1643
1658
|
type: string
|
|
@@ -1646,7 +1661,7 @@ catalogues:
|
|
|
1646
1661
|
- name: kind
|
|
1647
1662
|
type: string
|
|
1648
1663
|
nullable: false
|
|
1649
|
-
description: "Bound to feature_kind: feature (the default), obligation (M-ADR 0226, Proposed) or journey (M-ADR 0229 §4.2
|
|
1664
|
+
description: "Bound to feature_kind: feature (the default), obligation (M-ADR 0226, Proposed) or journey (M-ADR 0229 §4.2), from the feature spec's **Kind:** line."
|
|
1650
1665
|
- name: source_sdd_id
|
|
1651
1666
|
type: string
|
|
1652
1667
|
nullable: true
|
|
@@ -1760,7 +1775,7 @@ catalogues:
|
|
|
1760
1775
|
- name: id
|
|
1761
1776
|
type: string
|
|
1762
1777
|
nullable: false
|
|
1763
|
-
description: "{domain}-{stem}-{kind} — the kind component joined the id at schema 3.0.0 (M-ADR 0203 §4.4), ending the kindless collision class where one service published as two contract families (openapi + proto) minted one id (measured cost: 1 row + 2 edges permanently unrepresentable, plus legitimate shared-contract citation flagged as divergence). Mechanical migration: new id = old id + '-' + kind; `sdd-cli catalogue migration-map` emits the pairs, and authored alias targets (M-ADR 0147) re-key automatically during the cut window."
|
|
1778
|
+
description: "{domain}-{stem}-{kind} — the kind component joined the id at schema 3.0.0 (M-ADR 0203 §4.4), ending the kindless collision class where one service published as two contract families (openapi + proto) minted one id (measured cost: 1 row + 2 edges permanently unrepresentable, plus legitimate shared-contract citation flagged as divergence). Mechanical migration: new id = old id + '-' + kind; `sdd-cli catalogue migration-map` emits the pairs, and authored alias targets (M-ADR 0147) re-key automatically during the cut window. From schema 7.0.0 the stem is split at its CamelCase boundaries as an entity name is (`OrderPlaced` → `order-placed`), and references to the unsplit form re-key the same way, from id, kind and spec_path."
|
|
1764
1779
|
- name: name
|
|
1765
1780
|
type: string
|
|
1766
1781
|
nullable: false
|
|
@@ -1946,7 +1961,7 @@ catalogues:
|
|
|
1946
1961
|
- name: service_id
|
|
1947
1962
|
type: string
|
|
1948
1963
|
nullable: true
|
|
1949
|
-
description: Matched by container name
|
|
1964
|
+
description: Matched to a service id by the container's diagram id or display-name slug, then by its display name through docs/platform/reference/service-name-aliases.yaml.
|
|
1950
1965
|
- name: type
|
|
1951
1966
|
type: string
|
|
1952
1967
|
nullable: true
|
|
@@ -2211,7 +2226,7 @@ catalogues:
|
|
|
2211
2226
|
- name: id
|
|
2212
2227
|
type: string
|
|
2213
2228
|
nullable: false
|
|
2214
|
-
description: "Slugged PK: {domain_id}-{entitySlug(name)}.
|
|
2229
|
+
description: "Slugged PK: {domain_id}-{entitySlug(name)}. A glossary-manifest entry with domain=platform-wide is the domain entity its slug names when exactly one does, and adds no row of its own; one naming none, or several, is kept as `_platform-wide-{entitySlug(name)}` with a finding. A superseded manifest entry is no entity: glossary_xref holds its supersedes edge."
|
|
2215
2230
|
- name: name
|
|
2216
2231
|
type: string
|
|
2217
2232
|
nullable: false
|
|
@@ -2255,6 +2270,10 @@ catalogues:
|
|
|
2255
2270
|
nullable: true
|
|
2256
2271
|
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.
|
|
2257
2272
|
source_of_truth: execution
|
|
2273
|
+
- name: ppt_archetype
|
|
2274
|
+
type: string
|
|
2275
|
+
nullable: true
|
|
2276
|
+
description: "The entity's Party-Place-Thing archetype (ppt_archetype). A structure fact: where the SDD has a structural model, the model holds it, and a change states it in its deltas. In an SDD without a model it is read from the capability specs' Entity Classification tables: the first archetype a row's archetype cell names, colour labels and qualifiers aside (Moment alone is moment-interval, PartyRole is role); a cell naming none, an entity no table row resolves to, and two tables disagreeing are reported. Added in schema 7.1.0 (M-ADR 0230)."
|
|
2258
2277
|
description: "Data-model entity (a structural realisation of a concept inside a domain's data model). Distinct from glossary_items (vocabulary-level) and from domain_models (file-level). Added in schema 2.13.0 per M-ADR 0159. Forward source: `models[].entities[]` in glossary-alignment-manifest.yaml. Reverse source: `EntityName { ... }` declarations inside `erDiagram` fences in `docs/domains/{domain}/architecture/data-model.mermaid.md`. PK uses an entity-aware slug that normalises across UPPER_SNAKE and PascalCase authoring styles."
|
|
2259
2278
|
foreign_keys:
|
|
2260
2279
|
- column: domain_id
|
|
@@ -2268,6 +2287,7 @@ catalogues:
|
|
|
2268
2287
|
nullable: true
|
|
2269
2288
|
column_enums:
|
|
2270
2289
|
persistence_kind: enums.persistence_kind
|
|
2290
|
+
ppt_archetype: enums.ppt_archetype
|
|
2271
2291
|
repo_capability:
|
|
2272
2292
|
primary_key: id
|
|
2273
2293
|
source_of_truth: derived
|
|
@@ -3933,7 +3953,7 @@ catalogues:
|
|
|
3933
3953
|
- name: input_paths
|
|
3934
3954
|
type: string
|
|
3935
3955
|
nullable: true
|
|
3936
|
-
description: "';'-separated paths of the review's other assessed inputs (its `inputs:` list, M-ADR 0230 §4.9
|
|
3956
|
+
description: "';'-separated paths of the review's other assessed inputs (its `inputs:` list, M-ADR 0230 §4.9), beside target_path: a feature's UX evidence, say. Each is read at corpus_ref, so a later edit to any of them makes the score stale, as an edit to the target does. Added in schema 6.0.0."
|
|
3937
3957
|
- name: staleness_basis
|
|
3938
3958
|
type: string
|
|
3939
3959
|
nullable: false
|
|
@@ -4752,7 +4772,7 @@ catalogues:
|
|
|
4752
4772
|
nullable: true
|
|
4753
4773
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
4754
4774
|
source_of_truth: execution
|
|
4755
|
-
description: "Edge: evidence about one revision of a requirement, in one repository that implements it (M-ADR 0230 §4.7
|
|
4775
|
+
description: "Edge: evidence about one revision of a requirement, in one repository that implements it (M-ADR 0230 §4.7; replaces requirement_status in schema 6.0.0). Derived, never authored: observed from a baseline's **Observed in:** line, declared-shipped from an archived change's PASS verdict through its work orders' implements lists, each about the revision that change's delta states. An assertion is current while its revision is, and history once a later change moves the requirement, so evidence about one revision is never read as proof of another. An agreed change is not an assertion: it is an intended revision (requirement_revisions) and a change effect (change_effects). Per (revision, repository), the latest assertion wins."
|
|
4756
4776
|
natural_key:
|
|
4757
4777
|
- revision_id
|
|
4758
4778
|
- repo_id
|
|
@@ -4904,7 +4924,7 @@ catalogues:
|
|
|
4904
4924
|
nullable: true
|
|
4905
4925
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
4906
4926
|
source_of_truth: execution
|
|
4907
|
-
description: "Edge: a requirement a user journey cites for one of its steps (M-ADR 0229 §4.4
|
|
4927
|
+
description: "Edge: a requirement a user journey cites for one of its steps (M-ADR 0229 §4.4). Derived, never authored: from the @req tags in the journey's end-to-end test file, or from the citations in its journey feature's living spec. A journey with no row is untraced."
|
|
4908
4928
|
foreign_keys:
|
|
4909
4929
|
- column: journey_id
|
|
4910
4930
|
references: user_journeys.id
|
|
@@ -4955,7 +4975,7 @@ catalogues:
|
|
|
4955
4975
|
nullable: true
|
|
4956
4976
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
4957
4977
|
source_of_truth: execution
|
|
4958
|
-
description: "An attribute of a domain-model entity, an element of its own (M-ADR 0230 §4.3
|
|
4978
|
+
description: "An attribute of a domain-model entity, an element of its own (M-ADR 0230 §4.3): adding a field to an entity adds an attribute and does not modify the entity. Built from the domain model: a Mermaid ER diagram's attribute lines, or a TypeQL entity's owned attributes. Authored in the structural model (openspec/structure/) where an SDD has one. Added in schema 5.1.0; built from domain models since 6.0.0."
|
|
4959
4979
|
natural_key:
|
|
4960
4980
|
- entity_id
|
|
4961
4981
|
- name
|
|
@@ -5012,7 +5032,7 @@ catalogues:
|
|
|
5012
5032
|
nullable: true
|
|
5013
5033
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
5014
5034
|
source_of_truth: execution
|
|
5015
|
-
description: "A named relationship from one domain-model entity to another, an element of its own (M-ADR 0230 §4.3
|
|
5035
|
+
description: "A named relationship from one domain-model entity to another, an element of its own (M-ADR 0230 §4.3). Built from the domain model: a Mermaid ER diagram's relationship lines, or a TypeQL binary relation, with each end's cardinality and role. Authored in the structural model (openspec/structure/) where an SDD has one. Added in schema 5.1.0; built from domain models since 6.0.0, when one cardinality became two, one per end."
|
|
5016
5036
|
natural_key:
|
|
5017
5037
|
- from_entity_id
|
|
5018
5038
|
- to_entity_id
|
|
@@ -5065,7 +5085,7 @@ catalogues:
|
|
|
5065
5085
|
nullable: true
|
|
5066
5086
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
5067
5087
|
source_of_truth: execution
|
|
5068
|
-
description: "A domain invariant, a typed element of the domain model (M-ADR 0230 §4.3
|
|
5088
|
+
description: "A domain invariant, a typed element of the domain model (M-ADR 0230 §4.3): a rule the entity's data always satisfies. It is structure, not intent, wherever it is first written. Built from the domain model's keys: a primary key (PK, or a TypeQL @key) or a unique key (UK) is a unique invariant over its attributes, and a TypeQL role's @card is a cardinality invariant on its relationship. Authored in the structural model (openspec/structure/) where an SDD has one, which can state every kind. Added in schema 5.1.0; built from domain models since 6.0.0."
|
|
5069
5089
|
foreign_keys:
|
|
5070
5090
|
- column: entity_id
|
|
5071
5091
|
references: entities.id
|
|
@@ -5111,7 +5131,7 @@ catalogues:
|
|
|
5111
5131
|
nullable: true
|
|
5112
5132
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
5113
5133
|
source_of_truth: execution
|
|
5114
|
-
description: "One row per distinct text of a requirement (M-ADR 0230 §4.7
|
|
5134
|
+
description: "One row per distinct text of a requirement (M-ADR 0230 §4.7; schema 6.0.0). Derived from the behaviour store: the living spec's text is current; an agreed open change's ADDED or MODIFIED block is intended; an archived change's block the living spec no longer holds is history. A revision is identified by its digest, so the same text is one revision wherever it appears."
|
|
5115
5135
|
foreign_keys:
|
|
5116
5136
|
- column: requirement_id
|
|
5117
5137
|
references: requirements.id
|
|
@@ -5160,7 +5180,7 @@ catalogues:
|
|
|
5160
5180
|
type: string
|
|
5161
5181
|
nullable: true
|
|
5162
5182
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
5163
|
-
description: "One row per element a change writes (M-ADR 0230 §4.7
|
|
5183
|
+
description: "One row per element a change writes (M-ADR 0230 §4.7; schema 6.0.0): a requirement from its behaviour deltas, a structural element from its structural deltas (an archived change's from its effect record, structure/applied.yaml). Base and target are the revisions it moves the element between; a requirement's are revision ids (<requirement>@<12 hex digits of its digest>), catalogued in requirement_revisions except the target of a change not yet agreed; a structural element's are sha256 digests."
|
|
5164
5184
|
natural_key:
|
|
5165
5185
|
- change_id
|
|
5166
5186
|
- element_kind
|
|
@@ -5196,7 +5216,7 @@ catalogues:
|
|
|
5196
5216
|
- name: sunset_date
|
|
5197
5217
|
type: string
|
|
5198
5218
|
nullable: true
|
|
5199
|
-
description: "For a deprecated revision, the date it is retired
|
|
5219
|
+
description: "For a deprecated revision, the date it is retired: the sunset an archived change's agreement record states for the version (contracts[].deprecates.sunset, agreement-record schema 1.3.0). Empty for a version deprecated only in BASELINE.md, whose sunset is not read."
|
|
5200
5220
|
- name: change_id
|
|
5201
5221
|
type: string
|
|
5202
5222
|
nullable: true
|
|
@@ -5212,7 +5232,7 @@ catalogues:
|
|
|
5212
5232
|
nullable: true
|
|
5213
5233
|
description: The platform SDD the row belongs to (stamped at aggregation).
|
|
5214
5234
|
source_of_truth: execution
|
|
5215
|
-
description: A contract version's content revisions, each identified by its content digest (M-ADR 0230 §4.2
|
|
5235
|
+
description: A contract version's content revisions, each identified by its content digest (M-ADR 0230 §4.2; schema 6.0.0). The contract file's current content is active (deprecated where the contract is); a revision an agreed open change pins as the one it agrees (agreement record 1.2.0, contracts[].agrees) is agreed; one an archived change agreed that the file no longer holds is retired. A compatible amendment is a new revision at the same path, so an agreed revision and the active one coexist, both checked. Active is a claim about the published specification, not about deployment.
|
|
5216
5236
|
foreign_keys:
|
|
5217
5237
|
- column: contract_id
|
|
5218
5238
|
references: contracts.id
|
|
@@ -13,26 +13,46 @@
|
|
|
13
13
|
// `sdd-cli catalogue migration-map`; the alias-file re-key (M-ADR 0147 targets
|
|
14
14
|
// are authored copies of tool-minted ids — regenerable, not drift, the same
|
|
15
15
|
// rule as external_system_exemption) consults the in-build form of it.
|
|
16
|
+
/** A contract id's kindless slug (catalogue schema 7.0.0): CamelCase split, lower-cased, non-alphanumerics to `-`. */
|
|
17
|
+
export function contractSlug(s) {
|
|
18
|
+
return legacySlug(s.replace(/([A-Z]{2,})([A-Z][a-z])/g, "$1-$2").replace(/([a-z0-9])([A-Z])/g, "$1-$2"));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A contract id's kindless slug from its domain segment and file stem: only the stem is CamelCase-split, so a
|
|
22
|
+
* domain directory keeps the slug it always had.
|
|
23
|
+
*/
|
|
24
|
+
export function contractKindlessSlug(domain, stem) {
|
|
25
|
+
if (domain === "")
|
|
26
|
+
return contractSlug(stem);
|
|
27
|
+
return [legacySlug(domain), contractSlug(stem)].filter((p) => p !== "").join("-");
|
|
28
|
+
}
|
|
29
|
+
/** The pre-7.0.0 slug: the same, without the CamelCase split. */
|
|
30
|
+
function legacySlug(s) {
|
|
31
|
+
return s
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/^_+/, "") // turn _common into common
|
|
34
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
35
|
+
.replace(/^-+|-+$/g, "");
|
|
36
|
+
}
|
|
37
|
+
function stemOfPath(specPath) {
|
|
38
|
+
const base = specPath.split(/[\\/]/).pop() ?? "";
|
|
39
|
+
return base.replace(/\.[^.]+$/, "");
|
|
40
|
+
}
|
|
16
41
|
/** The 3.0.0 contract id: the kindless slug with its kind appended. */
|
|
17
42
|
export function contractIdWithKind(kindlessSlug, kind) {
|
|
18
43
|
return kindlessSlug === "" ? "" : `${kindlessSlug}-${kind}`;
|
|
19
44
|
}
|
|
20
45
|
/**
|
|
21
|
-
* legacy
|
|
22
|
-
*
|
|
23
|
-
* under
|
|
24
|
-
*
|
|
46
|
+
* legacy id → the current ids it maps to, from built rows: the 2.x kindless id (→ 3.0.0), and the pre-7.0.0
|
|
47
|
+
* unsplit-stem id, with and without its kind. More than one target means the id was a measured collision
|
|
48
|
+
* under the old form (two families sharing one slug) — the consumer decides which family is meant, loudly,
|
|
49
|
+
* never by guess.
|
|
25
50
|
*/
|
|
26
51
|
export function buildLegacyContractIdMap(rows) {
|
|
27
52
|
const map = new Map();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (id === "" || kind === "" || !id.endsWith(`-${kind}`))
|
|
32
|
-
continue;
|
|
33
|
-
const legacy = id.slice(0, -(kind.length + 1));
|
|
34
|
-
if (legacy === "")
|
|
35
|
-
continue;
|
|
53
|
+
const add = (legacy, id) => {
|
|
54
|
+
if (legacy === "" || legacy === id)
|
|
55
|
+
return;
|
|
36
56
|
const ids = map.get(legacy);
|
|
37
57
|
if (ids) {
|
|
38
58
|
if (!ids.includes(id))
|
|
@@ -41,6 +61,23 @@ export function buildLegacyContractIdMap(rows) {
|
|
|
41
61
|
else {
|
|
42
62
|
map.set(legacy, [id]);
|
|
43
63
|
}
|
|
64
|
+
};
|
|
65
|
+
for (const row of rows) {
|
|
66
|
+
const id = String(row["id"] ?? "");
|
|
67
|
+
const kind = String(row["kind"] ?? "");
|
|
68
|
+
if (id === "" || kind === "" || !id.endsWith(`-${kind}`))
|
|
69
|
+
continue;
|
|
70
|
+
const kindless = id.slice(0, -(kind.length + 1));
|
|
71
|
+
add(kindless, id);
|
|
72
|
+
// 7.0.0: the stem's segment as it read before the CamelCase split.
|
|
73
|
+
const stem = stemOfPath(String(row["spec_path"] ?? ""));
|
|
74
|
+
const split = contractSlug(stem);
|
|
75
|
+
const unsplit = legacySlug(stem);
|
|
76
|
+
if (split === "" || split === unsplit || !kindless.endsWith(split))
|
|
77
|
+
continue;
|
|
78
|
+
const oldKindless = kindless.slice(0, -split.length) + unsplit;
|
|
79
|
+
add(oldKindless, id);
|
|
80
|
+
add(`${oldKindless}-${kind}`, id);
|
|
44
81
|
}
|
|
45
82
|
for (const ids of map.values())
|
|
46
83
|
ids.sort((a, b) => a.localeCompare(b));
|
|
@@ -218,7 +218,7 @@ const ManifestSchema = z.object({
|
|
|
218
218
|
// Shared-library trait (sdd-manifest-schema 1.12.0, M-ADR 0217 §4.4). Profile-agnostic,
|
|
219
219
|
// like `exemplar`; absent is the unmarked norm, no finding.
|
|
220
220
|
shared_library: z.boolean().optional(),
|
|
221
|
-
// Where this estate's journey behaviour is mastered (sdd-manifest-schema 1.13.0, M-ADR 0229 §4.2
|
|
221
|
+
// Where this estate's journey behaviour is mastered (sdd-manifest-schema 1.13.0, M-ADR 0229 §4.2).
|
|
222
222
|
// `master` is declared at the root, and journey-impact treats a member naming its platform as inheriting it.
|
|
223
223
|
// `test_paths` is never inherited: it is relative to this SDD, so an SDD that holds end-to-end files declares
|
|
224
224
|
// its own block, with the master and its paths. Unknown values fail at load.
|