@sdd-method/maintain-cli 0.13.1 → 0.15.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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/catalogue/aggregate.js +50 -13
  3. package/dist/lib/catalogue/build.js +72 -23
  4. package/dist/lib/catalogue/builders/adr-governs.js +1 -1
  5. package/dist/lib/catalogue/builders/adrs.js +2 -2
  6. package/dist/lib/catalogue/builders/c4-containers.js +6 -2
  7. package/dist/lib/catalogue/builders/contract-revisions.js +45 -7
  8. package/dist/lib/catalogue/builders/contracts.js +4 -11
  9. package/dist/lib/catalogue/builders/data-model.js +1 -1
  10. package/dist/lib/catalogue/builders/entities-manifest.js +10 -4
  11. package/dist/lib/catalogue/builders/entity-archetype.js +121 -0
  12. package/dist/lib/catalogue/builders/entity-merge.js +90 -0
  13. package/dist/lib/catalogue/builders/features.js +1 -1
  14. package/dist/lib/catalogue/builders/glossary-xref.js +11 -5
  15. package/dist/lib/catalogue/builders/journey-requirement.js +5 -5
  16. package/dist/lib/catalogue/builders/quality-scores.js +1 -1
  17. package/dist/lib/catalogue/builders/requirements.js +1 -1
  18. package/dist/lib/catalogue/builders/service-contract-consumes.js +2 -2
  19. package/dist/lib/catalogue/builders/service-entity-tables.js +1 -1
  20. package/dist/lib/catalogue/canonical-schema.yaml +50 -30
  21. package/dist/lib/catalogue/contract-id.js +49 -12
  22. package/dist/lib/catalogue/manifest-loader.js +1 -1
  23. package/dist/lib/catalogue/retired-catalogues.js +1 -1
  24. package/dist/lib/catalogue/rubric-coverage-map.yaml +7 -3
  25. package/dist/lib/catalogue/schema-compat.js +1 -1
  26. package/dist/lib/catalogue/staleness-verdicts.js +3 -3
  27. package/dist/lib/structure/catalogue.js +31 -10
  28. package/dist/lib/structure/compose.js +1 -1
  29. package/dist/lib/structure/effects.js +1 -1
  30. package/dist/lib/structure/intended.js +194 -0
  31. package/dist/lib/structure/model.js +1 -1
  32. package/dist/lib/structure/render.js +1 -1
  33. package/dist/lib/structure/view.js +8 -3
  34. package/dist/lib/validate/agreement-evidence.js +2 -2
  35. package/dist/lib/validate/behaviour-store.js +49 -12
  36. package/dist/lib/validate/dispatch.js +2 -2
  37. package/dist/lib/validate/journey-impact.js +7 -7
  38. package/dist/lib/validate/structural-model.js +48 -35
  39. package/dist/maintain-program.js +7 -1
  40. package/dist/maintain-version.json +2 -2
  41. package/dist/verbs/structure.js +95 -0
  42. package/dist/verify-version.json +2 -2
  43. package/package.json +2 -2
@@ -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: "6.0.0"
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, Proposed).
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; Proposed).
689
- description: What a catalogued feature is (M-ADR 0226 and M-ADR 0229, both Proposed).
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, Proposed).
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, Proposed). Each catalogue carries one; a column carries one only where it differs.
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, Proposed): 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."
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, Proposed).
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, Proposed).
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, Proposed).
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, Proposed).
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, Proposed).
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, Proposed): the change whose archive makes the decision apply, by its undated name; empty when it applies on acceptance. Added in schema 6.0.0."
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, Proposed)."
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, Proposed). 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."
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, Proposed), from the feature spec's **Kind:** line."
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 ↔ service name/repo where possible.
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)}. For manifest-sourced rows with domain=platform-wide, the leading domain segment is empty (`-{entitySlug(name)}`) and the row carries a [finding] convention."
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, Proposed), 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."
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, Proposed; 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."
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, Proposed). 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."
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, Proposed): 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."
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, Proposed). 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."
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, Proposed): 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."
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, Proposed; 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."
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, Proposed; 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."
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. Not yet populated: the builder does not yet read a version's sunset date (BASELINE.md's optional sunset_date)."
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, Proposed; 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.
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 (2.x, kindless) id → the 3.0.0 kind-suffixed ids it maps to, from
22
- * built rows. More than one target means the id was a measured collision
23
- * under 2.x (two families sharing one kindless slug) — the consumer decides
24
- * which family is meant, loudly, never by guess.
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
- for (const row of rows) {
29
- const id = String(row["id"] ?? "");
30
- const kind = String(row["kind"] ?? "");
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, Proposed).
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.
@@ -7,7 +7,7 @@ import { rm, stat } from "node:fs/promises";
7
7
  import { join } from "node:path";
