@primmel/primmel 1.11.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/check-rules.d.ts.map +1 -1
- package/dist/src/check-rules.js +71 -0
- package/dist/src/check-rules.js.map +1 -1
- package/dist/src/check.d.ts.map +1 -1
- package/dist/src/check.js +457 -3
- package/dist/src/check.js.map +1 -1
- package/dist/src/model-diff.d.ts.map +1 -1
- package/dist/src/model-diff.js +22 -0
- package/dist/src/model-diff.js.map +1 -1
- package/dist/src/ser-des/config/calculation.d.ts.map +1 -1
- package/dist/src/ser-des/config/calculation.js +134 -18
- package/dist/src/ser-des/config/calculation.js.map +1 -1
- package/dist/src/ser-des/config/commonTestCondition.d.ts +5 -0
- package/dist/src/ser-des/config/commonTestCondition.d.ts.map +1 -0
- package/dist/src/ser-des/config/commonTestCondition.js +75 -0
- package/dist/src/ser-des/config/commonTestCondition.js.map +1 -0
- package/dist/src/ser-des/config/documentModule.d.ts +5 -0
- package/dist/src/ser-des/config/documentModule.d.ts.map +1 -0
- package/dist/src/ser-des/config/documentModule.js +109 -0
- package/dist/src/ser-des/config/documentModule.js.map +1 -0
- package/dist/src/ser-des/config/formulaNote.d.ts +5 -0
- package/dist/src/ser-des/config/formulaNote.d.ts.map +1 -0
- package/dist/src/ser-des/config/formulaNote.js +66 -0
- package/dist/src/ser-des/config/formulaNote.js.map +1 -0
- package/dist/src/ser-des/config/index.d.ts.map +1 -1
- package/dist/src/ser-des/config/index.js +98 -0
- package/dist/src/ser-des/config/index.js.map +1 -1
- package/dist/src/ser-des/config/informativeAnnex.d.ts +5 -0
- package/dist/src/ser-des/config/informativeAnnex.d.ts.map +1 -0
- package/dist/src/ser-des/config/informativeAnnex.js +185 -0
- package/dist/src/ser-des/config/informativeAnnex.js.map +1 -0
- package/dist/src/ser-des/config/partAnnex.d.ts +5 -0
- package/dist/src/ser-des/config/partAnnex.d.ts.map +1 -0
- package/dist/src/ser-des/config/partAnnex.js +91 -0
- package/dist/src/ser-des/config/partAnnex.js.map +1 -0
- package/dist/src/ser-des/config/process.d.ts.map +1 -1
- package/dist/src/ser-des/config/process.js +369 -3
- package/dist/src/ser-des/config/process.js.map +1 -1
- package/dist/src/ser-des/config/processModel.d.ts +13 -0
- package/dist/src/ser-des/config/processModel.d.ts.map +1 -0
- package/dist/src/ser-des/config/processModel.js +163 -0
- package/dist/src/ser-des/config/processModel.js.map +1 -0
- package/dist/src/ser-des/config/schemeType.d.ts +7 -0
- package/dist/src/ser-des/config/schemeType.d.ts.map +1 -0
- package/dist/src/ser-des/config/schemeType.js +232 -0
- package/dist/src/ser-des/config/schemeType.js.map +1 -0
- package/dist/src/ser-des/config/storyline.d.ts +7 -0
- package/dist/src/ser-des/config/storyline.d.ts.map +1 -0
- package/dist/src/ser-des/config/storyline.js +264 -0
- package/dist/src/ser-des/config/storyline.js.map +1 -0
- package/dist/src/ser-des/config/subject.d.ts.map +1 -1
- package/dist/src/ser-des/config/subject.js +93 -0
- package/dist/src/ser-des/config/subject.js.map +1 -1
- package/dist/src/ser-des/package.d.ts.map +1 -1
- package/dist/src/ser-des/package.js +36 -8
- package/dist/src/ser-des/package.js.map +1 -1
- package/dist/src/ser-des/parse.d.ts.map +1 -1
- package/dist/src/ser-des/parse.js +13 -0
- package/dist/src/ser-des/parse.js.map +1 -1
- package/dist/src/ser-des/types.d.ts +22 -0
- package/dist/src/ser-des/types.d.ts.map +1 -1
- package/dist/src/types/Calculation.d.ts +35 -0
- package/dist/src/types/Calculation.d.ts.map +1 -1
- package/dist/src/types/CommonTestCondition.d.ts +15 -0
- package/dist/src/types/CommonTestCondition.d.ts.map +1 -0
- package/dist/src/types/CommonTestCondition.js +22 -0
- package/dist/src/types/CommonTestCondition.js.map +1 -0
- package/dist/src/types/DocumentModule.d.ts +27 -0
- package/dist/src/types/DocumentModule.d.ts.map +1 -0
- package/dist/src/types/DocumentModule.js +38 -0
- package/dist/src/types/DocumentModule.js.map +1 -0
- package/dist/src/types/InformativeAnnex.d.ts +44 -0
- package/dist/src/types/InformativeAnnex.d.ts.map +1 -0
- package/dist/src/types/InformativeAnnex.js +35 -0
- package/dist/src/types/InformativeAnnex.js.map +1 -0
- package/dist/src/types/Package.d.ts +7 -3
- package/dist/src/types/Package.d.ts.map +1 -1
- package/dist/src/types/PartAnnex.d.ts +18 -0
- package/dist/src/types/PartAnnex.d.ts.map +1 -0
- package/dist/src/types/PartAnnex.js +29 -0
- package/dist/src/types/PartAnnex.js.map +1 -0
- package/dist/src/types/ProcessModel.d.ts +27 -0
- package/dist/src/types/ProcessModel.d.ts.map +1 -0
- package/dist/src/types/ProcessModel.js +31 -0
- package/dist/src/types/ProcessModel.js.map +1 -0
- package/dist/src/types/SchemeType.d.ts +51 -0
- package/dist/src/types/SchemeType.d.ts.map +1 -0
- package/dist/src/types/SchemeType.js +41 -0
- package/dist/src/types/SchemeType.js.map +1 -0
- package/dist/src/types/Standard.d.ts +36 -0
- package/dist/src/types/Standard.d.ts.map +1 -1
- package/dist/src/types/Storyline.d.ts +62 -0
- package/dist/src/types/Storyline.d.ts.map +1 -0
- package/dist/src/types/Storyline.js +51 -0
- package/dist/src/types/Storyline.js.map +1 -0
- package/dist/src/types/Subject.d.ts +33 -0
- package/dist/src/types/Subject.d.ts.map +1 -1
- package/dist/src/types/Symbol.d.ts +17 -0
- package/dist/src/types/Symbol.d.ts.map +1 -1
- package/dist/src/types/process.d.ts +99 -1
- package/dist/src/types/process.d.ts.map +1 -1
- package/dist-browser/index.mjs +1791 -988
- package/dist-cli/primmel.mjs +2131 -56
- package/package.json +1 -1
package/dist/src/check.js
CHANGED
|
@@ -152,7 +152,10 @@
|
|
|
152
152
|
// (TODO.roadmap/39b — ISO/IEC 17065 role segregation) are
|
|
153
153
|
// well-formed: pair members resolve to declared processes (or the
|
|
154
154
|
// reserved case_personnel token), are distinct, and include the
|
|
155
|
-
// owning process
|
|
155
|
+
// owning process; the kind-conditional XOR (smart TODO.roadmap/40
|
|
156
|
+
// batch 2): a disjoint constraint is ONLY a pair, a consultancy
|
|
157
|
+
// bar carries barred relations (required) + an optional period and
|
|
158
|
+
// never a pair
|
|
156
159
|
// C74 process-io-type-coherence (TODO.roadmap/38): typed transition
|
|
157
160
|
// boundaries — one name (signature in/out, registers) carries ONE
|
|
158
161
|
// type; two declarations of the same name whose quantity
|
|
@@ -311,6 +314,55 @@
|
|
|
311
314
|
// document approving organs, decision_rule edges. Per-register
|
|
312
315
|
// gating (the C58 doctrine): an edge is checked only when its
|
|
313
316
|
// target register is in composition scope
|
|
317
|
+
// C121 abstract-process-references-resolve (smart TODO.roadmap/40
|
|
318
|
+
// batch 2): the abstract-process model's framework bindings resolve
|
|
319
|
+
// — roles/organs/participant_kinds lists, decision.rule, the
|
|
320
|
+
// declaration kind + sign|update action vocabulary,
|
|
321
|
+
// discharges_gate, realized_by → process, approved_by → approval,
|
|
322
|
+
// the calendar windows' shape (kind vocabulary; exactly one of
|
|
323
|
+
// years/months), and the process_model's sequence + register
|
|
324
|
+
// maintainer edges. Per-register gating (the C58 doctrine)
|
|
325
|
+
// C122 scheme-type-resolves (smart TODO.roadmap/40 batch 2): the
|
|
326
|
+
// ISO/IEC 17067 register is composable — a manifest's scheme_type
|
|
327
|
+
// token resolves against the composed scheme_type register when one
|
|
328
|
+
// is in scope; a scheme_type's determination / attestation /
|
|
329
|
+
// surveillance.activities entries resolve against the composed
|
|
330
|
+
// scheme_activity_kind menus. C98's no-surveillance set defers to
|
|
331
|
+
// the register's surveillance.required (register-free fallback
|
|
332
|
+
// stays)
|
|
333
|
+
// C123 document-module-references-resolve (smart TODO.roadmap/40 batch
|
|
334
|
+
// 2): the document module's edges resolve — sequence → process,
|
|
335
|
+
// register maintainer → governance_organ (per-register gated), the
|
|
336
|
+
// declared namespace is an absolute requirement-namespace path (the
|
|
337
|
+
// pin C119 prefers over the requirement_class-id derivation), and
|
|
338
|
+
// the informative annex's applies_to names a known package
|
|
339
|
+
// (locator-gated)
|
|
340
|
+
// C124 pair-list-shape (smart TODO.roadmap/40 batch 4): the
|
|
341
|
+
// attribute_definition pair_list block's declaration shape — key +
|
|
342
|
+
// value slots required, component ids unique, key_dimension →
|
|
343
|
+
// applicability dimension (per-register gated); the closed-registry-
|
|
344
|
+
// over-values leg stays app-side
|
|
345
|
+
// C125 formula-variant-shape (smart TODO.roadmap/40 batch 4): the
|
|
346
|
+
// calculation variant block's declaration shape — variant ids
|
|
347
|
+
// unique, the type-conditional facets present (expression /
|
|
348
|
+
// table_lookup / profile_lookup); the params are engine call-site
|
|
349
|
+
// names and carry no resolution leg
|
|
350
|
+
// C126 formula-note-targets-resolve (smart TODO.roadmap/40 batch 4):
|
|
351
|
+
// the formula_note register's reverse applicability resolves —
|
|
352
|
+
// applies_to → symbol (per-register gated)
|
|
353
|
+
// C127 common-test-condition-shape (smart TODO.roadmap/40 batch 4):
|
|
354
|
+
// the common_test_condition register's declaration shape — the
|
|
355
|
+
// description is required; the title stays optional (the r91 map
|
|
356
|
+
// entries carry none) and the reference a free citation string
|
|
357
|
+
// C128 part-annex-shape (smart TODO.roadmap/40 batch 4): the
|
|
358
|
+
// part_annex register's declaration shape — letters unique per
|
|
359
|
+
// package, the source provenance required; the obligation
|
|
360
|
+
// vocabulary is parse-enforced upstream
|
|
361
|
+
// C129 storyline-shape (smart TODO.roadmap/40 batch 4): the demo-seed
|
|
362
|
+
// shapes — the id_prefix pattern (register-free), party →
|
|
363
|
+
// demo_world participant seeds (gated), record stores → entity-
|
|
364
|
+
// class stores (gated), in-construct record cross-references
|
|
365
|
+
// (ungated); field-level discipline stays app-side
|
|
314
366
|
//
|
|
315
367
|
// Levels (TODO.roadmap/17): the DEFAULT level runs the normal-level
|
|
316
368
|
// rules at their catalog severities. --audit additionally runs the
|
|
@@ -1365,6 +1417,11 @@ function checkPackage(dir, options = {}) {
|
|
|
1365
1417
|
// never roles — a scheme may bind one role to evaluation, review AND
|
|
1366
1418
|
// decision, so the norms quantify over process involvement. Per-
|
|
1367
1419
|
// assignment runtime enforcement is the platform's business (task 44).
|
|
1420
|
+
// The kind-conditional shape (smart TODO.roadmap/40 batch 2 — the YAML
|
|
1421
|
+
// schema's allOf discipline, data/schemas/abstract-processes.yaml): a
|
|
1422
|
+
// case_personnel_disjoint constraint is ONLY a pair (no period, no
|
|
1423
|
+
// barred relations); a consultancy_bar is ONLY a barred-relations list
|
|
1424
|
+
// with an optional period (never a pair).
|
|
1368
1425
|
// Mirror of the OIML SMART linker's R24 abstract-process-segregation.
|
|
1369
1426
|
{
|
|
1370
1427
|
const SEGREGATION_CASE_PERSONNEL = 'case_personnel';
|
|
@@ -1381,6 +1438,9 @@ function checkPackage(dir, options = {}) {
|
|
|
1381
1438
|
err('C59', `process ${p.id}: segregation constraint "${s.id}" is case_personnel_disjoint but declares ${s.pair.length} pair members — exactly two are required (segregation-members-resolve)`);
|
|
1382
1439
|
continue;
|
|
1383
1440
|
}
|
|
1441
|
+
if (s.period !== '' || s.barred.length > 0) {
|
|
1442
|
+
err('C59', `process ${p.id}: segregation constraint "${s.id}" is case_personnel_disjoint but declares ${s.period !== '' ? `period "${s.period}"` : 'barred relations'} — a disjoint constraint is ONLY a pair; the period/barred facets belong to consultancy_bar (segregation-members-resolve)`);
|
|
1443
|
+
}
|
|
1384
1444
|
const [a, b] = s.pair;
|
|
1385
1445
|
if (a === b) {
|
|
1386
1446
|
err('C59', `process ${p.id}: segregation constraint "${s.id}" declares "${a}" disjoint from itself — the two personnel sets must be distinct (segregation-members-resolve)`);
|
|
@@ -1395,6 +1455,134 @@ function checkPackage(dir, options = {}) {
|
|
|
1395
1455
|
err('C59', `process ${p.id}: segregation constraint "${s.id}" is declared on process "${p.id}" but neither pair member is "${p.id}" — the constrained process owns its segregation constraints (segregation-members-resolve)`);
|
|
1396
1456
|
}
|
|
1397
1457
|
}
|
|
1458
|
+
else {
|
|
1459
|
+
// consultancy_bar — the barred client relations are REQUIRED
|
|
1460
|
+
// (a bar that bars nothing is no bar); the pair is the disjoint
|
|
1461
|
+
// kind's shape and never appears here; the period is optional
|
|
1462
|
+
// (4.2.10 body-specified vs 7.13.6 fixed P2Y).
|
|
1463
|
+
if (s.barred.length === 0) {
|
|
1464
|
+
err('C59', `process ${p.id}: segregation constraint "${s.id}" is consultancy_bar but bars no client relation — the barred list is required (segregation-members-resolve)`);
|
|
1465
|
+
}
|
|
1466
|
+
if (s.pair.length > 0) {
|
|
1467
|
+
err('C59', `process ${p.id}: segregation constraint "${s.id}" is consultancy_bar but declares a pair — the pair is the case_personnel_disjoint shape; a bar carries barred relations and an optional period only (segregation-members-resolve)`);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
// ── C121: abstract-process-references-resolve (smart TODO.roadmap/40 ──
|
|
1474
|
+
// batch 2; the packages-as-SSOT epic) ───────────────────────────────
|
|
1475
|
+
// The abstract-process model's framework bindings resolve against the
|
|
1476
|
+
// composed registers: the role binding list, the organ and participant
|
|
1477
|
+
// kind facets, the decision rule, the declaration kind (plus the sign |
|
|
1478
|
+
// update action vocabulary), the discharged declaration gate, the
|
|
1479
|
+
// realizing processes and the authorizing approvals; the calendar
|
|
1480
|
+
// windows' shape legs (the max_elapsed | min_elapsed kind vocabulary;
|
|
1481
|
+
// exactly one of years/months — the scheme_lifecycle duration
|
|
1482
|
+
// sub-grammar) are register-free. The process_model's sequence members
|
|
1483
|
+
// (→ process) and register maintainers (→ governance_organ) ride the
|
|
1484
|
+
// same rule. Per-register gating (the C58 doctrine): an edge is checked
|
|
1485
|
+
// only when its target register is in composition scope. The windows'
|
|
1486
|
+
// anchor/applies_to FIELD resolution stays consumer-side (the R26
|
|
1487
|
+
// precedent). Mirror of the OIML SMART linker's abstract-process
|
|
1488
|
+
// reference legs.
|
|
1489
|
+
{
|
|
1490
|
+
const apRoleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
1491
|
+
const apGateIds = new Set((standard.declarationGates ?? []).map(g => g.id));
|
|
1492
|
+
const apRuleIds = new Set((standard.decisionRules ?? []).map(r => r.id));
|
|
1493
|
+
const apApprovalIds = new Set((standard.approvals ?? []).map(a => a.id));
|
|
1494
|
+
// The C120 organ/kind/declaration resolvers report under C120 — the
|
|
1495
|
+
// abstract-process edges are C121's, so the rule carries its own
|
|
1496
|
+
// per-register-gated resolvers over the same composed registers.
|
|
1497
|
+
const resolveOrganAp = (where, facet, id) => {
|
|
1498
|
+
if (organIds.size > 0 && id !== '' && !organIds.has(id)) {
|
|
1499
|
+
err('C121', `${where}: ${facet} "${id}" is not a declared governance organ (abstract-process-references-resolve)`);
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
const resolveKindAp = (where, facet, id) => {
|
|
1503
|
+
if (kindIds.size > 0 && id !== '' && !kindIds.has(id)) {
|
|
1504
|
+
err('C121', `${where}: ${facet} "${id}" is not a declared participant kind (abstract-process-references-resolve)`);
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
const resolveDeclarationAp = (where, facet, id) => {
|
|
1508
|
+
if (declIds.size > 0 && id !== '' && !declIds.has(id)) {
|
|
1509
|
+
err('C121', `${where}: ${facet} "${id}" is not a declared declaration kind (abstract-process-references-resolve)`);
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
const resolveProcessRef = (where, facet, id) => {
|
|
1513
|
+
if (frameworkProcessIds.size > 0 && !frameworkProcessIds.has(id)) {
|
|
1514
|
+
err('C121', `${where}: ${facet} "${id}" is not a declared process (abstract-process-references-resolve)`);
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
for (const p of standard.processes ?? []) {
|
|
1518
|
+
const where = `process ${p.id}`;
|
|
1519
|
+
if (apRoleIds.size > 0) {
|
|
1520
|
+
for (const r of p.roles ?? []) {
|
|
1521
|
+
if (!apRoleIds.has(r)) {
|
|
1522
|
+
err('C121', `${where}: roles "${r}" is not a declared role (abstract-process-references-resolve)`);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
for (const o of p.organs ?? []) {
|
|
1527
|
+
resolveOrganAp(where, 'organs', o);
|
|
1528
|
+
}
|
|
1529
|
+
for (const k of p.participantKinds ?? []) {
|
|
1530
|
+
resolveKindAp(where, 'participant_kinds', k);
|
|
1531
|
+
}
|
|
1532
|
+
if (p.decision) {
|
|
1533
|
+
if (p.decision.rule === '') {
|
|
1534
|
+
err('C121', `${where}: the decision facet declares no rule — a decision names the decision_rule it applies (abstract-process-references-resolve)`);
|
|
1535
|
+
}
|
|
1536
|
+
else if (apRuleIds.size > 0 && !apRuleIds.has(p.decision.rule)) {
|
|
1537
|
+
err('C121', `${where}: decision.rule "${p.decision.rule}" is not a declared decision rule (abstract-process-references-resolve)`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (p.declaration) {
|
|
1541
|
+
if (p.declaration.kind === '') {
|
|
1542
|
+
err('C121', `${where}: the declaration facet declares no kind — a declaration names the declaration_kind it signs or updates (abstract-process-references-resolve)`);
|
|
1543
|
+
}
|
|
1544
|
+
else {
|
|
1545
|
+
resolveDeclarationAp(where, 'declaration.kind', p.declaration.kind);
|
|
1546
|
+
}
|
|
1547
|
+
if (p.declaration.action !== 'sign' &&
|
|
1548
|
+
p.declaration.action !== 'update') {
|
|
1549
|
+
err('C121', `${where}: declaration action "${p.declaration.action || '(none)'}" — a declaration is signed or updated (valid: sign, update) (abstract-process-references-resolve)`);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
if (p.dischargesGate !== '' &&
|
|
1553
|
+
apGateIds.size > 0 &&
|
|
1554
|
+
!apGateIds.has(p.dischargesGate)) {
|
|
1555
|
+
err('C121', `${where}: discharges_gate "${p.dischargesGate}" is not a declared declaration gate (abstract-process-references-resolve)`);
|
|
1556
|
+
}
|
|
1557
|
+
for (const r of p.realizedBy ?? []) {
|
|
1558
|
+
resolveProcessRef(where, 'realized_by', r);
|
|
1559
|
+
}
|
|
1560
|
+
if (apApprovalIds.size > 0) {
|
|
1561
|
+
for (const a of p.approvedBy ?? []) {
|
|
1562
|
+
if (!apApprovalIds.has(a)) {
|
|
1563
|
+
err('C121', `${where}: approved_by "${a}" is not a declared approval (abstract-process-references-resolve)`);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
for (const w of p.windows ?? []) {
|
|
1568
|
+
const wwhere = `${where} window ${w.id}`;
|
|
1569
|
+
if (w.kind !== 'max_elapsed' && w.kind !== 'min_elapsed') {
|
|
1570
|
+
err('C121', `${wwhere}: kind "${w.kind || '(none)'}" — a calendar window is a deadline or a cooling-off (valid: max_elapsed, min_elapsed) (abstract-process-references-resolve)`);
|
|
1571
|
+
}
|
|
1572
|
+
if (w.windowYears > 0 === w.windowMonths > 0) {
|
|
1573
|
+
err('C121', `${wwhere}: the window duration declares ${w.windowYears > 0
|
|
1574
|
+
? 'both years and months'
|
|
1575
|
+
: 'neither years nor months'} — exactly one is required (the scheme_lifecycle window sub-grammar) (abstract-process-references-resolve)`);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
for (const m of standard.processModels ?? []) {
|
|
1580
|
+
const where = `process_model ${m.id}`;
|
|
1581
|
+
for (const s of m.sequence ?? []) {
|
|
1582
|
+
resolveProcessRef(where, 'sequence', s);
|
|
1583
|
+
}
|
|
1584
|
+
for (const r of m.registers ?? []) {
|
|
1585
|
+
resolveOrganAp(`${where} register ${r.id}`, 'maintainer', r.maintainer);
|
|
1398
1586
|
}
|
|
1399
1587
|
}
|
|
1400
1588
|
}
|
|
@@ -2427,6 +2615,88 @@ function checkPackage(dir, options = {}) {
|
|
|
2427
2615
|
}
|
|
2428
2616
|
}
|
|
2429
2617
|
}
|
|
2618
|
+
// ── C122: scheme-type-resolves (smart TODO.roadmap/40 batch 2; the ──
|
|
2619
|
+
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
2620
|
+
// The ISO/IEC 17067 register is composable: a manifest's `scheme_type`
|
|
2621
|
+
// token resolves against the composed scheme_type register WHEN one is
|
|
2622
|
+
// in scope (per-register gating, the C58 doctrine — a register-less
|
|
2623
|
+
// package keeps the register-free fallback, the C89 spelling
|
|
2624
|
+
// precedent); and a scheme_type's determination / attestation /
|
|
2625
|
+
// surveillance.activities entries resolve against the composed
|
|
2626
|
+
// scheme_activity_kind menus when THAT register is in scope. C98's
|
|
2627
|
+
// hard-coded no-surveillance set defers to the register's
|
|
2628
|
+
// surveillance.required (below, in the C97–C98 block).
|
|
2629
|
+
{
|
|
2630
|
+
const schemeTypeIds = new Set((standard.schemeTypes ?? []).map(t => t.id));
|
|
2631
|
+
const menuIds = new Set((standard.schemeActivityKinds ?? []).map(k => k.id));
|
|
2632
|
+
const manifestSchemeType = standard.packageManifest?.schemeType;
|
|
2633
|
+
if (schemeTypeIds.size > 0 &&
|
|
2634
|
+
manifestSchemeType &&
|
|
2635
|
+
!schemeTypeIds.has(manifestSchemeType)) {
|
|
2636
|
+
err('C122', `package "${standard.packageManifest.id}": scheme_type "${manifestSchemeType}" is not a declared scheme type of the in-scope ISO/IEC 17067 register (scheme-type-resolves)`);
|
|
2637
|
+
}
|
|
2638
|
+
if (menuIds.size > 0) {
|
|
2639
|
+
const resolveMenuEntry = (where, facet, id) => {
|
|
2640
|
+
if (!menuIds.has(id)) {
|
|
2641
|
+
err('C122', `${where}: ${facet} "${id}" is not a declared scheme activity kind (scheme-type-resolves)`);
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
for (const t of standard.schemeTypes ?? []) {
|
|
2645
|
+
const where = `scheme_type ${t.id}`;
|
|
2646
|
+
for (const d of t.determination) {
|
|
2647
|
+
resolveMenuEntry(where, 'determination', d);
|
|
2648
|
+
}
|
|
2649
|
+
for (const a of t.attestation) {
|
|
2650
|
+
resolveMenuEntry(where, 'attestation', a);
|
|
2651
|
+
}
|
|
2652
|
+
for (const s of t.surveillance?.activities ?? []) {
|
|
2653
|
+
resolveMenuEntry(where, 'surveillance.activities', s);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
// ── C123: document-module-references-resolve (smart TODO.roadmap/40 ──
|
|
2659
|
+
// batch 2; the packages-as-SSOT epic) ───────────────────────────────
|
|
2660
|
+
// The document module's edges resolve: the sequence members against
|
|
2661
|
+
// the composed processes, the register maintainers against the
|
|
2662
|
+
// governance_organ register (per-register gating, the C58 doctrine),
|
|
2663
|
+
// and the declared namespace is an absolute requirement-namespace path
|
|
2664
|
+
// (the register-free shape leg). The informative annex's applies_to
|
|
2665
|
+
// names a known package — checked through the dependency-manifest
|
|
2666
|
+
// locator, so the leg is gated on a locator being supplied (the
|
|
2667
|
+
// C97-class resolution discipline; without a locator the sibling scan
|
|
2668
|
+
// cannot adjudicate non-sibling ids and the leg stays silent).
|
|
2669
|
+
{
|
|
2670
|
+
const dmOrganIds = new Set((standard.governanceOrgans ?? []).map(o => o.id));
|
|
2671
|
+
const dmProcessIds = new Set((standard.processes ?? []).map(p => p.id));
|
|
2672
|
+
for (const m of standard.documentModules ?? []) {
|
|
2673
|
+
const where = `document_module ${m.id}`;
|
|
2674
|
+
if (m.namespace !== '' && !m.namespace.startsWith('/')) {
|
|
2675
|
+
err('C123', `${where}: namespace "${m.namespace}" is not an absolute requirement-namespace path (e.g. /req/cs/${m.id}) — the declared pin C119 guards must be a path (document-module-references-resolve)`);
|
|
2676
|
+
}
|
|
2677
|
+
if (dmProcessIds.size > 0) {
|
|
2678
|
+
for (const s of m.sequence ?? []) {
|
|
2679
|
+
if (!dmProcessIds.has(s)) {
|
|
2680
|
+
err('C123', `${where}: sequence "${s}" is not a declared process (document-module-references-resolve)`);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
if (dmOrganIds.size > 0) {
|
|
2685
|
+
for (const r of m.registers ?? []) {
|
|
2686
|
+
if (r.maintainer !== '' && !dmOrganIds.has(r.maintainer)) {
|
|
2687
|
+
err('C123', `${where} register ${r.id}: maintainer "${r.maintainer}" is not a declared governance organ (document-module-references-resolve)`);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
if (options.resolvePackage) {
|
|
2693
|
+
for (const a of standard.informativeAnnexes ?? []) {
|
|
2694
|
+
if (a.appliesTo !== '' && !resolveDepManifest(a.appliesTo)) {
|
|
2695
|
+
err('C123', `informative_annex ${a.id}: applies_to "${a.appliesTo}" does not resolve to a known package — the CASCO layer the annex applies to must resolve (document-module-references-resolve)`);
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2430
2700
|
// ── C97–C98: the certification program (TODO.v2/01;
|
|
2431
2701
|
// analysis/twin-certification-design.md Q4) ──
|
|
2432
2702
|
// A fourth publisher with the product_reference shape: the scheme
|
|
@@ -2464,9 +2734,16 @@ function checkPackage(dir, options = {}) {
|
|
|
2464
2734
|
// surveillance machinery. A monitor IS a continuous claim ("without
|
|
2465
2735
|
// triggers, 'continuous' has no clock" — types/Monitor.ts); an
|
|
2466
2736
|
// activity_kind { surveillance } process is a surveillance claim.
|
|
2737
|
+
// When the scheme-type register is in scope (smart TODO.roadmap/40
|
|
2738
|
+
// batch 2 — composed via uses), the register's own
|
|
2739
|
+
// surveillance.required adjudicates; the hard-coded type_1a/1b set
|
|
2740
|
+
// is the register-free fallback (the C89 spelling precedent).
|
|
2467
2741
|
const SCHEME_TYPES_WITHOUT_SURVEILLANCE = new Set(['type_1a', 'type_1b']);
|
|
2468
|
-
|
|
2469
|
-
|
|
2742
|
+
const registerEntry = (standard.schemeTypes ?? []).find(t => t.id === productManifest.schemeType);
|
|
2743
|
+
const noSurveillance = registerEntry
|
|
2744
|
+
? !registerEntry.surveillance?.required
|
|
2745
|
+
: SCHEME_TYPES_WITHOUT_SURVEILLANCE.has(productManifest.schemeType ?? '');
|
|
2746
|
+
if (productManifest.schemeType && noSurveillance) {
|
|
2470
2747
|
const claims = [];
|
|
2471
2748
|
if ((standard.monitors ?? []).length > 0) {
|
|
2472
2749
|
claims.push(`${standard.monitors.length} monitor(s): ${standard.monitors.map(m => m.id).join(', ')}`);
|
|
@@ -3066,6 +3343,183 @@ function checkPackage(dir, options = {}) {
|
|
|
3066
3343
|
}
|
|
3067
3344
|
}
|
|
3068
3345
|
}
|
|
3346
|
+
// ── C124: pair-list-shape (smart TODO.roadmap/40 batch 4; the ──────
|
|
3347
|
+
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
3348
|
+
// The pair_list block's declaration shape: the key and value slots are
|
|
3349
|
+
// required, the component ids are unique within the block, and the
|
|
3350
|
+
// key_dimension names a declared applicability dimension WHEN the
|
|
3351
|
+
// dimension register is in composition scope (per-register gating, the
|
|
3352
|
+
// C58 doctrine). The closed-registry-over-VALUES leg (pair keys drawn
|
|
3353
|
+
// from the dimension's values ∪ the declared components) quantifies
|
|
3354
|
+
// over app-side records, so it stays app-side — the kernel checks the
|
|
3355
|
+
// declaration shape only.
|
|
3356
|
+
for (const a of standard.attributeDefinitions ?? []) {
|
|
3357
|
+
const pl = a.pairList;
|
|
3358
|
+
if (!pl) {
|
|
3359
|
+
continue;
|
|
3360
|
+
}
|
|
3361
|
+
const where = `attribute_definition ${a.id}: pair_list`;
|
|
3362
|
+
if (!pl.key) {
|
|
3363
|
+
err('C124', `${where}: the key slot is required (pair-list-shape)`);
|
|
3364
|
+
}
|
|
3365
|
+
if (!pl.value) {
|
|
3366
|
+
err('C124', `${where}: the value slot is required (pair-list-shape)`);
|
|
3367
|
+
}
|
|
3368
|
+
const seen = new Set();
|
|
3369
|
+
for (const c of pl.components) {
|
|
3370
|
+
if (seen.has(c.id)) {
|
|
3371
|
+
err('C124', `${where}: component "${c.id}" is declared twice (pair-list-shape)`);
|
|
3372
|
+
}
|
|
3373
|
+
seen.add(c.id);
|
|
3374
|
+
}
|
|
3375
|
+
if (pl.keyDimension && dimIds.size > 0 && !dimIds.has(pl.keyDimension)) {
|
|
3376
|
+
err('C124', `${where}: key_dimension "${pl.keyDimension}" is not a declared applicability dimension (pair-list-shape)`);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
// ── C125: formula-variant-shape (smart TODO.roadmap/40 batch 4; the ─
|
|
3380
|
+
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
3381
|
+
// The variant block's declaration shape (the top-level calculation
|
|
3382
|
+
// shape discipline mirrored onto the realization): variant ids are
|
|
3383
|
+
// unique within the owning calculation, and the type-conditional
|
|
3384
|
+
// facets are present — an expression variant carries its expression,
|
|
3385
|
+
// a table_lookup variant its lookup block, a profile_lookup variant
|
|
3386
|
+
// its profile path. The variant's params are engine call-site names
|
|
3387
|
+
// and deliberately do NOT resolve against the calculation's inputs —
|
|
3388
|
+
// there is no params-resolve leg (the block stays documentary/
|
|
3389
|
+
// engine-facing).
|
|
3390
|
+
for (const c of standard.calculations ?? []) {
|
|
3391
|
+
const seen = new Set();
|
|
3392
|
+
for (const v of c.variants ?? []) {
|
|
3393
|
+
const where = `calculation ${c.id}: variant ${v.id}`;
|
|
3394
|
+
if (seen.has(v.id)) {
|
|
3395
|
+
err('C125', `${where}: the variant id is declared twice (formula-variant-shape)`);
|
|
3396
|
+
}
|
|
3397
|
+
seen.add(v.id);
|
|
3398
|
+
if (v.type === 'expression' && !v.expression) {
|
|
3399
|
+
err('C125', `${where}: type expression requires the expression facet (formula-variant-shape)`);
|
|
3400
|
+
}
|
|
3401
|
+
if (v.type === 'table_lookup' && !v.lookup) {
|
|
3402
|
+
err('C125', `${where}: type table_lookup requires the lookup block (formula-variant-shape)`);
|
|
3403
|
+
}
|
|
3404
|
+
if (v.type === 'profile_lookup' && !v.profile) {
|
|
3405
|
+
err('C125', `${where}: type profile_lookup requires the profile facet (formula-variant-shape)`);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
// ── C126: formula-note-targets-resolve (smart TODO.roadmap/40 ──────
|
|
3410
|
+
// batch 4; the packages-as-SSOT epic) ────────────────────────────────
|
|
3411
|
+
// The formula_note register's reverse applicability resolves: every
|
|
3412
|
+
// applies_to entry names a declared symbol WHEN the symbol register
|
|
3413
|
+
// is in composition scope (per-register gating, the C58 doctrine).
|
|
3414
|
+
if (symbolIds.size > 0) {
|
|
3415
|
+
for (const n of standard.formulaNotes ?? []) {
|
|
3416
|
+
for (const s of n.appliesTo ?? []) {
|
|
3417
|
+
if (!symbolIds.has(s)) {
|
|
3418
|
+
err('C126', `formula_note ${n.id}: applies_to "${s}" is not a declared symbol (formula-note-targets-resolve)`);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
// ── C127: common-test-condition-shape (smart TODO.roadmap/40 ───────
|
|
3424
|
+
// batch 4; the packages-as-SSOT epic) ────────────────────────────────
|
|
3425
|
+
// The register's declaration shape: the description is the one facet
|
|
3426
|
+
// BOTH YAML shapes carry (the r60/r144/r129 array entries and the r91
|
|
3427
|
+
// keyed-map entries alike) — a condition without its text is a
|
|
3428
|
+
// carrier with no content. The title stays OPTIONAL (the r91 map
|
|
3429
|
+
// entries carry none; requiring it would force content invention at
|
|
3430
|
+
// migration) and the reference stays a free citation string (the
|
|
3431
|
+
// linker owns the citation semantics — no kernel leg).
|
|
3432
|
+
for (const c of standard.commonTestConditions ?? []) {
|
|
3433
|
+
if (!c.description) {
|
|
3434
|
+
err('C127', `common_test_condition ${c.id}: the description is required — a condition entry carries its normative text (common-test-condition-shape)`);
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
// ── C128: part-annex-shape (smart TODO.roadmap/40 batch 4; the ─────
|
|
3438
|
+
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
3439
|
+
// The annex-volume register's declaration shape: letters are unique
|
|
3440
|
+
// per package (the index keys on the printed letter) and every entry
|
|
3441
|
+
// carries its clause-URN provenance. The obligation vocabulary is
|
|
3442
|
+
// parse-enforced upstream (the fail-closed precedent), so it carries
|
|
3443
|
+
// no check leg.
|
|
3444
|
+
{
|
|
3445
|
+
const seenLetters = new Map();
|
|
3446
|
+
for (const a of standard.partAnnexes ?? []) {
|
|
3447
|
+
if (a.letter) {
|
|
3448
|
+
const prior = seenLetters.get(a.letter);
|
|
3449
|
+
if (prior !== undefined) {
|
|
3450
|
+
err('C128', `part_annex ${a.id}: letter "${a.letter}" is already declared by ${prior} — the annex-volume index keys on the printed letter (part-annex-shape)`);
|
|
3451
|
+
}
|
|
3452
|
+
else {
|
|
3453
|
+
seenLetters.set(a.letter, a.id);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
if (!a.source?.doc) {
|
|
3457
|
+
err('C128', `part_annex ${a.id}: the source provenance is required — a documentary register entry cites its part (part-annex-shape)`);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
// ── C129: storyline-shape (smart TODO.roadmap/40 batch 4; the ──────
|
|
3462
|
+
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
3463
|
+
// The demo-seed shapes: the id_prefix pattern (the register-free shape
|
|
3464
|
+
// leg); the party laboratory/authority resolve against the package's
|
|
3465
|
+
// participant seeds WHEN a demo_world with participants is in scope
|
|
3466
|
+
// (per-register gating, the C58 doctrine); the record stores resolve
|
|
3467
|
+
// against the declared entity-class stores WHEN that register is in
|
|
3468
|
+
// scope; and the in-construct cross-references resolve — a record
|
|
3469
|
+
// field keyed by a sibling record's store names a sibling record of
|
|
3470
|
+
// that store (in-construct, ungated). The field-level discipline (the
|
|
3471
|
+
// schema's per-slot required fields) stays app-side.
|
|
3472
|
+
{
|
|
3473
|
+
const participantSeeds = new Set();
|
|
3474
|
+
for (const w of standard.demoWorlds ?? []) {
|
|
3475
|
+
for (const section of w.participants ?? []) {
|
|
3476
|
+
for (const e of section.entries ?? []) {
|
|
3477
|
+
participantSeeds.add(e.id);
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
const entityStores = new Set((standard.dataclasses ?? [])
|
|
3482
|
+
.map(c => c.store)
|
|
3483
|
+
.filter((s) => !!s));
|
|
3484
|
+
for (const s of standard.storylines ?? []) {
|
|
3485
|
+
const where = `storyline ${s.id}`;
|
|
3486
|
+
if (s.idPrefix && !/^[a-z0-9-]+$/.test(s.idPrefix)) {
|
|
3487
|
+
err('C129', `${where}: id_prefix "${s.idPrefix}" does not match ^[a-z0-9-]+$ — the id-minting prefix is a lowercase slug (storyline-shape)`);
|
|
3488
|
+
}
|
|
3489
|
+
if (participantSeeds.size > 0) {
|
|
3490
|
+
for (const [slot, ref] of [
|
|
3491
|
+
['laboratory', s.party.laboratory],
|
|
3492
|
+
['authority', s.party.authority],
|
|
3493
|
+
]) {
|
|
3494
|
+
if (ref && !participantSeeds.has(ref)) {
|
|
3495
|
+
err('C129', `${where}: party ${slot} "${ref}" is not a declared participant seed (storyline-shape)`);
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
const siblingStores = new Map();
|
|
3500
|
+
for (const r of s.records ?? []) {
|
|
3501
|
+
if (entityStores.size > 0 && !entityStores.has(r.store)) {
|
|
3502
|
+
err('C129', `${where}: record store "${r.store}" is not a declared entity-class store (storyline-shape)`);
|
|
3503
|
+
}
|
|
3504
|
+
let ids = siblingStores.get(r.store);
|
|
3505
|
+
if (!ids) {
|
|
3506
|
+
ids = new Set();
|
|
3507
|
+
siblingStores.set(r.store, ids);
|
|
3508
|
+
}
|
|
3509
|
+
ids.add(r.id);
|
|
3510
|
+
}
|
|
3511
|
+
for (const r of s.records ?? []) {
|
|
3512
|
+
for (const [key, value] of Object.entries(r.fields)) {
|
|
3513
|
+
const targets = siblingStores.get(key);
|
|
3514
|
+
if (targets !== undefined &&
|
|
3515
|
+
!Array.isArray(value) &&
|
|
3516
|
+
!targets.has(String(value))) {
|
|
3517
|
+
err('C129', `${where}: record ${r.store} ${r.id}: ${key} "${String(value)}" is not a sibling ${key} record of this storyline (storyline-shape)`);
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3069
3523
|
// C34 — duality-coherence: one value structure, two roles.
|
|
3070
3524
|
for (const d of standard.duals ?? []) {
|
|
3071
3525
|
if (d.attribute && !attrIds.has(d.attribute)) {
|