@sdd-method/verify-cli 0.1.1 → 0.2.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.
@@ -63,6 +63,16 @@ export async function runBaselineCheck(options, write) {
63
63
  write("[error] baseline-check: no findings source (--findings <path>, or pipe them in)\n");
64
64
  return { exitCode: 2 };
65
65
  }
66
+ // An input with no lines at all is not "zero findings": every real findings
67
+ // stream (build, coverage, validate output) carries at least its own metric
68
+ // and verdict lines. The default source is stdin, so an empty pipe arrives
69
+ // here as nothing — refused, never compared (empty-set sweep, R2).
70
+ if (findingsText.trim() === "") {
71
+ write(`[error] baseline-check: the findings stream is EMPTY — no lines of any kind. ` +
72
+ `This is not zero findings; it is no measurement (an empty pipe, or the wrong ` +
73
+ `file). Pass --findings <path> to the captured stream\n`);
74
+ return { exitCode: 2 };
75
+ }
66
76
  let baselineText;
67
77
  try {
68
78
  baselineText = await readFile(baselinePath, "utf-8");
@@ -102,7 +112,8 @@ export async function runBaselineCheck(options, write) {
102
112
  // The condition is deliberately narrow: live gate-acting findings ZERO, baseline
103
113
  // non-empty, and EVERY entry closed. That triple cannot arise from ordinary
104
114
  // remediation without also being worth a human look.
105
- if (live.lines.length === 0 && base.lines.length > 0 && cmp.stale.length === base.lines.length) {
115
+ const allClosed = live.lines.length === 0 && base.lines.length > 0 && cmp.stale.length === base.lines.length;
116
+ if (allClosed) {
106
117
  write(`[finding] divergence: baseline_check compared ZERO live gate-acting findings ` +
107
118
  `against ${base.lines.length} baseline entr(ies) and reported ALL of them closed. ` +
108
119
  `This is reported as a DEFECT rather than a pass: an all-closed baseline is far ` +
@@ -131,6 +142,10 @@ export async function runBaselineCheck(options, write) {
131
142
  // Only genuinely unbaselined findings fail. A reworded finding is the case
132
143
  // this whole mechanism exists to stop failing, and a closed one is good news
133
144
  // that a re-take tidies — neither is a regression.
134
- return { exitCode: cmp.unbaselined.length > 0 ? 1 : 0 };
145
+ // The all-closed guard FAILS the gate (empty-set sweep, R2, ruled 2026-09-29):
146
+ // it used to print "a DEFECT rather than a pass" and exit 0, so the regression
147
+ // gate still passed on exactly the empty/wrong stream it was written to catch.
148
+ // A genuinely all-fixed estate re-takes its baseline once, deliberately.
149
+ return { exitCode: cmp.unbaselined.length > 0 || allClosed ? 1 : 0 };
135
150
  }
136
151
  //# sourceMappingURL=baseline-check.js.map
@@ -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: "3.13.0"
10
+ schema_version: "3.18.0"
11
11
  schema_owner: "sdd-method"
12
12
  schema_status: "generated"
13
13
  enums:
@@ -129,8 +129,12 @@ enums:
129
129
  description: Docs-only repo.
130
130
  runtime: false
131
131
  emits_service_row: false
132
+ - value: firmware
133
+ description: "Firmware that runs on a device: the application image, bootloader, loader, on-device plug-ins (M-ADR 0215). Runtime, but emits no service row — a device is not a node in the platform's service graph. Its tooling, test benches and libraries keep their own kinds."
134
+ runtime: true
135
+ emits_service_row: false
132
136
  description: |
133
- Type of repository, declared in `orchestration/repo-config/*.yaml` as `service_type` and surfaced in `repositories.component_type` and `services.component_type`. The canonical builder uses this to decide which repos are runtime services (api_service, worker, gateway, frontend, mobile) and which are non-runtime (library, infrastructure, documentation). Whether a repo is a vertical-domain application is conveyed by its SDD manifest's `profile: application-product`, not by a service_type tag — this enum is strictly about runtime artefact shape (per resolution of adopt-canonical-catalogue-builder.md §7.1).
137
+ Type of repository, declared in `orchestration/repo-config/*.yaml` as `service_type` and surfaced in `repositories.component_type` and `services.component_type`. The canonical builder uses this to decide which repos are runtime services (api_service, worker, gateway, frontend, mobile) and which are non-runtime (library, infrastructure, documentation); firmware runs, but on a device, so it is runtime without a service row (M-ADR 0215) — service emission follows emits_service_row, not runtime. Whether a repo is a vertical-domain application is conveyed by its SDD manifest's `profile: application-product`, not by a service_type tag — this enum is strictly about runtime artefact shape (per resolution of adopt-canonical-catalogue-builder.md §7.1).
134
138
  capability_exemption_reason:
135
139
  values:
136
140
  - value: infrastructure
@@ -246,7 +250,7 @@ enums:
246
250
  description: A persona the journey passes to or from.
247
251
  - value: supporting
248
252
  description: A persona present in the journey without owning it.
249
- description: "The kind of participation a persona has in a user journey, in `persona_journey.role` — the ADR 0210 worked example's `participation`. Bound at 3.9.0 (M-ADR 0210 §4.5): parsed from the persona spec's declared `Roles:` line, a method template surface; every row at both estates was in-vocabulary when bound."
253
+ description: "The kind of participation a persona has in a user journey, in `persona_journey.role` — the M-ADR 0210 worked example's `participation`. Bound at 3.9.0 (M-ADR 0210 §4.5): parsed from the persona spec's declared `Roles:` line, a method template surface; every row at both estates was in-vocabulary when bound."
250
254
  model_notation:
251
255
  values:
252
256
  - value: mermaid-erd
@@ -474,20 +478,134 @@ enums:
474
478
  - value: code-vs-contract
475
479
  - value: binding-vs-reality
476
480
  description: M-ADR 0194 conformance finding target. Bound at 3.13.0 from the column descriptions; measured against both reference estates before binding (M-ADR 0205 §4.4).
477
- conformance_role:
481
+ implementation_role:
478
482
  values:
479
483
  - value: as-is
484
+ description: The existing implementation.
480
485
  - value: to-be
481
- description: M-ADR 0194 conformance finding role. Bound at 3.13.0 from the column descriptions; measured against both reference estates before binding (M-ADR 0205 §4.4).
482
- conformance_lifecycle:
486
+ description: The target implementation the spec is migrating toward.
487
+ description: An implementation member's role against its spec (M-ADR 0181 §2.1). Bound at 3.13.0 on conformance_finding as conformance_role; renamed at 3.15.0, when implementation_members became its owner (M-ADR 0214 §4.2) — the values did not change.
488
+ implementation_lifecycle:
483
489
  values:
484
490
  - value: active
491
+ description: In service and the current implementation.
485
492
  - value: planned
493
+ description: "Not yet built: the repository may not exist yet."
486
494
  - value: deprecated
495
+ description: "Still running, scheduled for retirement (M-ADR 0214 §4.7: what some bindings called sunset)."
487
496
  - value: migrating
497
+ description: Being moved onto, or off, while both sides run.
488
498
  - value: legacy-active
499
+ description: The as-is implementation, still serving while a to-be replaces it.
489
500
  - value: decommissioned
490
- description: M-ADR 0194 conformance finding lifecycle. Bound at 3.13.0 from the column descriptions; measured against both reference estates before binding (M-ADR 0205 §4.4).
501
+ description: "Retired: no longer running."
502
+ description: An implementation member's lifecycle (M-ADR 0181 §2.1). Bound at 3.13.0 on conformance_finding as conformance_lifecycle; renamed at 3.15.0, when implementation_members became its owner (M-ADR 0214 §4.2) — the values did not change.
503
+ repo_scope:
504
+ values:
505
+ - value: component
506
+ description: "Part of what this SDD specifies: the SDD's artefact stack applies, and its capabilities or other services depend on it."
507
+ - value: support
508
+ description: "Supports a component without being one: its operational configuration, tooling, tests, collections, or a library it consumes."
509
+ description: Whether a mirrored repository is inside the scope of what its SDD specifies (M-ADR 0213). Deliberately neither a profile (profile is the SDD's) nor a role (role is M-ADR 0181's as-is | to-be); a repository's kind is component_type.
510
+ evidence_direction:
511
+ values:
512
+ - value: forward-authored
513
+ description: The spec came first and the code was built from it.
514
+ - value: seeding-reverse
515
+ description: A spec that never existed, recovered from pre-existing internal code.
516
+ - value: external-doc-reverse
517
+ description: The external surface a product consumes, grounded in a third party's published API or documentation.
518
+ - value: conformance-reverse
519
+ description: Own forward or maintained code, verified against its spec, with legitimate evolution fed back.
520
+ description: How an implementation member is evidenced (M-ADR 0180 §2.1; forward-authored added by M-ADR 0181 §2.1).
521
+ member_presence:
522
+ values:
523
+ - value: observed
524
+ description: Exported and observed in this baseline.
525
+ - value: carried
526
+ description: "Not re-exported: carried from an earlier baseline (carried_from), with its reason."
527
+ - value: removed
528
+ description: In the prior baseline and not in this one (members_removed), with its reason.
529
+ description: A snapshot member's presence in its baseline — the three states the snapshot conventions record (M-ADR 0214 §4.4).
530
+ conformance_disposition:
531
+ values:
532
+ - value: conformant
533
+ description: Code matches spec.
534
+ - value: conformant-open
535
+ description: "Both sides agree the item is deferred or absent: an open decision, not drift."
536
+ - value: code-defect
537
+ description: "The spec is authority: fix the code."
538
+ - value: spec-stale
539
+ description: "The code legitimately evolved: update the spec."
540
+ - value: divergence
541
+ description: "A genuine design question: triage, don't fail."
542
+ description: The triage outcome of a conformance-reverse delta (M-ADR 0180 §2.3). Bound so every file that records an outcome records one of these; the triage records are not yet catalogued (M-ADR 0214 §4.8).
543
+ delta_disposition:
544
+ values:
545
+ - value: capability-added
546
+ description: The code gained a capability the spec does not have.
547
+ - value: capability-removed
548
+ description: The code lost a capability the spec still has.
549
+ - value: spec-update
550
+ description: "The code legitimately evolved within a capability: the spec follows."
551
+ - value: refactor-only-no-spec-change
552
+ description: "The code changed and its behaviour did not: no spec change."
553
+ - value: ambiguous
554
+ description: "Cannot be classified from the delta alone: triage."
555
+ description: The classification of a delta-reverse finding between two snapshots (M-ADR 0180 §2.7). Bound so every file that records one records one of these; delta findings are not yet catalogued (M-ADR 0214 §4.8).
556
+ supply_verdict:
557
+ values:
558
+ - value: supported
559
+ description: The item reaches the canonical boundary from this integration.
560
+ - value: partial
561
+ description: It reaches it with a stated limitation (rate, scope, accuracy).
562
+ - value: source-absent
563
+ description: "The provider cannot supply it: nobody's work."
564
+ - value: gateway-gap
565
+ description: "The provider can and this integration does not: the integration's work."
566
+ - value: not-applicable
567
+ description: The item does not apply to this integration.
568
+ description: "What an integration states about a data item (M-ADR 0216 §4.2). unknown is NOT a member: it is the reading of no row."
569
+ supply_basis:
570
+ values:
571
+ - value: code
572
+ description: Read from the integration's code.
573
+ - value: grounded
574
+ description: Grounded in a vendor or source artefact.
575
+ - value: declared
576
+ description: Declared by the integration's own configuration.
577
+ - value: inferred
578
+ description: Inferred, with no artefact stating it.
579
+ description: The evidence class of a supply statement.
580
+ data_necessity:
581
+ values:
582
+ - value: required
583
+ description: The feature cannot work without it (or without one member of its alternative_group).
584
+ - value: enhances
585
+ description: The feature works without it, less well. Never decides coverage.
586
+ description: How a feature needs a data item.
587
+ support_verdict:
588
+ values:
589
+ - value: supported
590
+ description: Every requirement is met.
591
+ - value: partial
592
+ description: Every requirement is met or partly met, and one only partly.
593
+ - value: unknown
594
+ description: No requirement is unmet, and one is undecided because no supply row exists.
595
+ - value: unsupported
596
+ description: A requirement is unmet; limiting_item_id and limiting_reason name it.
597
+ - value: not-applicable
598
+ description: Every required item is not-applicable to this integration.
599
+ description: Whether one integration supports one feature at the canonical boundary (M-ADR 0216 §4.4).
600
+ limiting_reason:
601
+ values:
602
+ - value: source-absent
603
+ description: "The provider cannot supply the item: nobody's work."
604
+ - value: gateway-gap
605
+ description: "The integration does not supply what the provider has: the integration's work."
606
+ - value: no-canonical-home
607
+ description: "The platform has no canonical field for the item: platform contract work."
608
+ description: Why an unsupported feature is unsupported, and so whose work the gap is.
491
609
  catalogues:
492
610
  domains:
493
611
  primary_key: id
@@ -875,7 +993,7 @@ catalogues:
875
993
  - name: domain_id
876
994
  type: string
877
995
  nullable: true
878
- description: "Domain slug for a domain-scoped ADR (docs/domains/<slug>/…); 'platform' for a platform-scoped ADR (docs/platform/adr/), whose owner has that domain by construction; EMPTY for a method-scoped ADR (docs/method/adr/, docs/method/platform-adr/) — method ADRs are cross-cutting governance and belong to no domain. Nullable since 3.10.0 (M-ADR 0136 §4.3 amendment): before it, method ADRs were pinned to 'platform' and dangled in every platform-root without a domain of that name."
996
+ description: "Domain slug for a domain-scoped ADR (docs/domains/<slug>/…); 'platform' for a platform-scoped ADR (docs/platform/adr/), whose owner has that domain by construction; EMPTY for a method-scoped ADR (docs/method/adr/, docs/method/platform-adr/) — method ADRs are cross-cutting governance and belong to no domain. Nullable since 3.10.0 (M-ADR 0136 §4.3 amendment): before it, method ADRs were pinned to 'platform' and dangled in every platform-root without a domain of that name. External for application- and integration-product SDDs since 3.17.0, as capabilities.domain_id is: an app or integration SDD files its own domain-scoped decisions under a platform-side domain (e.g. integrations) it does not declare, so its build logs an external finding naming the value, and the owning platform's aggregate validates it — and fails there if that platform declares no such domain."
879
997
  - name: decision_date
880
998
  type: date
881
999
  nullable: true
@@ -905,6 +1023,9 @@ catalogues:
905
1023
  - column: domain_id
906
1024
  references: domains.id
907
1025
  nullable: true
1026
+ external_for_profiles:
1027
+ - application-product
1028
+ - integration-product
908
1029
  - column: superseded_by_id
909
1030
  references: adrs.id
910
1031
  nullable: true
@@ -1670,6 +1791,7 @@ catalogues:
1670
1791
  - name: profile
1671
1792
  type: string
1672
1793
  nullable: false
1794
+ description: "DEPRECATED (removal at 4.0.0): not an SDD profile. The per-repo field reused sdd_profile's values to record only whether a mirrored repository is one of the SDD's own components or a supporting one — see repo_scope (M-ADR 0213)."
1673
1795
  - name: group
1674
1796
  type: string
1675
1797
  nullable: true
@@ -1713,6 +1835,10 @@ catalogues:
1713
1835
  type: string
1714
1836
  nullable: true
1715
1837
  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.
1838
+ - name: repo_scope
1839
+ type: string
1840
+ nullable: true
1841
+ description: "Whether the repository is inside the scope of what THIS SDD specifies, from its reverse-metadata.yaml (M-ADR 0213 §4.2): component (part of what the SDD specifies) or support (supports a component without being one). Empty for a repository with no reverse metadata. Its kind is component_type; the SDD's profile is products/applications.profile."
1716
1842
  foreign_keys:
1717
1843
  - column: domain_id
1718
1844
  references: domains.id
@@ -1722,6 +1848,7 @@ catalogues:
1722
1848
  nullable: true
1723
1849
  column_enums:
1724
1850
  component_type: enums.service_type
1851
+ repo_scope: enums.repo_scope
1725
1852
  domain_models:
1726
1853
  primary_key: id
1727
1854
  columns:
@@ -3279,8 +3406,8 @@ catalogues:
3279
3406
  description: "Conformance-reverse drift summary, one row per check (method M-ADR 0180). NOT produced by `catalogue build` (it reads the spec repo) — produced by `sdd-cli conformance --emit-finding` (which has the code repo) and federated here by the aggregator. Each row is a run-dated snapshot of declared-vs-observed drift for an implementation member (M-ADR 0181 role/lifecycle), so estate-wide conformance is queryable (aggregate union) and trendable (snapshot diff across runs/commits). Verdicts are PROVISIONAL — disposition (code-defect | spec-stale | divergence) is triage, recorded elsewhere. No enforced FK on implementation_repo: the code repo is frequently cross-repo and not in this SDD's repositories.csv. Added in schema 2.25.0."
3280
3407
  column_enums:
3281
3408
  target: enums.conformance_target
3282
- role: enums.conformance_role
3283
- lifecycle: enums.conformance_lifecycle
3409
+ role: enums.implementation_role
3410
+ lifecycle: enums.implementation_lifecycle
3284
3411
  quality_scores:
3285
3412
  primary_key: id
3286
3413
  columns:
@@ -3555,3 +3682,342 @@ catalogues:
3555
3682
  column_enums:
3556
3683
  item_kind: enums.scaffold_item_kind
3557
3684
  state: enums.scaffold_item_state
3685
+ implementations:
3686
+ primary_key: id
3687
+ columns:
3688
+ - name: id
3689
+ type: string
3690
+ nullable: false
3691
+ description: The binding's implementation key — the same value every snapshot manifest of this implementation carries as implementation.
3692
+ - name: name
3693
+ type: string
3694
+ nullable: false
3695
+ description: The binding's name key, else the id.
3696
+ - name: spec
3697
+ type: string
3698
+ nullable: false
3699
+ description: The spec this implementation implements — the binding's spec key.
3700
+ - name: snapshot_area
3701
+ type: string
3702
+ nullable: true
3703
+ description: Store-relative path of this implementation's snapshot area (M-ADR 0214 §4.4), from the binding. Empty when the implementation has none.
3704
+ - name: doc_path
3705
+ type: string
3706
+ nullable: false
3707
+ description: Path of the binding file, relative to the SDD root.
3708
+ - name: source_sdd_id
3709
+ type: string
3710
+ nullable: true
3711
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3712
+ - name: platform_sdd_id
3713
+ type: string
3714
+ nullable: true
3715
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4.
3716
+ description: "An implementation of this SDD's spec: the durable system identity whose snapshots are its versions (M-ADR 0181 §2.5, M-ADR 0214 §4.1). One row per .sdd/implementation-binding.yaml that names its implementation; a binding without an implementation key emits nothing and a finding, because there is no identity to key on."
3717
+ implementation_members:
3718
+ primary_key: id
3719
+ columns:
3720
+ - name: id
3721
+ type: string
3722
+ nullable: false
3723
+ description: <implementation_id>::<repo_key>.
3724
+ - name: implementation_id
3725
+ type: string
3726
+ nullable: false
3727
+ - name: repo_id
3728
+ type: string
3729
+ nullable: true
3730
+ description: The member's repository when it resolves in this SDD's repositories. Empty for a planned member whose repository does not exist yet; a member of any other lifecycle that does not resolve is a builder finding.
3731
+ - name: repo_key
3732
+ type: string
3733
+ nullable: false
3734
+ description: The repository the binding names (its member repo key), resolved or not.
3735
+ - name: role
3736
+ type: string
3737
+ nullable: true
3738
+ - name: lifecycle
3739
+ type: string
3740
+ nullable: true
3741
+ - name: evidence_direction
3742
+ type: string
3743
+ nullable: true
3744
+ description: How this member is evidenced (M-ADR 0180 §2.1, placed on the member by M-ADR 0181 §2.1). Empty when the binding does not say.
3745
+ - name: source_sdd_id
3746
+ type: string
3747
+ nullable: true
3748
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3749
+ - name: platform_sdd_id
3750
+ type: string
3751
+ nullable: true
3752
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4.
3753
+ description: "Edge: a repository is a member of an implementation, with the member's role, lifecycle and evidence direction (M-ADR 0181 §2.1, M-ADR 0214 §4.2). Role and lifecycle live here once: repo_capability, repo_contract and feature_repo resolve their repository to this row rather than copying them (M-ADR 0214 §4.3). Membership is independent of the repository's repo_scope (M-ADR 0213): a shared library under conformance checks is a support member."
3754
+ foreign_keys:
3755
+ - column: implementation_id
3756
+ references: implementations.id
3757
+ nullable: false
3758
+ - column: repo_id
3759
+ references: repositories.id
3760
+ nullable: true
3761
+ column_enums:
3762
+ role: enums.implementation_role
3763
+ lifecycle: enums.implementation_lifecycle
3764
+ evidence_direction: enums.evidence_direction
3765
+ snapshots:
3766
+ primary_key: id
3767
+ columns:
3768
+ - name: id
3769
+ type: string
3770
+ nullable: false
3771
+ description: <implementation_id>@<baseline_id>.
3772
+ - name: implementation_id
3773
+ type: string
3774
+ nullable: false
3775
+ - name: baseline_id
3776
+ type: string
3777
+ nullable: false
3778
+ description: The observation date (YYYY-MM-DD) — the baseline folder and the manifest's snapshot_id.
3779
+ - name: prior_snapshot_id
3780
+ type: string
3781
+ nullable: true
3782
+ description: The snapshot this one was diffed from — <implementation_id>@<prior_snapshot>. Empty for the first.
3783
+ - name: credential_scan_result
3784
+ type: string
3785
+ nullable: true
3786
+ description: The manifest's credential_scan.result (clean, or what was found). Empty when no scan is recorded.
3787
+ - name: source_sdd_id
3788
+ type: string
3789
+ nullable: true
3790
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3791
+ - name: platform_sdd_id
3792
+ type: string
3793
+ nullable: true
3794
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4.
3795
+ description: One implementation's member set pinned at one observation (M-ADR 0181 §2.5, M-ADR 0214 §4.4). One row per dated baseline in the implementation's snapshot area whose snapshot.yaml names the same implementation. Needs the snapshot store root (build --snapshot-store or SDD_SNAPSHOT_STORE); without it the catalogue is empty and the build says so.
3796
+ foreign_keys:
3797
+ - column: implementation_id
3798
+ references: implementations.id
3799
+ nullable: false
3800
+ - column: prior_snapshot_id
3801
+ references: snapshots.id
3802
+ nullable: true
3803
+ snapshot_members:
3804
+ primary_key: id
3805
+ columns:
3806
+ - name: id
3807
+ type: string
3808
+ nullable: false
3809
+ description: <snapshot_id>::<repo_key>.
3810
+ - name: snapshot_id
3811
+ type: string
3812
+ nullable: false
3813
+ - name: repo_id
3814
+ type: string
3815
+ nullable: true
3816
+ description: The member's repository when it resolves in this SDD's repositories; empty otherwise (a removed member's repository may be gone).
3817
+ - name: repo_key
3818
+ type: string
3819
+ nullable: false
3820
+ description: The manifest's canonical_repo_key.
3821
+ - name: source_revision
3822
+ type: string
3823
+ nullable: false
3824
+ description: The revision observed (for a removed member, its last revision).
3825
+ - name: observed_at
3826
+ type: string
3827
+ nullable: true
3828
+ description: "The member's own observation date: the baseline's for an observed member, the earlier one for a carried member, the last observed for a removed one."
3829
+ - name: presence
3830
+ type: string
3831
+ nullable: false
3832
+ - name: source_sdd_id
3833
+ type: string
3834
+ nullable: true
3835
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3836
+ - name: platform_sdd_id
3837
+ type: string
3838
+ nullable: true
3839
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4.
3840
+ description: "Edge: a repository at one revision in one snapshot, with its presence — observed in this baseline, carried from an earlier one, or removed since the prior (M-ADR 0214 §4.4)."
3841
+ foreign_keys:
3842
+ - column: snapshot_id
3843
+ references: snapshots.id
3844
+ nullable: false
3845
+ - column: repo_id
3846
+ references: repositories.id
3847
+ nullable: true
3848
+ column_enums:
3849
+ presence: enums.member_presence
3850
+ data_items:
3851
+ primary_key: id
3852
+ columns:
3853
+ - name: id
3854
+ type: string
3855
+ nullable: false
3856
+ description: di.<category>.<name>, e.g. di.motion.speed.
3857
+ - name: name
3858
+ type: string
3859
+ nullable: false
3860
+ - name: category
3861
+ type: string
3862
+ nullable: false
3863
+ description: Open grouping (motion, behaviour, tyre, energy, …).
3864
+ - name: canonical_field
3865
+ type: string
3866
+ nullable: true
3867
+ description: The integration-facing canonical contract field that carries the item. EMPTY means the platform has no canonical home for it, and coverage reads every requirement on it as unmet (no-canonical-home).
3868
+ - name: description
3869
+ type: string
3870
+ nullable: true
3871
+ - name: source_sdd_id
3872
+ type: string
3873
+ nullable: true
3874
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3875
+ - name: platform_sdd_id
3876
+ type: string
3877
+ nullable: true
3878
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator (M-ADR 0208 §4.4).
3879
+ description: "A unit of data an integration may supply and a feature may need, at the canonical boundary (M-ADR 0216 §4.1). Platform-owned, like events. Not a wire signal: payload grammar stays adopter territory."
3880
+ integration_data_item:
3881
+ primary_key: id
3882
+ columns:
3883
+ - name: id
3884
+ type: string
3885
+ nullable: false
3886
+ description: <integration_id>::<data_item_id>::<evidence_ref>.
3887
+ - name: integration_id
3888
+ type: string
3889
+ nullable: false
3890
+ - name: data_item_id
3891
+ type: string
3892
+ nullable: false
3893
+ - name: verdict
3894
+ type: string
3895
+ nullable: false
3896
+ description: Bound to supply_verdict. A mapping-table row is supported, or partial when its Notes begin with partial; the Data Items Not Supplied section carries the rest.
3897
+ - name: basis
3898
+ type: string
3899
+ nullable: true
3900
+ description: "Bound to supply_basis: the evidence class of the statement."
3901
+ - name: evidence_ref
3902
+ type: string
3903
+ nullable: false
3904
+ description: Path and line of the statement.
3905
+ - name: checked_at
3906
+ type: string
3907
+ nullable: true
3908
+ description: "The governing Checked: line of the section, verbatim (a date, or a source revision). Reported for age; never read as a verdict."
3909
+ - name: source_sdd_id
3910
+ type: string
3911
+ nullable: true
3912
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3913
+ - name: platform_sdd_id
3914
+ type: string
3915
+ nullable: true
3916
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator (M-ADR 0208 §4.4).
3917
+ description: "Supply: what one integration states about one data item, from its feature-grounding (M-ADR 0216 §4.2). Several rows per item are normal (one per evidence_ref); coverage reads the best. No row reads unknown, and unknown is never written."
3918
+ foreign_keys:
3919
+ - column: integration_id
3920
+ references: integrations.id
3921
+ nullable: false
3922
+ - column: data_item_id
3923
+ references: data_items.id
3924
+ nullable: false
3925
+ external_for_profiles:
3926
+ - application-product
3927
+ - integration-product
3928
+ column_enums:
3929
+ verdict: enums.supply_verdict
3930
+ basis: enums.supply_basis
3931
+ feature_data_requirement:
3932
+ primary_key: id
3933
+ columns:
3934
+ - name: id
3935
+ type: string
3936
+ nullable: false
3937
+ description: <feature_id>::<data_item_id>.
3938
+ - name: feature_id
3939
+ type: string
3940
+ nullable: false
3941
+ - name: data_item_id
3942
+ type: string
3943
+ nullable: false
3944
+ - name: necessity
3945
+ type: string
3946
+ nullable: false
3947
+ description: Bound to data_necessity.
3948
+ - name: alternative_group
3949
+ type: string
3950
+ nullable: true
3951
+ description: "Empty, or a label: required rows sharing a label are satisfied by any one of them."
3952
+ - name: basis_ref
3953
+ type: string
3954
+ nullable: true
3955
+ description: Where the need is stated (path and line).
3956
+ - name: source_sdd_id
3957
+ type: string
3958
+ nullable: true
3959
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
3960
+ - name: platform_sdd_id
3961
+ type: string
3962
+ nullable: true
3963
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator (M-ADR 0208 §4.4).
3964
+ description: "Demand: a data item one feature needs, from the feature spec's Data Requirements table (M-ADR 0216 §4.3). Feature grain only: a capability's need is the union of its features'."
3965
+ foreign_keys:
3966
+ - column: feature_id
3967
+ references: features.id
3968
+ nullable: false
3969
+ - column: data_item_id
3970
+ references: data_items.id
3971
+ nullable: false
3972
+ external_for_profiles:
3973
+ - application-product
3974
+ - integration-product
3975
+ column_enums:
3976
+ necessity: enums.data_necessity
3977
+ feature_integration_support:
3978
+ primary_key: id
3979
+ columns:
3980
+ - name: id
3981
+ type: string
3982
+ nullable: false
3983
+ description: <feature_id>::<integration_id>.
3984
+ - name: feature_id
3985
+ type: string
3986
+ nullable: false
3987
+ - name: integration_id
3988
+ type: string
3989
+ nullable: false
3990
+ - name: verdict
3991
+ type: string
3992
+ nullable: false
3993
+ description: "Bound to support_verdict. Each requirement is decided first (an alternative_group is unmet only when every member is); the feature takes the worst: unsupported > unknown > partial > supported. not-applicable only when every required item is."
3994
+ - name: limiting_item_id
3995
+ type: string
3996
+ nullable: true
3997
+ description: "For unsupported: the deciding requirement's item. Empty otherwise."
3998
+ - name: limiting_reason
3999
+ type: string
4000
+ nullable: true
4001
+ description: "For unsupported: bound to limiting_reason. Empty otherwise."
4002
+ - name: source_sdd_id
4003
+ type: string
4004
+ nullable: true
4005
+ description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
4006
+ - name: platform_sdd_id
4007
+ type: string
4008
+ nullable: true
4009
+ description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator (M-ADR 0208 §4.4).
4010
+ description: "Coverage: whether one integration can support one feature, DERIVED at aggregation from supply and demand (M-ADR 0216 §4.4); empty in a member build. SUPPORTED MEANS THE ITEMS REACH THE CANONICAL BOUNDARY, NOT THAT THE FEATURE WORKS END TO END: routing past that boundary is not checked."
4011
+ foreign_keys:
4012
+ - column: feature_id
4013
+ references: features.id
4014
+ nullable: false
4015
+ - column: integration_id
4016
+ references: integrations.id
4017
+ nullable: false
4018
+ - column: limiting_item_id
4019
+ references: data_items.id
4020
+ nullable: true
4021
+ column_enums:
4022
+ verdict: enums.support_verdict
4023
+ limiting_reason: enums.limiting_reason
@@ -78,6 +78,9 @@ export function renderRepoCapability(findings, metrics) {
78
78
  }));
79
79
  }
80
80
  }
81
+ if (metrics.total === 0 && findings.length === 0) {
82
+ lines.push(`[metric] repo_capability_resolution: n/a — 0 repository rows, nothing to resolve`);
83
+ }
81
84
  if (metrics.total > 0) {
82
85
  const resolved = metrics.byCapability + metrics.byExemption;
83
86
  const pct = fmt1f((100.0 * resolved) / metrics.total);
@@ -125,6 +128,9 @@ export function renderServiceCapability(findings, metrics, tier) {
125
128
  `capability_service.csv); check the aggregate next${leverageNote(f)}`);
126
129
  }
127
130
  }
131
+ if (metrics.total === 0 && findings.length === 0) {
132
+ lines.push(`[metric] service_capability_resolution (tier=${tier}): n/a — 0 service rows, nothing to resolve`);
133
+ }
128
134
  if (metrics.total > 0) {
129
135
  const resolved = metrics.claimed + metrics.exempt;
130
136
  const pct = fmt1f((100.0 * resolved) / metrics.total);
@@ -426,6 +432,14 @@ export function renderScoresCoverage(res, tier) {
426
432
  `catalogue row — the assessment describes something the model no longer ` +
427
433
  `holds; re-key the registry entry or withdraw the score`);
428
434
  }
435
+ else if (f.kind === "recorded-path-mismatch") {
436
+ // COVERAGE class — report-only by ruling (decision #3, 2026-09-29): it
437
+ // becomes gate-acting only once both estates read zero, and that is a
438
+ // deliberate later change to this one word, as for orphaned-score above.
439
+ out.push(`[finding] coverage: ${f.rubricId} score for ${f.subject} — the recorded file ` +
440
+ `is not the target's catalogue path, so the assessment may describe a copy the ` +
441
+ `catalogue cannot see; re-score against the catalogue path or re-point file_path`);
442
+ }
429
443
  else {
430
444
  out.push(`[finding] coverage: ${f.subject}`);
431
445
  }
@@ -470,6 +484,22 @@ export function renderScoresCoverage(res, tier) {
470
484
  out.push(`[metric] scores_coverage.${m.rubricId}: ${m.covered}/${m.eligible} ` +
471
485
  `${m.targetKind}(s) scored, ${m.scored} score(s) present, ${m.orphaned} orphaned`);
472
486
  }
487
+ // Recorded-path check (decision #3): stated for every rubric with scores,
488
+ // including the ones it cannot check — n/a is named, never read as agreement.
489
+ const scoredMetrics = res.metrics.filter((m) => m.scored > 0);
490
+ const checkable = scoredMetrics.filter((m) => m.recordedPath !== null);
491
+ const notCheckable = scoredMetrics.filter((m) => m.recordedPath === null);
492
+ if (scoredMetrics.length > 0) {
493
+ const sum = (k) => checkable.reduce((a, m) => a + (m.recordedPath?.[k] ?? 0), 0);
494
+ out.push(`[metric] scores_coverage.recorded_path: ${sum("agree")} agree, ${sum("mismatch")} mismatch, ` +
495
+ `${sum("noRecordedPath")} with no recorded path — across ${checkable.length} rubric(s) whose ` +
496
+ `catalogue holds the scored file's path` +
497
+ (notCheckable.length > 0
498
+ ? `; n/a for ${notCheckable.length} (${notCheckable.map((m) => m.rubricId).join(", ")}) — ` +
499
+ `the catalogue does not hold the path of the file those rubrics score`
500
+ : "") +
501
+ ` (report-only; a path filled in from the catalogue agrees by construction)`);
502
+ }
473
503
  // The excluded population is printed BESIDE the denominator it shrank, never
474
504
  // folded into it. A reader comparing this run against a pre-filter baseline
475
505
  // must be able to see why the denominator moved without re-deriving it, and