8
8
  /** name -> why it went, and what replaced it. */
9
9
  export const RETIRED_CATALOGUES = new Map([
10
- ["requirement_status", "retired in catalogue schema 6.0.0: evidence about one revision of a requirement is requirement_assertions (M-ADR 0230 §4.7, Proposed)"],
10
+ ["requirement_status", "retired in catalogue schema 6.0.0: evidence about one revision of a requirement is requirement_assertions (M-ADR 0230 §4.7)"],
11
11
  ]);
12
12
  /** Remove each retired catalogue's file from `dir`. Returns the file names removed. */
13
13
  export async function removeRetiredCatalogues(dir, findings, label) {
@@ -954,7 +954,7 @@ coverage:
954
954
  entity_attributes:
955
955
  kind: entity
956
956
  scoring: scored-at-source
957
- instrument: an element of the structural model (M-ADR 0230, Proposed); scored with its domain model by rubrics/domain-architecture-rubric.md
957
+ instrument: an element of the structural model (M-ADR 0230); scored with its domain model by rubrics/domain-architecture-rubric.md
958
958
  relationships:
959
959
  entity_id:
960
960
  class: membership
@@ -962,7 +962,7 @@ coverage:
962
962
  entity_relationships:
963
963
  kind: entity
964
964
  scoring: scored-at-source
965
- instrument: an element of the structural model (M-ADR 0230, Proposed); scored with its domain model by rubrics/domain-architecture-rubric.md
965
+ instrument: an element of the structural model (M-ADR 0230); scored with its domain model by rubrics/domain-architecture-rubric.md
966
966
  relationships:
967
967
  from_entity_id:
968
968
  class: membership
@@ -973,7 +973,7 @@ coverage:
973
973
  domain_invariants:
974
974
  kind: entity
975
975
  scoring: scored-at-source
976
- instrument: an element of the structural model (M-ADR 0230, Proposed); scored with its domain model by rubrics/domain-architecture-rubric.md
976
+ instrument: an element of the structural model (M-ADR 0230); scored with its domain model by rubrics/domain-architecture-rubric.md
977
977
  relationships:
978
978
  entity_id:
979
979
  class: membership
@@ -1039,3 +1039,7 @@ coverage:
1039
1039
  kind: enum
1040
1040
  scoring: not-applicable
1041
1041
  reason: closed vocabulary, not an artefact
1042
+ ppt_archetype:
1043
+ kind: enum
1044
+ scoring: not-applicable
1045
+ reason: closed vocabulary, not an artefact
@@ -27,7 +27,7 @@
27
27
  * M-ADR 0134 §4.6 (canonical builder + aggregator).
28
28
  */
29
29
  export const SCHEMA_COMPAT = {
30
- catalogue_schema: "^6.0.0",
30
+ catalogue_schema: "^7.0.0",
31
31
  sdd_manifest: "^1.0.0",
32
32
  aggregation_manifest: "^1.0.0",
33
33
  };
@@ -40,7 +40,7 @@
40
40
  // none — already reported by scores-coverage as unknown currency; not
41
41
  // re-reported here.
42
42
  //
43
- // ASSESSED INPUTS (M-ADR 0230 §4.9, Proposed): a review that names its other inputs (`input_paths`) is read with
43
+ // ASSESSED INPUTS (M-ADR 0230 §4.9): a review that names its other inputs (`input_paths`) is read with
44
44
  // each of them, once its target is current. An input that moved stales the score, with the input named. So does an
45
45
  // input gone from HEAD, and one the corpus did not hold at the anchor: unlike a missing target, the score can still
46
46
  // be re-reviewed against its target, and a review cannot have read an input that was not there, so both prescribe a
@@ -171,7 +171,7 @@ export async function runStalenessVerdicts(session, options) {
171
171
  };
172
172
  }
173
173
  const asStr = (v) => v === null || v === undefined ? "" : String(v).trim();
