@tracepass/dpp-validate 0.12.0 → 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.
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Battery field-applicability predicate — the SINGLE source of truth for
3
+ * "does this Annex XIII field apply to *this* battery?", consumed by both:
4
+ *
5
+ * - the compliance verdict engine (rules.ts) — flags a filled field that
6
+ * doesn't apply, or warns when applicability can't be determined; and
7
+ * - the passport editor UI (PassportEditor.tsx) — soft-collapses
8
+ * not-applicable fields so a reviewer isn't asked for data the
9
+ * regulation doesn't require for their battery type.
10
+ *
11
+ * Source: DG GROW battery-passport webinar 27 May 2026, slide 36
12
+ * (per-category applicability matrix) + the cross-reference/scope-gate
13
+ * slides; Reg. (EU) 2023/1542 Art. 14(1) + Annex VII Parts A and B.
14
+ * Analysis: knowledgebase/regulations/dpp-battery-webinar-2026-05.md.
15
+ * Spec home: knowledgebase/regulations/dpp-conditional-rules.md.
16
+ *
17
+ * Design contract — the SAFETY rule that makes A safe:
18
+ * "unknown" (a gate's trigger field is absent) ⇒ treat as APPLIES for the
19
+ * UI (show the field) and as `unverifiable_conditional` for the verdict
20
+ * (warn, never assert). We NEVER hide or suppress a field on uncertainty —
21
+ * a mis-classified battery must never lose a legally-required field.
22
+ *
23
+ * Triggers:
24
+ * - batteryCategory — a real template field in battery.json (enum: LMT |
25
+ * EV | industrial_gt_2kwh | industrial_lte_2kwh | SLI
26
+ * | portable). Read from `passport.fields`.
27
+ * - hasBMS, rechargeable, externalStorageOnly, isStationaryBess — tri-state
28
+ * booleans on `passport.batteryProfile` (a separate
29
+ * block, NOT template fields — keeps the template's
30
+ * field count intact). Absent ⇒ the dependent gate
31
+ * resolves to "unknown" (show + warn), not a guess.
32
+ *
33
+ * Pure + IO-free → unit-tested in tests/compliance/battery-applicability.test.ts.
34
+ */
35
+ import type { Passport } from "@tracepass/dpp-types";
36
+ import { IN_SCOPE_BATTERY_CATEGORIES } from "./battery-scope.js";
37
+ export type Applicability = "applies" | "not_applicable" | "unknown";
38
+ /** Battery categories that are *in scope* for a battery passport at all.
39
+ * Re-exported from the shared definition so this module's consumers keep
40
+ * importing it from here; the passport-level scope gate is rules.ts BAT-1. */
41
+ export { IN_SCOPE_BATTERY_CATEGORIES };
42
+ export interface FieldGate {
43
+ /** Template field keys this gate governs. */
44
+ keys: string[];
45
+ /** Regulation article / annex point reference, for the verdict citation. */
46
+ article: string;
47
+ /** One-line reason, surfaced in the verdict + editor tooltip. */
48
+ reason: string;
49
+ /** Extra remediation, for gates that turn on the stationary-storage flag. */
50
+ fixHint?: string;
51
+ /**
52
+ * ISO-8601 date from which the underlying provision is mandatory.
53
+ * Before this date `categoryFieldApplicability` returns "unknown" for fields
54
+ * governed by this gate, so they produce a warning but never a hard block.
55
+ * After this date the gate's `decide()` result is used as-is.
56
+ */
57
+ mandatoryFrom?: string;
58
+ /** Decide applicability from the resolved trigger values. */
59
+ decide(t: Triggers): Applicability;
60
+ }
61
+ interface Triggers {
62
+ /** Sub-category value read from the passport (batteryCategory / detergentUserType / …). */
63
+ category: string | undefined;
64
+ hasBMS: boolean | undefined;
65
+ rechargeable: boolean | undefined;
66
+ externalStorageOnly: boolean | undefined;
67
+ isStationaryBess: boolean | undefined;
68
+ microorganismsAdded?: boolean | undefined;
69
+ sdsProvided?: boolean | undefined;
70
+ containsAllergenicFragrances?: boolean | undefined;
71
+ alsoUnderOtherActs?: boolean | undefined;
72
+ /**
73
+ * Field-derived tri-state: resolves whether a named passport field has an
74
+ * approved, non-empty value.
75
+ * true — field present + approved
76
+ * undefined — field present but pending_review (unknown → no hard block)
77
+ * false — field absent or empty
78
+ * Optional for backward compat; absent ≡ false (field not present).
79
+ */
80
+ fieldPresent?(key: string): boolean | undefined;
81
+ }
82
+ /**
83
+ * The conditional gates, one per applicability rule from the deck. Order
84
+ * doesn't matter — each governs a disjoint set of field keys.
85
+ */
86
+ export declare const BATTERY_FIELD_GATES: FieldGate[];
87
+ /** Every field key under any gate (the conditional set). */
88
+ export declare const GATED_FIELD_KEYS: ReadonlySet<string>;
89
+ /**
90
+ * Applicability for a single battery field key. Ungated keys are always
91
+ * "applies". This is the granular accessor the editor uses per-field.
92
+ */
93
+ export declare function fieldApplicability(passport: Passport, key: string): Applicability;
94
+ /**
95
+ * Full map for the gated fields only (callers default everything else to
96
+ * "applies"). The verdict engine iterates this to emit findings; the
97
+ * editor reads it once per render and looks up per field.
98
+ *
99
+ * Returns an empty map for non-battery passports — `category` here is the
100
+ * template category, NOT batteryCategory; callers pass it so this is a
101
+ * no-op outside battery and the predicate stays battery-scoped.
102
+ */
103
+ /** @deprecated Use `categoryFieldApplicability` instead. Kept for backward compat. */
104
+ export declare function batteryFieldApplicability(passport: Passport, category: string): Record<string, Applicability>;
105
+ /** Look up the gate metadata (article + reason) for a gated field key. */
106
+ export declare function gateForKey(key: string): FieldGate | undefined;
107
+ /**
108
+ * Date strings from which each category's passport obligations become mandatory.
109
+ * Before these dates, any gate that carries `mandatoryFrom` returns "unknown"
110
+ * (warning, no hard block). After, the gate's `decide()` result is used as-is.
111
+ */
112
+ export declare const DETERGENTS_PASSPORT_MANDATORY_FROM = "2029-09-23";
113
+ export declare const TOYS_PASSPORT_MANDATORY_FROM = "2030-08-01";
114
+ /**
115
+ * CATEGORY_FIELD_GATES — generalised gate registry, keyed by template category.
116
+ *
117
+ * Phase 1: battery only. Phase 2: detergents and toys added.
118
+ *
119
+ * Callers that need to enumerate gates for a category use this registry.
120
+ * The battery-specific `batteryFieldApplicability` is now a thin wrapper.
121
+ */
122
+ export declare const CATEGORY_FIELD_GATES: Record<string, FieldGate[]>;
123
+ /**
124
+ * Compute the applicability map for all gated fields of any category.
125
+ * Returns {} for categories with no gates (safe to iterate).
126
+ *
127
+ * Replaces the battery-only early return in `batteryFieldApplicability` with a
128
+ * generic path: every category that has gates in CATEGORY_FIELD_GATES is handled,
129
+ * everything else is a no-op. `batteryFieldApplicability` is kept as a
130
+ * backward-compat alias.
131
+ *
132
+ * @param now Optional reference date for `mandatoryFrom` enforcement. Defaults
133
+ * to the current wall clock. Callers should pass a fixed date in tests.
134
+ * When a gate's `mandatoryFrom` is in the future relative to `now`,
135
+ * ALL keys governed by that gate resolve to "unknown" (warning only,
136
+ * never a hard publish block) — keeping the provision from creating
137
+ * obligations before it legally applies.
138
+ */
139
+ export declare function categoryFieldApplicability(passport: Passport, category: string, now?: Date): Record<string, Applicability>;
140
+ //# sourceMappingURL=battery-applicability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery-applicability.d.ts","sourceRoot":"","sources":["../../src/vendor/battery-applicability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAGjE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAErE;;+EAE+E;AAC/E,OAAO,EAAE,2BAA2B,EAAE,CAAC;AAUvC,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,MAAM,CAAC,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;CACpC;AAED,UAAU,QAAQ;IAChB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAE7B,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,YAAY,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,mBAAmB,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,gBAAgB,EAAE,OAAO,GAAG,SAAS,CAAC;IAGtC,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAElC,4BAA4B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnD,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC;;;;;;;OAOG;IACH,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACjD;AAyFD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,EA2L1C,CAAC;AAQF,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAA+B,CAAC;AAEjF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAIjF;AAED;;;;;;;;GAQG;AACH,sFAAsF;AACtF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAE/B;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAE7D;AAED;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,eAAe,CAAC;AAC/D,eAAO,MAAM,4BAA4B,eAAe,CAAC;AAqFzD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAI5D,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,IAAI,GACT,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAiB/B"}
@@ -0,0 +1,513 @@
1
+ /**
2
+ * Battery field-applicability predicate — the SINGLE source of truth for
3
+ * "does this Annex XIII field apply to *this* battery?", consumed by both:
4
+ *
5
+ * - the compliance verdict engine (rules.ts) — flags a filled field that
6
+ * doesn't apply, or warns when applicability can't be determined; and
7
+ * - the passport editor UI (PassportEditor.tsx) — soft-collapses
8
+ * not-applicable fields so a reviewer isn't asked for data the
9
+ * regulation doesn't require for their battery type.
10
+ *
11
+ * Source: DG GROW battery-passport webinar 27 May 2026, slide 36
12
+ * (per-category applicability matrix) + the cross-reference/scope-gate
13
+ * slides; Reg. (EU) 2023/1542 Art. 14(1) + Annex VII Parts A and B.
14
+ * Analysis: knowledgebase/regulations/dpp-battery-webinar-2026-05.md.
15
+ * Spec home: knowledgebase/regulations/dpp-conditional-rules.md.
16
+ *
17
+ * Design contract — the SAFETY rule that makes A safe:
18
+ * "unknown" (a gate's trigger field is absent) ⇒ treat as APPLIES for the
19
+ * UI (show the field) and as `unverifiable_conditional` for the verdict
20
+ * (warn, never assert). We NEVER hide or suppress a field on uncertainty —
21
+ * a mis-classified battery must never lose a legally-required field.
22
+ *
23
+ * Triggers:
24
+ * - batteryCategory — a real template field in battery.json (enum: LMT |
25
+ * EV | industrial_gt_2kwh | industrial_lte_2kwh | SLI
26
+ * | portable). Read from `passport.fields`.
27
+ * - hasBMS, rechargeable, externalStorageOnly, isStationaryBess — tri-state
28
+ * booleans on `passport.batteryProfile` (a separate
29
+ * block, NOT template fields — keeps the template's
30
+ * field count intact). Absent ⇒ the dependent gate
31
+ * resolves to "unknown" (show + warn), not a guess.
32
+ *
33
+ * Pure + IO-free → unit-tested in tests/compliance/battery-applicability.test.ts.
34
+ */
35
+ import { resolveConditionProfile } from "./condition-profile.js";
36
+ import { IN_SCOPE_BATTERY_CATEGORIES } from "./battery-scope.js";
37
+ import { SUBCATEGORY_FIELD } from "./subcategory.js";
38
+ /** Battery categories that are *in scope* for a battery passport at all.
39
+ * Re-exported from the shared definition so this module's consumers keep
40
+ * importing it from here; the passport-level scope gate is rules.ts BAT-1. */
41
+ export { IN_SCOPE_BATTERY_CATEGORIES };
42
+ /** Field keys whose applicability is conditional, with a short reason +
43
+ * the deck article they map to. Everything NOT listed here is always
44
+ * "applies". Kept as data so the rules engine + editor can enumerate the
45
+ * conditional set and so a reader can audit each gate against the deck. */
46
+ /** Remediation for the gates that depend on `isStationaryBess`. */
47
+ const SBESS_FIX_HINT = "For an industrial (>2 kWh) battery, confirm whether it is a stationary battery energy storage system: Art. 14 state-of-health and Annex VII expected-lifetime data apply only to stationary storage systems, LMT and (for state of health) EV batteries.";
48
+ /**
49
+ * Resolve a profile flag to a confirmed boolean. Only an `approved` flag
50
+ * counts — a `pending_review` (AI-suggested, unconfirmed) flag resolves to
51
+ * `undefined` ("unknown") so it can NEVER hard-hide a field. This is the
52
+ * safety contract: AI may suggest, but gating tightens only on confirmation.
53
+ *
54
+ * Accepts any object with `value: boolean` and `status: string` — works for
55
+ * both the legacy `BatteryProfileFlag` and the new `ConditionFlag`.
56
+ */
57
+ function confirmed(flag) {
58
+ if (!flag)
59
+ return undefined;
60
+ return flag.status === "approved" ? flag.value : undefined;
61
+ }
62
+ function readTriggers(passport, templateCategory = "battery") {
63
+ // Sub-category field key (batteryCategory / detergentUserType / …) from the
64
+ // SUBCATEGORY_FIELD map. Falls back to undefined for categories with no map entry (e.g. toys).
65
+ const subcatFieldKey = SUBCATEGORY_FIELD[templateCategory];
66
+ const catField = subcatFieldKey ? passport.fields[subcatFieldKey] : undefined;
67
+ // Use the canonical accessor so conditionProfile (new) and batteryProfile
68
+ // (legacy) both work through the same path.
69
+ const profile = resolveConditionProfile(passport);
70
+ return {
71
+ category: catField && catField.value != null && catField.value !== ""
72
+ ? String(catField.value)
73
+ : undefined,
74
+ // Battery flags
75
+ hasBMS: confirmed(profile["hasBMS"]),
76
+ rechargeable: confirmed(profile["rechargeable"]),
77
+ externalStorageOnly: confirmed(profile["externalStorageOnly"]),
78
+ isStationaryBess: confirmed(profile["isStationaryBess"]),
79
+ // Detergents flags (phase 2)
80
+ microorganismsAdded: confirmed(profile["microorganismsAdded"]),
81
+ sdsProvided: confirmed(profile["sdsProvided"]),
82
+ // Toys flags (phase 2)
83
+ containsAllergenicFragrances: confirmed(profile["containsAllergenicFragrances"]),
84
+ alsoUnderOtherActs: confirmed(profile["alsoUnderOtherActs"]),
85
+ // Field-derived tri-state: approved non-empty → true; pending_review → undefined; absent → false
86
+ fieldPresent: (key) => {
87
+ const f = passport.fields[key];
88
+ if (!f || f.value == null || f.value === "")
89
+ return false;
90
+ return f.status === "approved" ? true : undefined;
91
+ },
92
+ };
93
+ }
94
+ /**
95
+ * Is this battery an Art. 14(1) subject (state-of-health / expected-lifetime
96
+ * data applies)? Art. 14(1) covers EV, LMT, and stationary battery energy
97
+ * storage systems — NOT other industrial batteries.
98
+ *
99
+ * Returns:
100
+ * true — EV or LMT (deterministic from category alone)
101
+ * true/false — industrial_gt_2kwh resolved through isStationaryBess
102
+ * undefined — category absent (cannot determine) or
103
+ * industrial_gt_2kwh with isStationaryBess unconfirmed
104
+ * false — any other category (portable, SLI, industrial_lte_2kwh)
105
+ */
106
+ function art14Subject(t) {
107
+ if (t.category === undefined)
108
+ return undefined;
109
+ if (t.category === "EV" || t.category === "LMT")
110
+ return true;
111
+ if (t.category === "industrial_gt_2kwh")
112
+ return t.isStationaryBess;
113
+ return false;
114
+ }
115
+ /**
116
+ * Annex VII Part A + B applicability: applies for LMT; for industrial_gt_2kwh
117
+ * only when confirmed as a stationary BESS; not_applicable for EV and all
118
+ * other categories; unknown when category is absent or isStationaryBess unset.
119
+ *
120
+ * Used for: the Annex VII Part A cluster (remaining capacity, pack resistance,
121
+ * etc.) and the Annex VII Part B cluster (expected-lifetime: capacity/energy
122
+ * throughput, date of service entry).
123
+ */
124
+ function sbessOrLmt(t) {
125
+ if (t.category === undefined)
126
+ return "unknown";
127
+ if (t.category === "LMT")
128
+ return "applies";
129
+ if (t.category === "industrial_gt_2kwh") {
130
+ if (t.isStationaryBess === true)
131
+ return "applies";
132
+ if (t.isStationaryBess === false)
133
+ return "not_applicable";
134
+ return "unknown";
135
+ }
136
+ // EV uses SOCE instead; portable / SLI / industrial_lte_2kwh are out of scope.
137
+ return "not_applicable";
138
+ }
139
+ /**
140
+ * The conditional gates, one per applicability rule from the deck. Order
141
+ * doesn't matter — each governs a disjoint set of field keys.
142
+ */
143
+ export const BATTERY_FIELD_GATES = [
144
+ {
145
+ // Deck 1(k): "capacity threshold for exhaustion (only for electric
146
+ // vehicle batteries)". Resolvable today — trigger is batteryCategory.
147
+ keys: ["capacityThresholdForExhaustion"],
148
+ article: "Annex XIII 1(k)",
149
+ reason: "Capacity threshold for exhaustion applies only to electric-vehicle (EV) batteries.",
150
+ decide: (t) => {
151
+ if (t.category === undefined)
152
+ return "unknown";
153
+ return t.category === "EV" ? "applies" : "not_applicable";
154
+ },
155
+ },
156
+ {
157
+ // Annex XIII 4(b) / Art. 14(1): state of health applies to stationary
158
+ // battery energy storage systems (SBESS), LMT, and EV batteries — and
159
+ // only when they have a battery management system. A plain industrial
160
+ // battery that is NOT a stationary ESS does NOT owe state-of-health data.
161
+ // art14Subject encodes the Art. 14(1) subject set; hasBMS is the second
162
+ // condition. Either being false → not_applicable; either absent → unknown.
163
+ keys: ["stateOfHealth"],
164
+ article: "Annex XIII 4(b) / Art. 14",
165
+ fixHint: SBESS_FIX_HINT,
166
+ reason: "State of health (Art. 14) applies only to stationary battery energy storage systems, LMT, and EV batteries that have a battery management system (BMS). A plain industrial (>2 kWh) battery that is not a stationary storage system does not owe this data.",
167
+ decide: (t) => {
168
+ const a14 = art14Subject(t);
169
+ // Either axis definitively false → field does not apply.
170
+ if (a14 === false || t.hasBMS === false)
171
+ return "not_applicable";
172
+ // Either axis unknown (category absent, or SBESS unconfirmed) → cannot determine.
173
+ if (a14 === undefined || t.hasBMS === undefined)
174
+ return "unknown";
175
+ // Both confirmed true → applies.
176
+ return "applies";
177
+ },
178
+ },
179
+ {
180
+ // Deck (c): carbon footprint cross-references Art. 7, which is
181
+ // rechargeable-only ("If the battery is non-rechargeable, (c) remains
182
+ // empty"). rechargeable absent ⇒ unknown.
183
+ keys: [
184
+ "carbonFootprintTotal",
185
+ "carbonFootprintPerformanceClass",
186
+ "carbonFootprintLabel",
187
+ "carbonFootprintStudyUrl",
188
+ "cfRawMaterialAcquisition",
189
+ "cfMainProductProduction",
190
+ "cfDistribution",
191
+ "cfEndOfLifeRecycling",
192
+ ],
193
+ article: "Annex XIII 1(c) / Art. 7",
194
+ reason: "Carbon-footprint declaration applies only to rechargeable batteries.",
195
+ decide: (t) => {
196
+ if (t.rechargeable === undefined)
197
+ return "unknown";
198
+ return t.rechargeable ? "applies" : "not_applicable";
199
+ },
200
+ },
201
+ {
202
+ // Deck (e): recycled content cross-references Art. 8 ("If the battery
203
+ // has external storage only, (e) remains empty"). externalStorageOnly
204
+ // absent ⇒ unknown.
205
+ keys: [
206
+ "recycledContentCobalt",
207
+ "recycledContentLead",
208
+ "recycledContentLithium",
209
+ "recycledContentNickel",
210
+ "recycledContentDocumentation",
211
+ // Pre/post-consumer split-shares (Annex XIII 1(e)) carry the same gate
212
+ // as the combined figures above — external-storage-only batteries are exempt.
213
+ "preConsumerRecycledNickelShare",
214
+ "preConsumerRecycledCobaltShare",
215
+ "preConsumerRecycledLithiumShare",
216
+ "postConsumerRecycledNickelShare",
217
+ "postConsumerRecycledCobaltShare",
218
+ "postConsumerRecycledLithiumShare",
219
+ ],
220
+ article: "Annex XIII 1(e) / Art. 8",
221
+ reason: "Recycled-content information does not apply to industrial batteries with exclusively external storage.",
222
+ decide: (t) => {
223
+ // Art. 8(1) exempts only "industrial batteries … except those with
224
+ // exclusively external storage"; EV and LMT batteries owe it regardless.
225
+ if (t.category !== undefined && t.category !== "industrial_gt_2kwh")
226
+ return "applies";
227
+ if (t.externalStorageOnly === undefined)
228
+ return "unknown";
229
+ return t.externalStorageOnly ? "not_applicable" : "applies";
230
+ },
231
+ },
232
+ {
233
+ // Annex XIII 4(b) / Art. 14: state of certified energy (SOCE) applies
234
+ // only to electric-vehicle (EV) batteries. Resolvable from batteryCategory.
235
+ keys: ["stateOfCertifiedEnergy"],
236
+ article: "Annex XIII 4(b) / Art. 14",
237
+ reason: "State of certified energy (SOCE) applies only to electric-vehicle (EV) batteries.",
238
+ decide: (t) => {
239
+ if (t.category === undefined)
240
+ return "unknown";
241
+ return t.category === "EV" ? "applies" : "not_applicable";
242
+ },
243
+ },
244
+ {
245
+ // Annex XIII 4(b) / Art. 14 / Annex VII Part A: remaining capacity /
246
+ // power capability, remaining round-trip efficiency (DP 64), self-discharge
247
+ // evolution, pack ohmic resistance, and initial self-discharge rate apply
248
+ // only to SBESS and LMT batteries — NOT EV (which has SOCE instead), and
249
+ // NOT plain industrial batteries that are not stationary storage systems.
250
+ // Art. 14(1) subjects: SBESS + LMT + EV. Annex VII Part A applies to
251
+ // SBESS + LMT. sbessOrLmt() resolves this correctly.
252
+ // NOTE: roundTripEfficiencyFade (DP 58) is "if applicable" in ALL THREE
253
+ // guidance columns and therefore is NOT in this gate — it applies to EV too.
254
+ keys: [
255
+ "remainingCapacity",
256
+ "remainingPowerCapability",
257
+ "remainingRoundTripEfficiency",
258
+ "evolutionOfSelfDischargeRate",
259
+ "currentInternalResistancePack",
260
+ // initialSelfDischargeRate cites Annex VII Part A(4) — same applicability
261
+ // as the rest of the Part A cluster.
262
+ "initialSelfDischargeRate",
263
+ ],
264
+ article: "Annex XIII 4(b) / Art. 14 / Annex VII Part A",
265
+ fixHint: SBESS_FIX_HINT,
266
+ reason: "Remaining capacity, remaining power capability, remaining round-trip efficiency, self-discharge evolution, pack ohmic resistance, and initial self-discharge rate (Annex VII Part A) apply only to LMT batteries and stationary battery energy storage systems, not EV or non-stationary industrial batteries.",
267
+ decide: sbessOrLmt,
268
+ },
269
+ {
270
+ // Annex XIII 4(b) / Art. 14 / Annex VII Part B: expected-lifetime
271
+ // parameters — capacity throughput, energy throughput, and date of first
272
+ // service entry — apply only to SBESS and LMT batteries (Annex VII Part B).
273
+ // EV carries SOCE + the capacity-threshold gate instead; a plain industrial
274
+ // battery that is not a stationary storage system does not owe this data.
275
+ // The template already marks these notApplicable for EV via requiredBy;
276
+ // the gate enforces that at the applicability-warning layer too.
277
+ keys: [
278
+ "capacityThroughput",
279
+ "energyThroughput",
280
+ "dateOfServiceEntry",
281
+ ],
282
+ article: "Annex XIII 4(b) / Art. 14 / Annex VII Part B",
283
+ fixHint: SBESS_FIX_HINT,
284
+ reason: "Expected-lifetime parameters (Annex VII Part B) apply only to LMT batteries and stationary battery energy storage systems.",
285
+ decide: sbessOrLmt,
286
+ },
287
+ {
288
+ // Phase 1 — rechargeable gate: 12 performance fields that derive from the
289
+ // Art. 10(1) performance document, which only exists for rechargeable batteries.
290
+ //
291
+ // Category awareness: EV and LMT batteries are inherently rechargeable by
292
+ // definition — no flag check is needed. The gate always returns "applies" for
293
+ // those categories, matching the "required" entries in the template's requiredBy
294
+ // map. For industrial_gt_2kwh the flag is decisive: an approved rechargeable=true
295
+ // promotes the conditional template entry to a hard publish block; rechargeable=false
296
+ // means the Art. 10(1) duty does not arise.
297
+ //
298
+ // Note: dynamicEnergyRoundTripEfficiency (DP 57) is "if applicable" for ALL three
299
+ // categories and is NOT in this gate — it carries no requiredBy map at all.
300
+ //
301
+ // Legal basis:
302
+ // Annex XIII 1(j) — expected lifetime, cycle data, performance-fade metrics
303
+ // Annex XIII 1(n) — initial / 50%-cycle-life round-trip efficiency
304
+ // Annex XIII 4(a)(i/ii/iv) — dynamic rated capacity, power, internal resistance
305
+ // Art. 10(1) — performance document for rechargeable industrial >2 kWh,
306
+ // EV, and LMT batteries
307
+ keys: [
308
+ // Static performance (Annex XIII 1(j)/(n) / Art. 10(1))
309
+ "initialEnergyRoundTripEfficiency",
310
+ "roundTripEfficiencyAtHalfCycleLife",
311
+ // Expected lifetime (Annex XIII 4(c) / Art. 10(1))
312
+ "expectedLifetimeCycles",
313
+ "expectedLifetimeYears",
314
+ "expectedLifetimeReferenceConditions",
315
+ // Performance test parameters (Art. 10(1))
316
+ "cRateRelevantCycleLifeTest",
317
+ // State-of-health performance metrics (Art. 10(1) / Annex XIII 4(b) partial)
318
+ "internalResistanceIncrease",
319
+ "capacityFade",
320
+ "powerFade",
321
+ // Dynamic performance data (Annex XIII 4(a)(i/ii/iv)) — updated on status change
322
+ // Note: 4(a)(iii) = dynamicEnergyRoundTripEfficiency is "if applicable" → not gated
323
+ "dynamicRatedCapacity",
324
+ "dynamicPowerCapability",
325
+ "dynamicInternalResistance",
326
+ ],
327
+ article: "Annex XIII 1(j)/(n), 4(a)(i/ii/iv), Art. 10(1)",
328
+ reason: "These performance fields come from the Art. 10(1) performance document (rechargeable industrial >2 kWh, EV, LMT). EV and LMT are inherently rechargeable; the flag governs industrial_gt_2kwh only.",
329
+ decide: (t) => {
330
+ // EV and LMT are rechargeable by definition — the flag is irrelevant for them.
331
+ if (t.category === "EV" || t.category === "LMT")
332
+ return "applies";
333
+ // industrial_gt_2kwh: the rechargeable flag is decisive.
334
+ if (t.rechargeable === undefined)
335
+ return "unknown";
336
+ return t.rechargeable ? "applies" : "not_applicable";
337
+ },
338
+ },
339
+ ];
340
+ /** Pre-computed map from field key → the gate governing it. */
341
+ const KEY_TO_GATE = new Map();
342
+ for (const gate of BATTERY_FIELD_GATES) {
343
+ for (const k of gate.keys)
344
+ KEY_TO_GATE.set(k, gate);
345
+ }
346
+ /** Every field key under any gate (the conditional set). */
347
+ export const GATED_FIELD_KEYS = new Set(KEY_TO_GATE.keys());
348
+ /**
349
+ * Applicability for a single battery field key. Ungated keys are always
350
+ * "applies". This is the granular accessor the editor uses per-field.
351
+ */
352
+ export function fieldApplicability(passport, key) {
353
+ const gate = KEY_TO_GATE.get(key);
354
+ if (!gate)
355
+ return "applies";
356
+ return gate.decide(readTriggers(passport, "battery"));
357
+ }
358
+ /**
359
+ * Full map for the gated fields only (callers default everything else to
360
+ * "applies"). The verdict engine iterates this to emit findings; the
361
+ * editor reads it once per render and looks up per field.
362
+ *
363
+ * Returns an empty map for non-battery passports — `category` here is the
364
+ * template category, NOT batteryCategory; callers pass it so this is a
365
+ * no-op outside battery and the predicate stays battery-scoped.
366
+ */
367
+ /** @deprecated Use `categoryFieldApplicability` instead. Kept for backward compat. */
368
+ export function batteryFieldApplicability(passport, category) {
369
+ return categoryFieldApplicability(passport, category);
370
+ }
371
+ /** Look up the gate metadata (article + reason) for a gated field key. */
372
+ export function gateForKey(key) {
373
+ return KEY_TO_GATE.get(key);
374
+ }
375
+ /**
376
+ * Date strings from which each category's passport obligations become mandatory.
377
+ * Before these dates, any gate that carries `mandatoryFrom` returns "unknown"
378
+ * (warning, no hard block). After, the gate's `decide()` result is used as-is.
379
+ */
380
+ export const DETERGENTS_PASSPORT_MANDATORY_FROM = "2029-09-23";
381
+ export const TOYS_PASSPORT_MANDATORY_FROM = "2030-08-01";
382
+ // ─── Detergents gates (Reg (EU) 2026/405) ────────────────────────────────────
383
+ const DETERGENTS_FIELD_GATES = [
384
+ {
385
+ // Annex VI Part A(i): microorganisms list is required ONLY where micro-organisms
386
+ // are intentionally added to the product. If no micro-organisms are added the
387
+ // field does not apply. `microorganismsAdded` flag (owner-confirmed) resolves this.
388
+ keys: ["microorganisms"],
389
+ article: "Annex VI Part A(i) / Reg (EU) 2026/405",
390
+ reason: "The list of micro-organisms is required only where micro-organisms are intentionally added to the detergent formulation.",
391
+ mandatoryFrom: DETERGENTS_PASSPORT_MANDATORY_FROM,
392
+ decide: (t) => {
393
+ if (t.microorganismsAdded === undefined)
394
+ return "unknown";
395
+ return t.microorganismsAdded ? "applies" : "not_applicable";
396
+ },
397
+ },
398
+ {
399
+ // Annex VI Part A(h): the full ingredients list for industrial/institutional
400
+ // detergents is required UNLESS the manufacturer has provided a Safety Data Sheet
401
+ // (SDS) for the product. When an SDS is provided the duty is discharged separately.
402
+ keys: ["ingredients"],
403
+ article: "Annex VI Part A(h) / Reg (EU) 2026/405",
404
+ reason: "The full ingredients list for an industrial/institutional detergent is required in the passport unless a Safety Data Sheet covering that information has been provided separately.",
405
+ mandatoryFrom: DETERGENTS_PASSPORT_MANDATORY_FROM,
406
+ decide: (t) => {
407
+ if (t.sdsProvided === undefined)
408
+ return "unknown";
409
+ // SDS provided → the duty is discharged via SDS, not the passport.
410
+ return t.sdsProvided ? "not_applicable" : "applies";
411
+ },
412
+ },
413
+ ];
414
+ // ─── Toys gates (Reg (EU) 2025/2509) ─────────────────────────────────────────
415
+ const TOYS_FIELD_GATES = [
416
+ {
417
+ // Annex VI Part I(l): a list of fragrances to which the toy may expose users
418
+ // is required only when those fragrances are listed under Annex II of
419
+ // Regulation (EC) No 1223/2009 (cosmetics allergens). If no allergen-listed
420
+ // fragrances are present the duty does not arise.
421
+ keys: ["allergenicFragrances"],
422
+ article: "Annex VI Part I(l) / Reg (EU) 2025/2509",
423
+ reason: "A list of allergen-listed fragrances is required in the toy passport only when such fragrances are present in the toy.",
424
+ mandatoryFrom: TOYS_PASSPORT_MANDATORY_FROM,
425
+ decide: (t) => {
426
+ if (t.containsAllergenicFragrances === undefined)
427
+ return "unknown";
428
+ return t.containsAllergenicFragrances ? "applies" : "not_applicable";
429
+ },
430
+ },
431
+ {
432
+ // Annex VI Part I(h): a statement about which other Union acts apply to the
433
+ // toy is required only where the toy also falls under one of the acts listed
434
+ // in Annex XI to the Regulation, or under Directive 2011/65/EU (RoHS).
435
+ keys: ["replacesDeclarationOfConformity"],
436
+ article: "Annex VI Part I(h) / Reg (EU) 2025/2509",
437
+ reason: "Information on compliance with other applicable Union acts (Annex XI / RoHS) is required only when the toy falls under one of those acts.",
438
+ mandatoryFrom: TOYS_PASSPORT_MANDATORY_FROM,
439
+ decide: (t) => {
440
+ if (t.alsoUnderOtherActs === undefined)
441
+ return "unknown";
442
+ return t.alsoUnderOtherActs ? "applies" : "not_applicable";
443
+ },
444
+ },
445
+ {
446
+ // Annex VI Part I(j): the notified-body certificate reference is required only
447
+ // when a notified body was involved (i.e. the `notifiedBody` field is filled).
448
+ // This is field-derived (no separate flag): an approved non-empty `notifiedBody`
449
+ // → certificate reference applies; absent/empty → not_applicable.
450
+ keys: ["notifiedBodyCertificateReference"],
451
+ article: "Annex VI Part I(j) / Reg (EU) 2025/2509",
452
+ reason: "The notified-body certificate reference is required only when a notified body was involved in the conformity assessment (indicated by the notifiedBody field being filled).",
453
+ mandatoryFrom: TOYS_PASSPORT_MANDATORY_FROM,
454
+ decide: (t) => {
455
+ // fieldPresent is optional in Triggers (backward compat); treat absent as false.
456
+ const present = t.fieldPresent ? t.fieldPresent("notifiedBody") : false;
457
+ if (present === undefined)
458
+ return "unknown"; // pending_review → warn, no block
459
+ return present ? "applies" : "not_applicable";
460
+ },
461
+ },
462
+ ];
463
+ /**
464
+ * CATEGORY_FIELD_GATES — generalised gate registry, keyed by template category.
465
+ *
466
+ * Phase 1: battery only. Phase 2: detergents and toys added.
467
+ *
468
+ * Callers that need to enumerate gates for a category use this registry.
469
+ * The battery-specific `batteryFieldApplicability` is now a thin wrapper.
470
+ */
471
+ export const CATEGORY_FIELD_GATES = {
472
+ battery: BATTERY_FIELD_GATES,
473
+ detergents: DETERGENTS_FIELD_GATES,
474
+ toys: TOYS_FIELD_GATES,
475
+ };
476
+ /**
477
+ * Compute the applicability map for all gated fields of any category.
478
+ * Returns {} for categories with no gates (safe to iterate).
479
+ *
480
+ * Replaces the battery-only early return in `batteryFieldApplicability` with a
481
+ * generic path: every category that has gates in CATEGORY_FIELD_GATES is handled,
482
+ * everything else is a no-op. `batteryFieldApplicability` is kept as a
483
+ * backward-compat alias.
484
+ *
485
+ * @param now Optional reference date for `mandatoryFrom` enforcement. Defaults
486
+ * to the current wall clock. Callers should pass a fixed date in tests.
487
+ * When a gate's `mandatoryFrom` is in the future relative to `now`,
488
+ * ALL keys governed by that gate resolve to "unknown" (warning only,
489
+ * never a hard publish block) — keeping the provision from creating
490
+ * obligations before it legally applies.
491
+ */
492
+ export function categoryFieldApplicability(passport, category, now) {
493
+ const gates = CATEGORY_FIELD_GATES[category];
494
+ if (!gates || gates.length === 0)
495
+ return {};
496
+ const triggers = readTriggers(passport, category);
497
+ const effectiveNow = now ?? new Date();
498
+ const out = {};
499
+ for (const gate of gates) {
500
+ // Date-gate: if the provision has not yet entered into force, return "unknown"
501
+ // for every field in this gate — never a hard block before the law applies.
502
+ if (gate.mandatoryFrom && effectiveNow < new Date(gate.mandatoryFrom)) {
503
+ for (const k of gate.keys)
504
+ out[k] = "unknown";
505
+ continue;
506
+ }
507
+ const verdict = gate.decide(triggers);
508
+ for (const k of gate.keys)
509
+ out[k] = verdict;
510
+ }
511
+ return out;
512
+ }
513
+ //# sourceMappingURL=battery-applicability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery-applicability.js","sourceRoot":"","sources":["../../src/vendor/battery-applicability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Generalised condition-profile accessor.
3
+ *
4
+ * `conditionProfile` (the new field) wins when present; falls back to the
5
+ * legacy `batteryProfile` block, adapting it to the `ConditionFlag` shape
6
+ * (audit defaults to []). This mirrors the `gs1` → `identifier` precedence
7
+ * pattern: old data stays readable without a migration, and new writes land
8
+ * in the canonical location.
9
+ *
10
+ * **Use `resolveConditionProfile` everywhere** instead of reading
11
+ * `passport.batteryProfile` directly — this is the single source of truth
12
+ * for "what condition flags has a reviewer set?".
13
+ *
14
+ * Pure + IO-free → unit-tested in tests/compliance/condition-profile.test.ts.
15
+ */
16
+ import type { Passport, ConditionProfile } from "@tracepass/dpp-types";
17
+ /**
18
+ * Return the effective condition profile for a passport, regardless of which
19
+ * storage field holds the data.
20
+ *
21
+ * - If `passport.conditionProfile` is present (even as `{}`), return it as-is.
22
+ * An empty conditionProfile means "all flags cleared" — it must NOT fall back
23
+ * to batteryProfile, or clearing the last flag would resurrect legacy data.
24
+ * - Otherwise, adapt `passport.batteryProfile` (the legacy shape, which lacks
25
+ * per-flag audit arrays) to the `ConditionProfile` shape with `audit: []`.
26
+ * - If neither is set, return an empty object.
27
+ */
28
+ export declare function resolveConditionProfile(passport: Pick<Passport, "conditionProfile" | "batteryProfile">): ConditionProfile;
29
+ //# sourceMappingURL=condition-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition-profile.d.ts","sourceRoot":"","sources":["../../src/vendor/condition-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEvE;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,kBAAkB,GAAG,gBAAgB,CAAC,GAAG,gBAAgB,CAkBzH"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Generalised condition-profile accessor.
3
+ *
4
+ * `conditionProfile` (the new field) wins when present; falls back to the
5
+ * legacy `batteryProfile` block, adapting it to the `ConditionFlag` shape
6
+ * (audit defaults to []). This mirrors the `gs1` → `identifier` precedence
7
+ * pattern: old data stays readable without a migration, and new writes land
8
+ * in the canonical location.
9
+ *
10
+ * **Use `resolveConditionProfile` everywhere** instead of reading
11
+ * `passport.batteryProfile` directly — this is the single source of truth
12
+ * for "what condition flags has a reviewer set?".
13
+ *
14
+ * Pure + IO-free → unit-tested in tests/compliance/condition-profile.test.ts.
15
+ */
16
+ /**
17
+ * Return the effective condition profile for a passport, regardless of which
18
+ * storage field holds the data.
19
+ *
20
+ * - If `passport.conditionProfile` is present (even as `{}`), return it as-is.
21
+ * An empty conditionProfile means "all flags cleared" — it must NOT fall back
22
+ * to batteryProfile, or clearing the last flag would resurrect legacy data.
23
+ * - Otherwise, adapt `passport.batteryProfile` (the legacy shape, which lacks
24
+ * per-flag audit arrays) to the `ConditionProfile` shape with `audit: []`.
25
+ * - If neither is set, return an empty object.
26
+ */
27
+ export function resolveConditionProfile(passport) {
28
+ if (passport.conditionProfile !== undefined) {
29
+ return passport.conditionProfile;
30
+ }
31
+ const bp = passport.batteryProfile;
32
+ if (!bp)
33
+ return {};
34
+ const out = {};
35
+ for (const [key, flag] of Object.entries(bp)) {
36
+ if (flag != null) {
37
+ out[key] = {
38
+ value: flag.value,
39
+ status: flag.status,
40
+ source: flag.source,
41
+ audit: [],
42
+ };
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+ //# sourceMappingURL=condition-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition-profile.js","sourceRoot":"","sources":["../../src/vendor/condition-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAA+D;IACrG,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,QAAQ,CAAC,gBAAgB,CAAC;IACnC,CAAC;IACD,MAAM,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC;IACnC,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACnB,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAA2C,EAAE,CAAC;QACvF,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,GAAG,CAAC,GAAG,CAAC,GAAG;gBACT,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,EAAE;aACV,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -19,24 +19,35 @@
19
19
  * and commit `mirror-manifest.json` with the code.
20
20
  */
21
21
  import type { Passport, Template, TemplateField } from "@tracepass/dpp-types";
22
+ /** Applicability value returned by the condition-gate engine. */
23
+ export type GateApplicability = "applies" | "not_applicable" | "unknown";
22
24
  export interface PublishCheck {
23
25
  ready: boolean;
24
26
  /** Field keys that have no value set. */
25
27
  missingFields: string[];
26
28
  /** Field keys with a value but status != approved. */
27
29
  unapprovedFields: string[];
30
+ /**
31
+ * Field keys with `requiredBy[category] === "conditional"` whose gate
32
+ * resolved `"applies"` (from an APPROVED flag) and the field is empty.
33
+ * This is a HARD publish block — stricter than a soft gap, because the
34
+ * owner has confirmed the condition holds.
35
+ */
36
+ conditionalMissingFields: string[];
28
37
  /** Short human reason suitable for a tooltip or toast — undefined when ready. */
29
38
  reason?: string;
30
39
  /**
31
40
  * Distinguishes WHY the passport isn't ready:
32
- * - "hard" → structural blocker that can't be bypassed
33
- * (already-published, wrong status, template missing).
34
- * - "gap" → required fields are missing or unapproved. The UI
35
- * can offer to publish anyway with an explicit "I
36
- * understand the risk" acknowledgement.
41
+ * - "hard" → structural blocker that can't be bypassed
42
+ * (already-published, wrong status, template missing).
43
+ * - "gap" → required fields are missing or unapproved. The UI
44
+ * can offer to publish anyway with an explicit "I
45
+ * understand the risk" acknowledgement.
46
+ * - "conditional" → a condition-flag gate confirmed a duty applies and
47
+ * the field is empty. Hard block, same as "hard".
37
48
  * - `undefined` when ready === true.
38
49
  */
39
- blockerType?: "hard" | "gap";
50
+ blockerType?: "hard" | "gap" | "conditional";
40
51
  }
41
52
  /**
42
53
  * Resolves whether a template field is effectively required for a specific
@@ -100,6 +111,31 @@ export declare function effectiveRequiredFor(templateCategory: string, tf: Pick<
100
111
  * absence of information is never grounds for asserting non-applicability.
101
112
  */
102
113
  export declare function notApplicableForCategory(tf: Pick<TemplateField, "validation">, category: string | undefined): boolean;
114
+ /** The field-level findings returned by `evaluateFieldRequirements`. */
115
+ export interface FieldRequirements {
116
+ missingFields: string[];
117
+ unapprovedFields: string[];
118
+ conditionalMissingFields: string[];
119
+ }
120
+ /**
121
+ * Evaluate field-level requirements for a passport against its template,
122
+ * status-independently. Called by both `checkPublishReady` (which adds
123
+ * status early-returns) and `evaluateCompliance` (which must run even on
124
+ * published passports so live DPPs are re-evaluated after a template or
125
+ * flag change).
126
+ *
127
+ * Returns three buckets:
128
+ * - `missingFields` — statically required, no value set
129
+ * - `unapprovedFields` — statically required, value set but not approved
130
+ * - `conditionalMissingFields` — gate-confirmed conditional duty, absent
131
+ * OR present-but-unapproved (hard block)
132
+ *
133
+ * `applicabilityMap` is pre-computed by the caller via
134
+ * `categoryFieldApplicability(passport, template.category)`. Pass `{}` for
135
+ * non-battery categories — the gate branch is then a no-op. Keeping it
136
+ * injected preserves the IO-free/pure contract of this function.
137
+ */
138
+ export declare function evaluateFieldRequirements(passport: Passport, template: Template, applicabilityMap?: Record<string, GateApplicability>): FieldRequirements;
103
139
  /**
104
140
  * Returns whether `passport` satisfies every publish precondition given
105
141
  * its `template`. A passport is publish-ready iff:
@@ -107,9 +143,19 @@ export declare function notApplicableForCategory(tf: Pick<TemplateField, "valida
107
143
  * states return ready:false with a specific reason),
108
144
  * - every template field whose effective required flag (resolved via
109
145
  * `effectiveRequired` — which consults `requiredBy` when present) is
110
- * true has a non-empty value AND status === "approved".
146
+ * true has a non-empty value AND status === "approved",
147
+ * - every `requiredBy[category] === "conditional"` field whose gate
148
+ * resolves `"applies"` (from an APPROVED condition flag) also has a
149
+ * non-empty value AND status === "approved" (hard block, same severity
150
+ * as a structural blocker).
111
151
  *
112
152
  * `undefined` template means we can't evaluate — treat as not-ready.
153
+ *
154
+ * @param applicabilityMap Pre-computed gate-applicability map (field key →
155
+ * "applies" | "not_applicable" | "unknown"). Computed by the caller using
156
+ * `categoryFieldApplicability(passport, template.category)`. Pass `{}` or
157
+ * omit for non-battery passports (no change in behaviour). Keeping it
158
+ * injected preserves the IO-free/pure contract of this function.
113
159
  */
114
- export declare function checkPublishReady(passport: Passport, template: Template | undefined): PublishCheck;
160
+ export declare function checkPublishReady(passport: Passport, template: Template | undefined, applicabilityMap?: Record<string, GateApplicability>): PublishCheck;
115
161
  //# sourceMappingURL=publish-gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"publish-gate.d.ts","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI9E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAQD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAUT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,EACxB,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAIT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAIT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAC7B,YAAY,CAyEd"}
1
+ {"version":3,"file":"publish-gate.d.ts","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI9E,iEAAiE;AACjE,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;OAKG;IACH,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,aAAa,CAAC;CAC9C;AAQD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAUT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,EACxB,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAIT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAIT;AAED,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,wBAAwB,EAAE,MAAM,EAAE,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,GAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAM,GACvD,iBAAiB,CAmEnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,gBAAgB,GAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAM,GACvD,YAAY,CAoEd"}
@@ -110,6 +110,87 @@ export function notApplicableForCategory(tf, category) {
110
110
  return false;
111
111
  return requiredBy[category] === "notApplicable";
112
112
  }
113
+ /**
114
+ * Evaluate field-level requirements for a passport against its template,
115
+ * status-independently. Called by both `checkPublishReady` (which adds
116
+ * status early-returns) and `evaluateCompliance` (which must run even on
117
+ * published passports so live DPPs are re-evaluated after a template or
118
+ * flag change).
119
+ *
120
+ * Returns three buckets:
121
+ * - `missingFields` — statically required, no value set
122
+ * - `unapprovedFields` — statically required, value set but not approved
123
+ * - `conditionalMissingFields` — gate-confirmed conditional duty, absent
124
+ * OR present-but-unapproved (hard block)
125
+ *
126
+ * `applicabilityMap` is pre-computed by the caller via
127
+ * `categoryFieldApplicability(passport, template.category)`. Pass `{}` for
128
+ * non-battery categories — the gate branch is then a no-op. Keeping it
129
+ * injected preserves the IO-free/pure contract of this function.
130
+ */
131
+ export function evaluateFieldRequirements(passport, template, applicabilityMap = {}) {
132
+ // The passport's sub-category, read from the field the template category
133
+ // names (battery: batteryCategory, fmcg: productSubcategory, detergents:
134
+ // detergentUserType; core SUBCATEGORY_FIELD), so effectiveRequired can
135
+ // resolve `requiredBy`. Unset → undefined → the plain `required` flag.
136
+ const category = passportSubcategory(template.category, passport.fields);
137
+ const missingFields = [];
138
+ const unapprovedFields = [];
139
+ const conditionalMissingFields = [];
140
+ for (const tf of template.fields) {
141
+ // `anticipated` fields rest on a delegated act that has not been adopted,
142
+ // so no instrument in force compels the data and their absence cannot block
143
+ // publication. Skip them before the required check.
144
+ if (tf.validation.anticipated)
145
+ continue;
146
+ const f = passport.fields[tf.key];
147
+ // ── Gate-aware conditional check ────────────────────────────────────────
148
+ // A field with requiredBy[category] === "conditional" is normally not
149
+ // required. When a gate's APPROVED flag confirms the condition applies,
150
+ // the field becomes required and its absence is a HARD publish block.
151
+ // An unapproved value (pending_review) is also a hard block — consistent
152
+ // with the owner's decision that conditional duties are non-bypassable.
153
+ // "unknown" (absent or pending_review FLAG) → skip here; the rules engine
154
+ // emits an unverifiable_conditional warning via BAT-APP but never blocks.
155
+ const rb = tf.validation.requiredBy;
156
+ if (applicabilityMap[tf.key] === "applies" &&
157
+ (
158
+ // Path 1: field has a per-subcategory requiredBy map and the current
159
+ // subcategory is "conditional" — the gate's "applies" verdict promotes
160
+ // the conditional entry to a hard block (battery, detergents pattern).
161
+ (rb && category !== undefined && rb[category] === "conditional") ||
162
+ // Path 2: no requiredBy map at all and field is not statically required.
163
+ // Used for categories whose gated fields carry no subcategory (toys) —
164
+ // the gate alone decides whether the duty arises; when it says "applies"
165
+ // the field becomes mandatory regardless of the subcategory value.
166
+ (!tf.validation.required && !rb))) {
167
+ if (isFieldAbsent(f)) {
168
+ conditionalMissingFields.push(tf.key);
169
+ }
170
+ else if (f.status !== "approved") {
171
+ // Field is present but not yet confirmed — same hard block as absent,
172
+ // because an unreviewed value satisfies neither the owner nor the law.
173
+ conditionalMissingFields.push(tf.key);
174
+ }
175
+ // Don't fall through to the static required check — the gate owns this
176
+ // field's requirement for this passport.
177
+ continue;
178
+ }
179
+ // ── Static required check ────────────────────────────────────────────────
180
+ if (!effectiveRequiredFor(template.category, tf, category))
181
+ continue;
182
+ // Shared helper: `[]` stays a valid answer ("none apply"), while a
183
+ // whitespace-only string now counts as missing — the inline `=== ""` it
184
+ // replaces let " " satisfy a required field and publish a blank.
185
+ if (isFieldAbsent(f)) {
186
+ missingFields.push(tf.key);
187
+ }
188
+ else if (f.status !== "approved") {
189
+ unapprovedFields.push(tf.key);
190
+ }
191
+ }
192
+ return { missingFields, unapprovedFields, conditionalMissingFields };
193
+ }
113
194
  /**
114
195
  * Returns whether `passport` satisfies every publish precondition given
115
196
  * its `template`. A passport is publish-ready iff:
@@ -117,16 +198,27 @@ export function notApplicableForCategory(tf, category) {
117
198
  * states return ready:false with a specific reason),
118
199
  * - every template field whose effective required flag (resolved via
119
200
  * `effectiveRequired` — which consults `requiredBy` when present) is
120
- * true has a non-empty value AND status === "approved".
201
+ * true has a non-empty value AND status === "approved",
202
+ * - every `requiredBy[category] === "conditional"` field whose gate
203
+ * resolves `"applies"` (from an APPROVED condition flag) also has a
204
+ * non-empty value AND status === "approved" (hard block, same severity
205
+ * as a structural blocker).
121
206
  *
122
207
  * `undefined` template means we can't evaluate — treat as not-ready.
208
+ *
209
+ * @param applicabilityMap Pre-computed gate-applicability map (field key →
210
+ * "applies" | "not_applicable" | "unknown"). Computed by the caller using
211
+ * `categoryFieldApplicability(passport, template.category)`. Pass `{}` or
212
+ * omit for non-battery passports (no change in behaviour). Keeping it
213
+ * injected preserves the IO-free/pure contract of this function.
123
214
  */
124
- export function checkPublishReady(passport, template) {
215
+ export function checkPublishReady(passport, template, applicabilityMap = {}) {
125
216
  if (passport.status === "published") {
126
217
  return {
127
218
  ready: false,
128
219
  missingFields: [],
129
220
  unapprovedFields: [],
221
+ conditionalMissingFields: [],
130
222
  reason: "Already published",
131
223
  blockerType: "hard",
132
224
  };
@@ -136,6 +228,7 @@ export function checkPublishReady(passport, template) {
136
228
  ready: false,
137
229
  missingFields: [],
138
230
  unapprovedFields: [],
231
+ conditionalMissingFields: [],
139
232
  reason: `Cannot publish from status ${passport.status}`,
140
233
  blockerType: "hard",
141
234
  };
@@ -145,50 +238,38 @@ export function checkPublishReady(passport, template) {
145
238
  ready: false,
146
239
  missingFields: [],
147
240
  unapprovedFields: [],
241
+ conditionalMissingFields: [],
148
242
  reason: "Template missing",
149
243
  blockerType: "hard",
150
244
  };
151
245
  }
152
- // The passport's sub-category, read from the field the template category
153
- // names (battery: batteryCategory, fmcg: productSubcategory, detergents:
154
- // detergentUserType; core SUBCATEGORY_FIELD), so effectiveRequired can
155
- // resolve `requiredBy`. Unset → undefined → the plain `required` flag.
156
- const category = passportSubcategory(template.category, passport.fields);
157
- const missingFields = [];
158
- const unapprovedFields = [];
159
- for (const tf of template.fields) {
160
- // `anticipated` fields rest on a delegated act that has not been adopted,
161
- // so no instrument in force compels the data and their absence cannot block
162
- // publication. Skip them before the required check.
163
- if (tf.validation.anticipated)
164
- continue;
165
- if (!effectiveRequiredFor(template.category, tf, category))
166
- continue;
167
- const f = passport.fields[tf.key];
168
- // Shared helper: `[]` stays a valid answer ("none apply"), while a
169
- // whitespace-only string now counts as missing — the inline `=== ""` it
170
- // replaces let " " satisfy a required field and publish a blank.
171
- if (isFieldAbsent(f)) {
172
- missingFields.push(tf.key);
173
- }
174
- else if (f.status !== "approved") {
175
- unapprovedFields.push(tf.key);
176
- }
177
- }
178
- if (missingFields.length === 0 && unapprovedFields.length === 0) {
179
- return { ready: true, missingFields: [], unapprovedFields: [] };
246
+ const { missingFields, unapprovedFields, conditionalMissingFields } = evaluateFieldRequirements(passport, template, applicabilityMap);
247
+ if (missingFields.length === 0 &&
248
+ unapprovedFields.length === 0 &&
249
+ conditionalMissingFields.length === 0) {
250
+ return {
251
+ ready: true,
252
+ missingFields: [],
253
+ unapprovedFields: [],
254
+ conditionalMissingFields: [],
255
+ };
180
256
  }
181
257
  const parts = [];
258
+ if (conditionalMissingFields.length > 0)
259
+ parts.push(`${conditionalMissingFields.length} conditional-required missing`);
182
260
  if (missingFields.length > 0)
183
261
  parts.push(`${missingFields.length} missing`);
184
262
  if (unapprovedFields.length > 0)
185
263
  parts.push(`${unapprovedFields.length} not approved`);
264
+ // Hard block when any conditional-required field is missing; soft gap otherwise.
265
+ const blockerType = conditionalMissingFields.length > 0 ? "conditional" : "gap";
186
266
  return {
187
267
  ready: false,
188
268
  missingFields,
189
269
  unapprovedFields,
270
+ conditionalMissingFields,
190
271
  reason: `Required fields: ${parts.join(", ")}`,
191
- blockerType: "gap",
272
+ blockerType,
192
273
  };
193
274
  }
194
275
  //# sourceMappingURL=publish-gate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"publish-gate.js","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"publish-gate.js","sourceRoot":"","sources":["../../src/vendor/publish-gate.ts"],"names":[],"mappings":"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"}
package/dist/verdict.d.ts CHANGED
@@ -9,8 +9,9 @@
9
9
  * Pure and IO-free: no database, no network, no clock.
10
10
  *
11
11
  * Composition:
12
- * static tier — required fields (checkPublishReady) + required
13
- * economic operators (required-roles) + format validation
12
+ * static tier — evaluateFieldRequirements (required fields,
13
+ * status-independent) + required economic operators
14
+ * (required-roles) + format validation
14
15
  * conditional tier — CONDITIONAL_RULES[category] + CROSS_CUTTING_RULES
15
16
  * coverage — "evaluated" if the category has conditional rules,
16
17
  * else "static-only"
@@ -1 +1 @@
1
- {"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAiB,MAAM,sBAAsB,CAAC;AAK9E,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAiEpB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM;AAChB,kFAAkF;AAClF,GAAG,GAAE,IAAiB,GACrB,gBAAgB,CA2GlB"}
1
+ {"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAiB,MAAM,sBAAsB,CAAC;AAM9E,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAiEpB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM;AAChB,kFAAkF;AAClF,GAAG,GAAE,IAAiB,GACrB,gBAAgB,CA6IlB"}
package/dist/verdict.js CHANGED
@@ -9,8 +9,9 @@
9
9
  * Pure and IO-free: no database, no network, no clock.
10
10
  *
11
11
  * Composition:
12
- * static tier — required fields (checkPublishReady) + required
13
- * economic operators (required-roles) + format validation
12
+ * static tier — evaluateFieldRequirements (required fields,
13
+ * status-independent) + required economic operators
14
+ * (required-roles) + format validation
14
15
  * conditional tier — CONDITIONAL_RULES[category] + CROSS_CUTTING_RULES
15
16
  * coverage — "evaluated" if the category has conditional rules,
16
17
  * else "static-only"
@@ -18,9 +19,10 @@
18
19
  * Each finding cites the article of the instrument that mandates it, taken
19
20
  * from the template field's own `regulationRef`.
20
21
  */
21
- import { checkPublishReady } from "./vendor/publish-gate.js";
22
+ import { evaluateFieldRequirements } from "./vendor/publish-gate.js";
22
23
  import { getPartyRoles } from "./vendor/required-roles.js";
23
24
  import { derivePassportCounts } from "./vendor/counts.js";
25
+ import { categoryFieldApplicability } from "./vendor/battery-applicability.js";
24
26
  import { CONDITIONAL_RULES, CROSS_CUTTING_RULES } from "./rules.js";
25
27
  /** Validate a filled value against a template field's format rules.
26
28
  * Returns a finding (warning) on mismatch, or null when clean. Empty
@@ -113,11 +115,17 @@ now = new Date()) {
113
115
  const critical = [];
114
116
  const warnings = [];
115
117
  const checkedRules = [];
116
- // ── Static tier 1: required fields (reuse the publish gate) ────────
118
+ // ── Static tier 1: required fields ─────────────────────────────────
117
119
  checkedRules.push("static:required-fields");
118
- const publish = checkPublishReady(passport, template);
120
+ // Compute the gate-applicability map for conditional duties whose APPROVED
121
+ // flag confirms the condition holds. evaluateFieldRequirements is called
122
+ // directly (not via checkPublishReady) so published passports are not
123
+ // short-circuited — live DPPs must be re-evaluated immediately when a
124
+ // field or flag changes.
125
+ const applicabilityMap = categoryFieldApplicability(passport, category);
126
+ const { missingFields, unapprovedFields, conditionalMissingFields } = evaluateFieldRequirements(passport, template, applicabilityMap);
119
127
  const fieldRef = (key) => template.fields.find((f) => f.key === key)?.regulationRef;
120
- for (const key of publish.missingFields) {
128
+ for (const key of missingFields) {
121
129
  const ref = fieldRef(key);
122
130
  critical.push({
123
131
  type: "missing_field",
@@ -129,7 +137,7 @@ now = new Date()) {
129
137
  fix: `Provide a value for ${key}.`,
130
138
  });
131
139
  }
132
- for (const key of publish.unapprovedFields) {
140
+ for (const key of unapprovedFields) {
133
141
  critical.push({
134
142
  type: "unapproved_field",
135
143
  severity: "critical",
@@ -138,6 +146,33 @@ now = new Date()) {
138
146
  fix: `Review and approve ${key}.`,
139
147
  });
140
148
  }
149
+ // Gate-confirmed conditional duties: the APPROVED flag makes the duty concrete
150
+ // and the field must be present + approved. Same hard-block severity as a
151
+ // statically-required field — the owner confirmed the condition holds.
152
+ // "gate:conditional" is pushed when the category has gates (non-empty
153
+ // applicabilityMap) so the caller knows whether gates were evaluated.
154
+ if (Object.keys(applicabilityMap).length > 0) {
155
+ checkedRules.push("gate:conditional");
156
+ }
157
+ for (const key of conditionalMissingFields) {
158
+ const ref = fieldRef(key);
159
+ const f = passport.fields[key];
160
+ const isAbsent = f == null || f.value === null || f.value === undefined || f.value === "";
161
+ critical.push({
162
+ type: "missing_field",
163
+ severity: "critical",
164
+ ruleId: "gate:conditional",
165
+ target: key,
166
+ regulation: ref ? template.regulation?.number : undefined,
167
+ article: ref?.article ?? undefined,
168
+ why: isAbsent
169
+ ? `Conditional field "${key}" is required because an approved condition flag confirms the duty applies, but the field has no value.`
170
+ : `Conditional field "${key}" is required because an approved condition flag confirms the duty applies, but the value is not yet approved.`,
171
+ fix: isAbsent
172
+ ? `Provide and approve a value for ${key}.`
173
+ : `Approve the value for ${key}.`,
174
+ });
175
+ }
141
176
  // ── Static tier 2: required economic-operator parties ──────────────
142
177
  checkedRules.push("static:required-parties");
143
178
  const roles = getPartyRoles(category);
@@ -1 +1 @@
1
- {"version":3,"file":"verdict.js","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verdict.js","sourceRoot":"","sources":["../src/verdict.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tracepass/dpp-validate",
3
- "version": "0.12.0",
4
- "description": "Evaluate an EU Digital Product Passport against its category field-spec — a three-tier compliance verdict with regulation-cited findings. Pure functions, no third-party runtime dependencies.",
3
+ "version": "0.14.0",
4
+ "description": "Evaluate an EU Digital Product Passport against its category field-spec \u2014 a three-tier compliance verdict with regulation-cited findings. Pure functions, no third-party runtime dependencies.",
5
5
  "keywords": [
6
6
  "digital-product-passport",
7
7
  "dpp",
@@ -45,7 +45,7 @@
45
45
  "clean": "rm -rf dist"
46
46
  },
47
47
  "dependencies": {
48
- "@tracepass/dpp-types": "^0.12.0"
48
+ "@tracepass/dpp-types": "^0.14.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "typescript": "^5.6.3"