174
- // input_paths (schema 6.0.0, M-ADR 0230 §4.9, Proposed): a catalogue built before it has no such column, and its
174
+ // input_paths (schema 6.0.0, M-ADR 0230 §4.9): a catalogue built before it has no such column, and its
175
175
  // scores are read as naming no other input.
176
176
  const query = (inputs) => `SELECT rubric_id, target_ref, target_path, corpus_ref, scored_at, staleness_basis, ${inputs}
177
177
  FROM "quality_scores"
@@ -191,7 +191,7 @@ export async function runStalenessVerdicts(session, options) {
191
191
  const rubricId = asStr(rubricIdRaw);
192
192
  const targetRef = asStr(targetRefRaw);
193
193
  const basis = asStr(basisRaw);
194
- // The review's other assessed inputs (M-ADR 0230 §4.9, Proposed): read with the target, so an edit to any of
194
+ // The review's other assessed inputs (M-ADR 0230 §4.9): read with the target, so an edit to any of
195
195
  // them makes the score stale as an edit to the target does.
196
196
  const inputs = asStr(inputPathsRaw).split(";").map((x) => x.trim()).filter((x) => x !== "");
197
197
  // Counted for BOTH bases, before the branch. The first cut of this counter
@@ -1,13 +1,15 @@
1
- // The catalogue from the structural model, and the model a built catalogue implies (M-ADR 0230 §4.3, §5.3,
2
- // Proposed). Where an SDD has a model, it is the record of structure, so every structure catalogue is built from it whole:
1
+ // The catalogue from the structural model, and the model a built catalogue implies (M-ADR 0230 §4.3, §5.3).
2
+ // Where an SDD has a model, it is the record of structure, so every structure catalogue is built from it whole:
3
3
  // a row per element of the current view, with the model's structure columns;
4
4
  // its other columns (every class but structure: derived, declaration, evidence, intent, execution) from the
5
5
  // source-built row with the same id;
6
6
  // for an element only the model holds, the derived columns the model itself settles (MODEL_DERIVED).
7
7
  // What the sources hold and the model does not, and a structure column where they disagree, is reported once per
8
- // catalogue. The model wins: either finding means the sources moved outside a change. Pure: no file is read or written.
8
+ // catalogue. The model wins: either finding means the sources moved outside a change. A source row that states an
9
+ // element as an agreed open change adds or modifies it is that change's intended element, not a disagreement: it is
10
+ // listed apart, and catalogued once the change archives. Pure: no file is read or written.
9
11
  import { stringify } from "yaml";
10
- import { FORMAT_VERSION } from "./model.js";
12
+ import { elementKey, FORMAT_VERSION } from "./model.js";
11
13
  import { STRUCTURE } from "./view.js";
12
14
  /** The view each generated file renders, for a derived path column of an element only the model holds. */
13
15
  export const VIEW_PATHS = {
@@ -44,8 +46,10 @@ function sample(ids) {
44
46
  /**
45
47
  * Replace every structure catalogue's rows with the view's. Rows of other catalogues are returned as they are. Source
46
48
  * rows sharing an id are merged first, later values filling earlier gaps, as the keyed-table dedupe would.
49
+ * `intended` holds the elements agreed open changes add or modify (intendedWrites); a source row agreeing with one is
50
+ * pushed to `intendedOut` as `<key> (<change>)` instead of being reported.
47
51
  */
48
- export function overlayModel(schema, view, rows, findings) {
52
+ export function overlayModel(schema, view, rows, findings, intended = new Map(), intendedOut = []) {
49
53
  const out = new Map(rows);
50
54
  for (const cat of schema.catalogues) {
51
55
  if (cat.sourceOfTruth !== STRUCTURE)
@@ -66,6 +70,20 @@ export function overlayModel(schema, view, rows, findings) {
66
70
  const elements = view.get(cat.name) ?? new Map();
67
71
  const built = [];
68
72
  const differ = [];
73
+ const disagree = (from, e) => structural.filter((c) => cell(from[c]) !== "" && cell(from[c]) !== cell(e.columns[c]));
74
+ // The source row states the element as an agreed open change does; one stating it otherwise is reported as such.
75
+ const differIntended = [];
76
+ const asIntended = (id, from) => {
77
+ const w = intended.get(elementKey(cat.name, id));
78
+ if (w === undefined)
79
+ return false;
80
+ const cols = disagree(from, w.element);
81
+ if (cols.length > 0)
82
+ differIntended.push(`${id} (${cols.join(", ")}; ${w.change})`);
83
+ else
84
+ intendedOut.push(`${elementKey(cat.name, id)} (${w.change})`);
85
+ return true;
86
+ };
69
87
  for (const e of elements.values()) {
70
88
  const from = source.get(e.id);
71
89
  const row = {};
@@ -73,8 +91,8 @@ export function overlayModel(schema, view, rows, findings) {
73
91
  for (const [k, v] of Object.entries(from))
74
92
  if (!structural.includes(k))
75
93
  row[k] = v;
76
- const cols = structural.filter((c) => cell(from[c]) !== "" && cell(from[c]) !== cell(e.columns[c]));
77
- if (cols.length > 0)
94
+ const cols = disagree(from, e);
95
+ if (cols.length > 0 && !asIntended(e.id, from))
78
96
  differ.push(`${e.id} (${cols.join(", ")})`);
79
97
  }
80
98
  else {
@@ -85,12 +103,15 @@ export function overlayModel(schema, view, rows, findings) {
85
103
  row[c] = e.columns[c];
86
104
  built.push(row);
87
105
  }
88
- const missing = [...source.keys()].filter((id) => id !== "" && !elements.has(id)).sort();
106
+ const missing = [...source.keys()].filter((id) => id !== "" && !elements.has(id) && !asIntended(id, source.get(id))).sort();
89
107
  if (missing.length > 0) {
90
- findings.add("divergence", `${cat.name}: the sources hold ${missing.length} row(s) the structural model does not (${sample(missing)}); the model is the record of structure, so they are not catalogued. Add them through a change's structural deltas, or remove them from the sources (M-ADR 0230 §4.3, Proposed)`, "warn", `catalogue:${cat.name}`);
108
+ findings.add("divergence", `${cat.name}: the sources hold ${missing.length} row(s) the structural model does not (${sample(missing)}); the model is the record of structure, so they are not catalogued. Add them through a change's structural deltas, or remove them from the sources (M-ADR 0230 §4.3)`, "warn", `catalogue:${cat.name}`);
109
+ }
110
+ if (differIntended.length > 0) {
111
+ findings.add("divergence", `${cat.name}: ${differIntended.length} source row(s) state an element an agreed open change adds or modifies, and disagree with it: ${sample(differIntended.sort())}. The change's delta is what archives; bring the source into line with it, or the delta with the source (M-ADR 0230 §4.3, §4.7)`, "warn", `catalogue:${cat.name}`);
91
112
  }
92
113
  if (differ.length > 0) {
93
- findings.add("divergence", `${cat.name}: ${differ.length} element(s) where the sources and the structural model disagree, catalogued as the model states them: ${sample(differ.sort())}. A source that is a generated view is rendered again; any other is brought into line through a change (M-ADR 0230 §4.3, Proposed)`, "warn", `catalogue:${cat.name}`);
114
+ findings.add("divergence", `${cat.name}: ${differ.length} element(s) where the sources and the structural model disagree, catalogued as the model states them: ${sample(differ.sort())}. A source that is a generated view is rendered again; any other is brought into line through a change (M-ADR 0230 §4.3)`, "warn", `catalogue:${cat.name}`);
94
115
  }
95
116
  out.set(cat.name, built);
96
117
  }
@@ -1,4 +1,4 @@
1
- // Views over the structural model, and how changes compose (M-ADR 0230 §4.5, §4.7, Proposed).
1
+ // Views over the structural model, and how changes compose (M-ADR 0230 §4.5, §4.7).
2
2
  // current the model as published
3
3
  // at a change current with that change's deltas and those of its dependency closure, dependencies first
4
4
  // intended current with every agreed change's deltas
@@ -1,4 +1,4 @@
1
- // A change's structural effect, and the effect record an archive leaves (M-ADR 0230 §4.6, §4.7, Proposed). Pure reads,
1
+ // A change's structural effect, and the effect record an archive leaves (M-ADR 0230 §4.6, §4.7). Pure reads,
2
2
  // so the catalogue builder and the verify-only CLI can carry them; writing the record is apply.ts's.
3
3
  import { parseDocument } from "yaml";
4
4
  import { elementKey, revision } from "./model.js";