@primmel/primmel 1.20.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +7 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/check-rules.d.ts +1 -1
  6. package/dist/src/check-rules.d.ts.map +1 -1
  7. package/dist/src/check-rules.js +36 -15
  8. package/dist/src/check-rules.js.map +1 -1
  9. package/dist/src/check.d.ts +4 -0
  10. package/dist/src/check.d.ts.map +1 -1
  11. package/dist/src/check.js +428 -137
  12. package/dist/src/check.js.map +1 -1
  13. package/dist/src/export/cddal.d.ts +25 -0
  14. package/dist/src/export/cddal.d.ts.map +1 -0
  15. package/dist/src/export/cddal.js +213 -0
  16. package/dist/src/export/cddal.js.map +1 -0
  17. package/dist/src/export/retrieval.d.ts +147 -6
  18. package/dist/src/export/retrieval.d.ts.map +1 -1
  19. package/dist/src/export/retrieval.js +254 -25
  20. package/dist/src/export/retrieval.js.map +1 -1
  21. package/dist/src/export/unitsdb.d.ts +19 -0
  22. package/dist/src/export/unitsdb.d.ts.map +1 -0
  23. package/dist/src/export/unitsdb.js +82 -0
  24. package/dist/src/export/unitsdb.js.map +1 -0
  25. package/dist/src/export/yaml-lite.d.ts +13 -0
  26. package/dist/src/export/yaml-lite.d.ts.map +1 -0
  27. package/dist/src/export/yaml-lite.js +219 -0
  28. package/dist/src/export/yaml-lite.js.map +1 -0
  29. package/dist/src/instance-resolution.d.ts +19 -0
  30. package/dist/src/instance-resolution.d.ts.map +1 -1
  31. package/dist/src/instance-resolution.js +87 -1
  32. package/dist/src/instance-resolution.js.map +1 -1
  33. package/dist/src/model-diff.d.ts.map +1 -1
  34. package/dist/src/model-diff.js +0 -5
  35. package/dist/src/model-diff.js.map +1 -1
  36. package/dist/src/runtime.d.ts +143 -0
  37. package/dist/src/runtime.d.ts.map +1 -0
  38. package/dist/src/runtime.js +648 -0
  39. package/dist/src/runtime.js.map +1 -0
  40. package/dist/src/ser-des/config/artifact.d.ts.map +1 -1
  41. package/dist/src/ser-des/config/artifact.js +6 -0
  42. package/dist/src/ser-des/config/artifact.js.map +1 -1
  43. package/dist/src/ser-des/config/attestation.d.ts +5 -0
  44. package/dist/src/ser-des/config/attestation.d.ts.map +1 -0
  45. package/dist/src/ser-des/config/attestation.js +143 -0
  46. package/dist/src/ser-des/config/attestation.js.map +1 -0
  47. package/dist/src/ser-des/config/data.d.ts.map +1 -1
  48. package/dist/src/ser-des/config/data.js +98 -0
  49. package/dist/src/ser-des/config/data.js.map +1 -1
  50. package/dist/src/ser-des/config/documentModule.js +5 -5
  51. package/dist/src/ser-des/config/documentModule.js.map +1 -1
  52. package/dist/src/ser-des/config/index.d.ts +8 -0
  53. package/dist/src/ser-des/config/index.d.ts.map +1 -1
  54. package/dist/src/ser-des/config/index.js +22 -49
  55. package/dist/src/ser-des/config/index.js.map +1 -1
  56. package/dist/src/ser-des/config/instance.d.ts.map +1 -1
  57. package/dist/src/ser-des/config/instance.js +24 -0
  58. package/dist/src/ser-des/config/instance.js.map +1 -1
  59. package/dist/src/ser-des/config/process.d.ts.map +1 -1
  60. package/dist/src/ser-des/config/process.js +8 -0
  61. package/dist/src/ser-des/config/process.js.map +1 -1
  62. package/dist/src/ser-des/config/processSequence.d.ts +19 -0
  63. package/dist/src/ser-des/config/processSequence.d.ts.map +1 -0
  64. package/dist/src/ser-des/config/processSequence.js +77 -0
  65. package/dist/src/ser-des/config/processSequence.js.map +1 -0
  66. package/dist/src/ser-des/config/quantity.d.ts +4 -1
  67. package/dist/src/ser-des/config/quantity.d.ts.map +1 -1
  68. package/dist/src/ser-des/config/quantity.js +63 -1
  69. package/dist/src/ser-des/config/quantity.js.map +1 -1
  70. package/dist/src/ser-des/config/subject.d.ts.map +1 -1
  71. package/dist/src/ser-des/config/subject.js +14 -0
  72. package/dist/src/ser-des/config/subject.js.map +1 -1
  73. package/dist/src/ser-des/index.d.ts +4 -1
  74. package/dist/src/ser-des/index.d.ts.map +1 -1
  75. package/dist/src/ser-des/index.js +3 -1
  76. package/dist/src/ser-des/index.js.map +1 -1
  77. package/dist/src/ser-des/package.d.ts.map +1 -1
  78. package/dist/src/ser-des/package.js +9 -7
  79. package/dist/src/ser-des/package.js.map +1 -1
  80. package/dist/src/ser-des/parse.d.ts +24 -0
  81. package/dist/src/ser-des/parse.d.ts.map +1 -1
  82. package/dist/src/ser-des/parse.js +69 -33
  83. package/dist/src/ser-des/parse.js.map +1 -1
  84. package/dist/src/ser-des/shape-front-end.d.ts +8 -0
  85. package/dist/src/ser-des/shape-front-end.d.ts.map +1 -0
  86. package/dist/src/ser-des/shape-front-end.js +215 -0
  87. package/dist/src/ser-des/shape-front-end.js.map +1 -0
  88. package/dist/src/ser-des/types.d.ts +4 -10
  89. package/dist/src/ser-des/types.d.ts.map +1 -1
  90. package/dist/src/types/Artifact.d.ts +7 -0
  91. package/dist/src/types/Artifact.d.ts.map +1 -1
  92. package/dist/src/types/Attestation.d.ts +60 -0
  93. package/dist/src/types/Attestation.d.ts.map +1 -0
  94. package/dist/src/types/Attestation.js +20 -0
  95. package/dist/src/types/Attestation.js.map +1 -0
  96. package/dist/src/types/DocumentModule.d.ts +1 -1
  97. package/dist/src/types/DocumentModule.d.ts.map +1 -1
  98. package/dist/src/types/Instance.d.ts +6 -2
  99. package/dist/src/types/Instance.d.ts.map +1 -1
  100. package/dist/src/types/ProcessModel.d.ts +0 -11
  101. package/dist/src/types/ProcessModel.d.ts.map +1 -1
  102. package/dist/src/types/ProcessModel.js +2 -28
  103. package/dist/src/types/ProcessModel.js.map +1 -1
  104. package/dist/src/types/Quantity.d.ts +22 -0
  105. package/dist/src/types/Quantity.d.ts.map +1 -1
  106. package/dist/src/types/Standard.d.ts +2 -13
  107. package/dist/src/types/Standard.d.ts.map +1 -1
  108. package/dist/src/types/Subject.d.ts +14 -0
  109. package/dist/src/types/Subject.d.ts.map +1 -1
  110. package/dist/src/types/data.d.ts +13 -0
  111. package/dist/src/types/data.d.ts.map +1 -1
  112. package/dist/src/types/process.d.ts +8 -0
  113. package/dist/src/types/process.d.ts.map +1 -1
  114. package/dist-browser/index.mjs +1746 -1829
  115. package/dist-cli/primmel.mjs +1630 -845
  116. package/package.json +15 -3
  117. package/dist/src/ser-des/config/processModel.d.ts +0 -13
  118. package/dist/src/ser-des/config/processModel.d.ts.map +0 -1
  119. package/dist/src/ser-des/config/processModel.js +0 -163
  120. package/dist/src/ser-des/config/processModel.js.map +0 -1
  121. package/dist/src/ser-des/config/storyline.d.ts +0 -7
  122. package/dist/src/ser-des/config/storyline.d.ts.map +0 -1
  123. package/dist/src/ser-des/config/storyline.js +0 -264
  124. package/dist/src/ser-des/config/storyline.js.map +0 -1
  125. package/dist/src/ser-des/config/workflowConfig.d.ts +0 -5
  126. package/dist/src/ser-des/config/workflowConfig.d.ts.map +0 -1
  127. package/dist/src/ser-des/config/workflowConfig.js +0 -129
  128. package/dist/src/ser-des/config/workflowConfig.js.map +0 -1
  129. package/dist/src/ser-des/config/workflowStage.d.ts +0 -5
  130. package/dist/src/ser-des/config/workflowStage.d.ts.map +0 -1
  131. package/dist/src/ser-des/config/workflowStage.js +0 -96
  132. package/dist/src/ser-des/config/workflowStage.js.map +0 -1
  133. package/dist/src/types/Storyline.d.ts +0 -62
  134. package/dist/src/types/Storyline.d.ts.map +0 -1
  135. package/dist/src/types/Storyline.js +0 -51
  136. package/dist/src/types/Storyline.js.map +0 -1
package/dist/src/check.js CHANGED
@@ -360,22 +360,6 @@
360
360
  // part_annex register's declaration shape — letters unique per
361
361
  // package, the source provenance required; the obligation
362
362
  // vocabulary is parse-enforced upstream
363
- // C129 storyline-shape (smart TODO.roadmap/40 batch 4): the demo-seed
364
- // shapes — the id_prefix pattern (register-free), party →
365
- // demo_world participant seeds (gated), record stores → entity-
366
- // class stores (gated), in-construct record cross-references
367
- // (ungated); field-level discipline stays app-side
368
- // C144–C146 the license facets (smart TODO.external-refs/04; the
369
- // entitlement facet + catalog): the catalog-key shape
370
- // (^std:[a-z0-9-]+$), at most one license_key per package (the
371
- // parser's earlier declarations surface here), and the
372
- // license_holder required with the key — a package without a key
373
- // is public content and every leg keys on the key's presence
374
- // C147–C148 the executable test programs (smart TODO.twin-demo/03;
375
- // the model-drive bindings): the preparation/stimulus entry-order
376
- // and step/point shape, and the hold/fresh_within duration
377
- // discipline (a stimulus point declares fresh_within — §14.12's
378
- // no-stale-semantics doctrine)
379
363
  //
380
364
  // Levels (TODO.roadmap/17): the DEFAULT level runs the normal-level
381
365
  // rules at their catalog severities. --audit additionally runs the
@@ -402,6 +386,10 @@
402
386
  Object.defineProperty(exports, "__esModule", { value: true });
403
387
  exports.checkPackage = checkPackage;
404
388
  exports.checkManifestResolution = checkManifestResolution;
389
+ exports.checkArtifactContracts = checkArtifactContracts;
390
+ exports.checkTypedPayloads = checkTypedPayloads;
391
+ exports.checkAttestations = checkAttestations;
392
+ exports.checkClosedDerivations = checkClosedDerivations;
405
393
  exports.checkSpellingCodes = checkSpellingCodes;
406
394
  exports.checkDeclaredPredicates = checkDeclaredPredicates;
407
395
  exports.checkTermAliases = checkTermAliases;
@@ -426,6 +414,7 @@ const FormulasUsed_1 = require("./types/FormulasUsed");
426
414
  const tokenize_1 = require("./ser-des/tokenize");
427
415
  const operational_state_1 = require("./operational-state");
428
416
  const check_rules_1 = require("./check-rules");
417
+ const instance_resolution_1 = require("./instance-resolution");
429
418
  const check_allowlist_1 = require("./check-allowlist");
430
419
  const text_coverage_1 = require("./text-coverage");
431
420
  const spelling_1 = require("./spelling");
@@ -490,7 +479,12 @@ function checkPackage(dir, options = {}) {
490
479
  });
491
480
  standard = loaded.standard;
492
481
  for (const i of loaded.issues) {
493
- if (i.code === 'provides-unconsumed') {
482
+ // The deprecation report (phase 2): the parse-time issue maps to
483
+ // its rule.
484
+ if (i.code === 'construct-deprecated') {
485
+ warn('C154', i.message);
486
+ }
487
+ else if (i.code === 'provides-unconsumed') {
494
488
  warn('C30', i.message);
495
489
  }
496
490
  else if (i.code === 'extends-deprecated') {
@@ -545,11 +539,20 @@ function checkPackage(dir, options = {}) {
545
539
  issues.push(...checkReferences(standard));
546
540
  // ── C116/C117: the verdict chain (MN 114 v3.2, clause 11.3) ────────
547
541
  issues.push(...checkVerdictChains(standard));
542
+ // ── C149: the closed-derivation rule (the typed kernel, R3) ────────
543
+ issues.push(...checkClosedDerivations(standard));
544
+ // ── C150/C151: the attestation's references and claim lineage ──────
545
+ issues.push(...checkAttestations(standard));
546
+ // ── C152: the typed payload rule (the typed kernel, R2) ────────────
547
+ issues.push(...checkTypedPayloads(standard));
548
+ // ── C153: the contract-derivation facet (the typed kernel, clause 10)
549
+ issues.push(...checkArtifactContracts(standard));
548
550
  const reqIds = new Set((standard.requirements ?? []).map((r) => r.id));
549
551
  const testIds = new Set((standard.conformanceTests ?? []).map((t) => t.id));
550
552
  const attrIds = new Set((standard.attributeDefinitions ?? []).map((a) => a.id));
553
+ const roleIds = new Set((standard.roles ?? []).map(r => r.id));
551
554
  const behaviorIds = new Set((standard.behaviors ?? []).map((b) => b.id));
552
- const subjectIds = new Set((standard.subjects ?? []).map((s) => s.id));
555
+ const subjectIds = new Set((standard.subjects ?? []).map(s => s.id));
553
556
  // Observables live in the symbols registry, not the attribute layer —
554
557
  // binds_to / limit.uses may reference them (e.g. sample.test_context
555
558
  // quantities that are measured test outputs).
@@ -1162,7 +1165,9 @@ function checkPackage(dir, options = {}) {
1162
1165
  // C45 — artifact-def-contract: the content contract is well-formed.
1163
1166
  for (const d of standard.artifactDefinitions ?? []) {
1164
1167
  const c = d.contentContract;
1165
- if (c.fields.length === 0) {
1168
+ // A derived contract (contract_from, C153) is the referenced
1169
+ // promise set's projection — its fields are the set's.
1170
+ if (!d.contractFrom && c.fields.length === 0) {
1166
1171
  err('C45', `artifact definition ${d.id}: content contract declares no fields — a definition must contract SOME content (artifact-def-contract)`);
1167
1172
  }
1168
1173
  const seenFields = new Set();
@@ -1697,15 +1702,6 @@ function checkPackage(dir, options = {}) {
1697
1702
  }
1698
1703
  }
1699
1704
  }
1700
- for (const m of standard.processModels ?? []) {
1701
- const where = `process_model ${m.id}`;
1702
- for (const s of m.sequence ?? []) {
1703
- resolveProcessRef(where, 'sequence', s);
1704
- }
1705
- for (const r of m.registers ?? []) {
1706
- resolveOrganAp(`${where} register ${r.id}`, 'maintainer', r.maintainer);
1707
- }
1708
- }
1709
1705
  }
1710
1706
  // ── C84: constraint-shape (TODO.roadmap/51 — BUG.R60-SSOT gap 7) ─────
1711
1707
  // The subject-intrinsic constraint's DECLARATION shape — the kernel
@@ -2039,7 +2035,6 @@ function checkPackage(dir, options = {}) {
2039
2035
  // escalation ACTIONS are runtime — the smart app's monitor service owns
2040
2036
  // them; these rules guarantee the declarations the runtime needs.
2041
2037
  {
2042
- const roleIds = new Set((standard.roles ?? []).map(r => r.id));
2043
2038
  const KNOWN_OUTCOMES = new Set(Monitor_1.MONITOR_OUTCOMES);
2044
2039
  const KNOWN_ACTIONS = new Set(Monitor_1.MONITOR_ESCALATION_ACTIONS);
2045
2040
  const KNOWN_STREAMS = new Set(Monitor_1.MONITOR_STREAMS);
@@ -2343,6 +2338,7 @@ function checkPackage(dir, options = {}) {
2343
2338
  };
2344
2339
  const instrumentIds = new Set((standard.instruments ?? []).map(i => i.id));
2345
2340
  const instanceIds = new Set((standard.instances ?? []).map(i => i.id));
2341
+ const dataclassIds = new Set((standard.dataclasses ?? []).map(c => c.id));
2346
2342
  const attrScopes = new Map((standard.attributeDefinitions ?? []).map(a => [a.id, a.scope]));
2347
2343
  // Classification-dimension scopes, by every spelling a classification
2348
2344
  // map may use: the instrument dimension id, the is_dimension attribute
@@ -2436,9 +2432,53 @@ function checkPackage(dir, options = {}) {
2436
2432
  }
2437
2433
  }
2438
2434
  // C20 — instance-of-resolves: the `of` reference names a declared
2439
- // subject (v3) or instrument (v2) definition.
2440
- if (!subjectIds.has(inst.of) && !instrumentIds.has(inst.of)) {
2441
- err('C20', `instance ${inst.id}: of "${inst.of}" is not a declared subject or instrument (instance-of-resolves)`);
2435
+ // subject (v3), instrument (v2), or ANOTHER INSTANCE (the power-type
2436
+ // discipline — every instance can serve as the definition of further
2437
+ // instantiation).
2438
+ if (!subjectIds.has(inst.of) &&
2439
+ !instrumentIds.has(inst.of) &&
2440
+ !instanceIds.has(inst.of) &&
2441
+ !dataclassIds.has(inst.of)) {
2442
+ err('C20', `instance ${inst.id}: of "${inst.of}" is not a declared subject, instrument, class, or instance (instance-of-resolves)`);
2443
+ }
2444
+ // C155 — power-type-chain (coherence leg): an instance whose `of`
2445
+ // names another instance carries NO upward subject-chain link — the
2446
+ // two are one pattern's two spellings, and mixing them makes the
2447
+ // delegation walk ambiguous.
2448
+ if (instanceIds.has(inst.of)) {
2449
+ const links = ['model', 'group', 'family'].filter(k => inst[k]);
2450
+ if (links.length > 0) {
2451
+ err('C155', `instance ${inst.id}: of "${inst.of}" names an instance and the instance carries an upward ${links.join('/')} link — the power-type chain and the subject-chain profile do not mix (power-type-chain)`);
2452
+ }
2453
+ }
2454
+ }
2455
+ // C155 — power-type-chain (acyclicity leg): the MIXED delegation
2456
+ // graph — of-chains to instances AND the prototype hop (an `of` to a
2457
+ // class whose extends names an instance) — is acyclic: a cycle over
2458
+ // these edges would make the delegation walk non-terminating, and a
2459
+ // class instantiating its own prototype (`instance x of C` while
2460
+ // `C extends { x }`) is the shapes' own mutual recursion.
2461
+ const delegation = (0, instance_resolution_1.delegationSuccessors)(standard);
2462
+ const ofAdj = new Map();
2463
+ for (const [from, to] of delegation) {
2464
+ if (instanceIds.has(to)) {
2465
+ ofAdj.set(from, [to]);
2466
+ }
2467
+ }
2468
+ const ofCycle = findCycle(ofAdj);
2469
+ if (ofCycle) {
2470
+ err('C155', `the power-type of-chain is cyclic: ${ofCycle.join(' → ')} (power-type-chain)`);
2471
+ }
2472
+ // C156 — class-extends-resolves: a class's extends names a declared
2473
+ // class or a declared INSTANCE (the definition side of the power-type
2474
+ // discipline — a class extending an instance specializes that
2475
+ // individual). Successfully merged links clear at resolve time, so a
2476
+ // surviving link is exactly an unresolved one.
2477
+ for (const c of standard.dataclasses ?? []) {
2478
+ if (c.extends &&
2479
+ !dataclassIds.has(c.extends) &&
2480
+ !instanceIds.has(c.extends)) {
2481
+ warn('C156', `class ${c.id}: extends "${c.extends}" is not a declared class or instance (class-extends-resolves)`);
2442
2482
  }
2443
2483
  }
2444
2484
  // C19 — chain-acyclic (integrity part 2): the resolved link graph is
@@ -3694,68 +3734,6 @@ function checkPackage(dir, options = {}) {
3694
3734
  }
3695
3735
  }
3696
3736
  }
3697
- // ── C129: storyline-shape (smart TODO.roadmap/40 batch 4; the ──────
3698
- // packages-as-SSOT epic) ────────────────────────────────────────────
3699
- // The demo-seed shapes: the id_prefix pattern (the register-free shape
3700
- // leg); the party laboratory/authority resolve against the package's
3701
- // participant seeds WHEN a demo_world with participants is in scope
3702
- // (per-register gating, the C58 doctrine); the record stores resolve
3703
- // against the declared entity-class stores WHEN that register is in
3704
- // scope; and the in-construct cross-references resolve — a record
3705
- // field keyed by a sibling record's store names a sibling record of
3706
- // that store (in-construct, ungated). The field-level discipline (the
3707
- // schema's per-slot required fields) stays app-side.
3708
- {
3709
- const participantSeeds = new Set();
3710
- for (const w of standard.demoWorlds ?? []) {
3711
- for (const section of w.participants ?? []) {
3712
- for (const e of section.entries ?? []) {
3713
- participantSeeds.add(e.id);
3714
- }
3715
- }
3716
- }
3717
- const entityStores = new Set((standard.dataclasses ?? [])
3718
- .map(c => c.store)
3719
- .filter((s) => !!s));
3720
- for (const s of standard.storylines ?? []) {
3721
- const where = `storyline ${s.id}`;
3722
- if (s.idPrefix && !/^[a-z0-9-]+$/.test(s.idPrefix)) {
3723
- err('C129', `${where}: id_prefix "${s.idPrefix}" does not match ^[a-z0-9-]+$ — the id-minting prefix is a lowercase slug (storyline-shape)`);
3724
- }
3725
- if (participantSeeds.size > 0) {
3726
- for (const [slot, ref] of [
3727
- ['laboratory', s.party.laboratory],
3728
- ['authority', s.party.authority],
3729
- ]) {
3730
- if (ref && !participantSeeds.has(ref)) {
3731
- err('C129', `${where}: party ${slot} "${ref}" is not a declared participant seed (storyline-shape)`);
3732
- }
3733
- }
3734
- }
3735
- const siblingStores = new Map();
3736
- for (const r of s.records ?? []) {
3737
- if (entityStores.size > 0 && !entityStores.has(r.store)) {
3738
- err('C129', `${where}: record store "${r.store}" is not a declared entity-class store (storyline-shape)`);
3739
- }
3740
- let ids = siblingStores.get(r.store);
3741
- if (!ids) {
3742
- ids = new Set();
3743
- siblingStores.set(r.store, ids);
3744
- }
3745
- ids.add(r.id);
3746
- }
3747
- for (const r of s.records ?? []) {
3748
- for (const [key, value] of Object.entries(r.fields)) {
3749
- const targets = siblingStores.get(key);
3750
- if (targets !== undefined &&
3751
- !Array.isArray(value) &&
3752
- !targets.has(String(value))) {
3753
- err('C129', `${where}: record ${r.store} ${r.id}: ${key} "${String(value)}" is not a sibling ${key} record of this storyline (storyline-shape)`);
3754
- }
3755
- }
3756
- }
3757
- }
3758
- }
3759
3737
  // ── C130: identity-and-aspect-references (smart TODO.roadmap/40 ─────
3760
3738
  // batch 3; the packages-as-SSOT epic) ────────────────────────────────
3761
3739
  // The documentary identity slots + the qualitative aspect register
@@ -4126,7 +4104,6 @@ function checkPackage(dir, options = {}) {
4126
4104
  // (the R26 field-resolution precedent). The phase vocabulary is
4127
4105
  // parse-enforced upstream (no check leg).
4128
4106
  {
4129
- const roleIds = new Set((standard.roles ?? []).map(r => r.id));
4130
4107
  // Class ids may carry a namespace suffix (`Application#data`) while
4131
4108
  // the workflow's inputs/outputs spell the bare entity name — index
4132
4109
  // both spellings (the semantic-status leg's precedent).
@@ -4137,28 +4114,6 @@ function checkPackage(dir, options = {}) {
4137
4114
  dataclassIds.add(c.id.slice(0, c.id.indexOf('#')));
4138
4115
  }
4139
4116
  }
4140
- const CLEAN_TOKEN = /^[A-Za-z_][A-Za-z0-9_]*$/;
4141
- for (const w of standard.workflowConfigs ?? []) {
4142
- for (const s of w.steps ?? []) {
4143
- const where = `workflow_config ${w.id}: step ${s.id}`;
4144
- if (s.actor && roleIds.size > 0 && !roleIds.has(s.actor)) {
4145
- err('C137', `${where}: actor "${s.actor}" is not a declared role (workflow-config-references)`);
4146
- }
4147
- for (const [facet, list] of [
4148
- ['inputs', s.inputs],
4149
- ['outputs', s.outputs],
4150
- ]) {
4151
- for (const entry of list) {
4152
- if (!CLEAN_TOKEN.test(entry)) {
4153
- continue; // a composite documentary string — never resolved
4154
- }
4155
- if (dataclassIds.size > 0 && !dataclassIds.has(entry)) {
4156
- err('C137', `${where}: ${facet} entry "${entry}" is not a declared data class (workflow-config-references)`);
4157
- }
4158
- }
4159
- }
4160
- }
4161
- }
4162
4117
  }
4163
4118
  // ── C138: verification-pathway-references (smart TODO.roadmap/40 ───
4164
4119
  // batch 3; the packages-as-SSOT epic) ────────────────────────────────
@@ -4405,26 +4360,6 @@ function checkPackage(dir, options = {}) {
4405
4360
  }
4406
4361
  // The workflow_stage members-resolve legs (step 5d) — the events
4407
4362
  // stay documentary, never resolved.
4408
- const stageApprovalIds = new Set((standard.approvals ?? []).map(a => a.id));
4409
- const stageGatewayIds = new Set((standard.gateways ?? []).map(g => g.id));
4410
- for (const s of standard.workflowStages ?? []) {
4411
- const where = `workflow_stage ${s.id}`;
4412
- for (const el of s.elements ?? []) {
4413
- if (gwProcessIds.size > 0 && !gwProcessIds.has(el)) {
4414
- err('C142', `${where}: element "${el}" is not a declared process (gateway-edges-resolve)`);
4415
- }
4416
- }
4417
- for (const a of s.approvals ?? []) {
4418
- if (stageApprovalIds.size > 0 && !stageApprovalIds.has(a)) {
4419
- err('C142', `${where}: approval "${a}" is not a declared approval (gateway-edges-resolve)`);
4420
- }
4421
- }
4422
- for (const g of s.gateways ?? []) {
4423
- if (stageGatewayIds.size > 0 && !stageGatewayIds.has(g)) {
4424
- err('C142', `${where}: gateway "${g}" is not a declared gateway (gateway-edges-resolve)`);
4425
- }
4426
- }
4427
- }
4428
4363
  }
4429
4364
  // ── C143: approval-references-resolve (smart TODO.roadmap/40 batch ──
4430
4365
  // 5; the packages-as-SSOT epic) ─────────────────────────────────────
@@ -5678,6 +5613,362 @@ function textAddressKey(item, segment) {
5678
5613
  * (titular:source:target:identifying); a `zz-` user-assigned code
5679
5614
  * validates with a warning (user-assigned codes are not portable).
5680
5615
  */
5616
+ // ── C153: artifact-contract-derivation (the typed kernel, clause 10) ─
5617
+ // An artifact definition whose content is the projection of declared
5618
+ // claims declares contract_from; the referenced set resolves, and a
5619
+ // definition that restates inline fields while deriving its contract
5620
+ // is the restatement the rule exists to prevent.
5621
+ function checkArtifactContracts(standard) {
5622
+ const issues = [];
5623
+ const err = (check, message) => issues.push({ check, severity: 'error', message });
5624
+ const setIds = new Set();
5625
+ for (const ps of standard.promiseSets ?? []) {
5626
+ setIds.add(ps.id);
5627
+ setIds.add(`${ps.id}.promise_set`);
5628
+ }
5629
+ for (const s of standard.subjects ?? []) {
5630
+ if ((s.is?.promises ?? []).length > 0) {
5631
+ setIds.add(s.id);
5632
+ setIds.add(`${s.id}.promise_set`);
5633
+ }
5634
+ }
5635
+ for (const d of standard.artifactDefinitions ?? []) {
5636
+ if (!d.contractFrom) {
5637
+ continue;
5638
+ }
5639
+ if (!setIds.has(d.contractFrom)) {
5640
+ err('C153', `artifact_definition ${d.id}: contract_from "${d.contractFrom}" does not resolve to a declared promise set (artifact-contract-derivation)`);
5641
+ }
5642
+ const c = d.contentContract;
5643
+ if (c.fields.length > 0 || c.structure || c.media.length > 0) {
5644
+ err('C153', `artifact_definition ${d.id}: declares both contract_from and inline contract fields — the derived contract is the referenced set's projection, never a restatement (artifact-contract-derivation)`);
5645
+ }
5646
+ }
5647
+ return issues;
5648
+ }
5649
+ // ── C152: payload-typed-by-class (the typed kernel R2, clause 10) ────
5650
+ // A dimension that declares payload_class types every value's
5651
+ // definition: the class resolves, every payload field is a field of
5652
+ // that class, and every value carries its definition — a value IS its
5653
+ // definition, not a symbol pointing at a table. The attribute-side
5654
+ // bridge resolves too: an attribute_definition's class reference names
5655
+ // a declared class.
5656
+ function checkTypedPayloads(standard) {
5657
+ const issues = [];
5658
+ const err = (check, message) => issues.push({ check, severity: 'error', message });
5659
+ const classFields = new Map();
5660
+ for (const c of standard.dataclasses ?? []) {
5661
+ classFields.set(c.id, new Set(c.attributes.map(a => a.id)));
5662
+ }
5663
+ // The power-type discipline reaches the typing bridge: an INSTANCE
5664
+ // is a class, and its shape is the fields of the class its `of`
5665
+ // chain terminates at (the prototype hop's mirror — an individual
5666
+ // typed by its definition's fields).
5667
+ const instanceShape = (id) => {
5668
+ const inst = (standard.instances ?? []).find(i => i.id === id);
5669
+ if (!inst) {
5670
+ return undefined;
5671
+ }
5672
+ let cur = inst;
5673
+ const seen = new Set([cur.id]);
5674
+ while (cur.of) {
5675
+ const next = (standard.instances ?? []).find(i => i.id === cur.of);
5676
+ if (!next || seen.has(next.id)) {
5677
+ break;
5678
+ }
5679
+ seen.add(next.id);
5680
+ cur = next;
5681
+ }
5682
+ return cur.of !== '' ? classFields.get(cur.of) : undefined;
5683
+ };
5684
+ const typedFields = (id) => classFields.get(id) ?? instanceShape(id);
5685
+ const checkDim = (owner, d) => {
5686
+ if (!d.payloadClass) {
5687
+ return;
5688
+ }
5689
+ const fields = typedFields(d.payloadClass);
5690
+ if (!fields) {
5691
+ err('C152', `${owner} dimension ${d.id}: payload_class "${d.payloadClass}" is not a declared class (payload-typed-by-class)`);
5692
+ return;
5693
+ }
5694
+ for (const v of d.values) {
5695
+ const keys = Object.keys(v.payload ?? {});
5696
+ if (keys.length === 0) {
5697
+ err('C152', `${owner} dimension ${d.id}: value ${v.id} carries no payload — a value of a payload_class dimension IS its definition (payload-typed-by-class)`);
5698
+ continue;
5699
+ }
5700
+ for (const key of keys) {
5701
+ if (!fields.has(key)) {
5702
+ err('C152', `${owner} dimension ${d.id}: value ${v.id} payload field "${key}" is not a field of class "${d.payloadClass}" (payload-typed-by-class)`);
5703
+ }
5704
+ }
5705
+ }
5706
+ };
5707
+ for (const inst of standard.instruments ?? []) {
5708
+ for (const d of inst.dimensions ?? []) {
5709
+ checkDim(`instrument ${inst.id}`, d);
5710
+ }
5711
+ }
5712
+ for (const d of standard.dimensions ?? []) {
5713
+ checkDim('top-level', d);
5714
+ }
5715
+ for (const a of standard.attributeDefinitions ?? []) {
5716
+ if (a.classRef && !typedFields(a.classRef)) {
5717
+ err('C152', `attribute_definition ${a.id}: class "${a.classRef}" is not a declared class (payload-typed-by-class)`);
5718
+ }
5719
+ }
5720
+ return issues;
5721
+ }
5722
+ // ── C150/C151: the attestation's references and claim lineage ───────
5723
+ // C150 attestation-references-resolve: the subject is a declared
5724
+ // instance, the promise set resolves in `Subject.promise_set_id` form,
5725
+ // every basis entry carries kind and id, and every claim's declaration
5726
+ // and verdict references are stated. C151
5727
+ // attestation-claim-lineage: every promise the certificate prints as
5728
+ // mandatory must be carried by a claim — a mandatory characteristic is
5729
+ // carried by lineage, never restated (clause 19).
5730
+ function checkAttestations(standard) {
5731
+ const issues = [];
5732
+ const err = (check, message) => issues.push({ check, severity: 'error', message });
5733
+ const roleIds = new Set((standard.roles ?? []).map(r => r.id));
5734
+ const instanceIds = new Set((standard.instances ?? []).map(i => i.id));
5735
+ const verdictIds = new Set((standard.verdicts ?? []).map(v => v.id));
5736
+ const setViews = new Map();
5737
+ for (const ps of standard.promiseSets ?? []) {
5738
+ setViews.set(ps.id, { promises: ps.promises });
5739
+ setViews.set(`${ps.id}.promise_set`, { promises: ps.promises });
5740
+ }
5741
+ for (const s of standard.subjects ?? []) {
5742
+ if ((s.is?.promises ?? []).length > 0) {
5743
+ setViews.set(s.id, { promises: s.is.promises });
5744
+ }
5745
+ }
5746
+ const resolveSet = (ref) => setViews.get(ref) ??
5747
+ setViews.get(ref.split('.').slice(1).join('.')) ??
5748
+ null;
5749
+ const mandatoryOf = (view) => {
5750
+ const out = new Set();
5751
+ for (const p of view.promises) {
5752
+ if (p.certificate?.obligation === 'mandatory') {
5753
+ out.add(p.id);
5754
+ }
5755
+ }
5756
+ return out;
5757
+ };
5758
+ for (const a of standard.attestations ?? []) {
5759
+ if (!a.subject) {
5760
+ err('C150', `attestation ${a.id}: no subject facet (attestation-references-resolve)`);
5761
+ }
5762
+ else if (!instanceIds.has(a.subject)) {
5763
+ err('C150', `attestation ${a.id}: subject "${a.subject}" is not a declared instance (attestation-references-resolve)`);
5764
+ }
5765
+ if (!a.promises) {
5766
+ err('C150', `attestation ${a.id}: no promises facet (attestation-references-resolve)`);
5767
+ }
5768
+ const set = a.promises ? resolveSet(a.promises) : null;
5769
+ if (a.promises && !set) {
5770
+ err('C150', `attestation ${a.id}: promises "${a.promises}" does not resolve to a declared promise set (attestation-references-resolve)`);
5771
+ }
5772
+ for (const b of a.basis) {
5773
+ if (!b.kind || !b.id) {
5774
+ err('C150', `attestation ${a.id}: a basis entry needs an evidence kind and an id (attestation-references-resolve)`);
5775
+ }
5776
+ }
5777
+ if (a.authority.role &&
5778
+ roleIds.size > 0 &&
5779
+ !roleIds.has(a.authority.role)) {
5780
+ err('C150', `attestation ${a.id}: authority role "${a.authority.role}" is not a declared role (attestation-references-resolve)`);
5781
+ }
5782
+ const carried = new Set(a.claims.map(c => c.promise));
5783
+ for (const c of a.claims) {
5784
+ if (set && !set.promises.some(p => p.id === c.promise)) {
5785
+ err('C150', `attestation ${a.id}: claim "${c.promise}" is not a promise of "${a.promises}" (attestation-references-resolve)`);
5786
+ }
5787
+ if (!c.declared) {
5788
+ err('C151', `attestation ${a.id}: claim "${c.promise}" carries no declaration reference (attestation-claim-lineage)`);
5789
+ }
5790
+ if (!c.validatedBy) {
5791
+ err('C151', `attestation ${a.id}: claim "${c.promise}" carries no verdict reference (attestation-claim-lineage)`);
5792
+ }
5793
+ else if (verdictIds.size > 0 &&
5794
+ !c.validatedBy.startsWith('/') &&
5795
+ !verdictIds.has(c.validatedBy)) {
5796
+ // Bare ids resolve inside the package; slash ids are the
5797
+ // requirement-anatomy path form and resolve in the linker.
5798
+ err('C150', `attestation ${a.id}: claim "${c.promise}" names verdict "${c.validatedBy}", which is not declared (attestation-references-resolve)`);
5799
+ }
5800
+ }
5801
+ if (set) {
5802
+ for (const pid of mandatoryOf(set)) {
5803
+ if (!carried.has(pid)) {
5804
+ err('C151', `attestation ${a.id}: the mandatory promise "${pid}" of "${a.promises}" is carried by no claim — a mandatory characteristic is carried by lineage, never restated (attestation-claim-lineage)`);
5805
+ }
5806
+ }
5807
+ }
5808
+ }
5809
+ return issues;
5810
+ }
5811
+ // ── C149: closed-derivation-references-declared ─────────────────────
5812
+ // The typed kernel's rule R3: a derivation expression names only
5813
+ // declared definitions — declared calculations, tables, symbols,
5814
+ // measurements/variables, data classes, enums, and attributes of the
5815
+ // model. The checker rejects an expression that references anything
5816
+ // else and names the missing declaration, so the undefined-reference
5817
+ // defect class cannot be authored.
5818
+ const CLOSED_DERIVATION_STOP = new Set([
5819
+ 'self',
5820
+ 'p',
5821
+ 'ocl',
5822
+ 'true',
5823
+ 'false',
5824
+ 'null',
5825
+ 'undefined',
5826
+ // OCL navigation and collection operators
5827
+ 'and',
5828
+ 'or',
5829
+ 'not',
5830
+ 'implies',
5831
+ 'if',
5832
+ 'then',
5833
+ 'else',
5834
+ 'endif',
5835
+ 'xor',
5836
+ 'let',
5837
+ 'in',
5838
+ 'exists',
5839
+ 'forAll',
5840
+ 'select',
5841
+ 'reject',
5842
+ 'collect',
5843
+ 'isEmpty',
5844
+ 'size',
5845
+ 'sum',
5846
+ 'min',
5847
+ 'max',
5848
+ 'abs',
5849
+ 'round',
5850
+ 'floor',
5851
+ 'sqrt',
5852
+ 'includes',
5853
+ 'excludes',
5854
+ 'includesAll',
5855
+ 'excludesAll',
5856
+ 'count',
5857
+ 'first',
5858
+ 'last',
5859
+ 'asSet',
5860
+ 'asOrderedSet',
5861
+ 'asSequence',
5862
+ 'asBag',
5863
+ 'sortedBy',
5864
+ 'iterate',
5865
+ 'oclIsUndefined',
5866
+ 'oclIsKindOf',
5867
+ 'oclIsTypeOf',
5868
+ 'allInstances',
5869
+ // table-lookup engine vocabulary (the lookup facet names these)
5870
+ 'lookup',
5871
+ 'profile',
5872
+ // OCL collection statistics and arithmetic
5873
+ 'average',
5874
+ 'mean',
5875
+ 'size',
5876
+ 'div',
5877
+ 'mod',
5878
+ 'collect',
5879
+ ]);
5880
+ // Attribute navigation: `head.field…` resolves when the HEAD is a
5881
+ // declared name (an input, a calculation, an attribute, a class-typed
5882
+ // value); the tail is navigation over that head's type, judged by the
5883
+ // class layer (C152), not by the closed-derivation rule.
5884
+ const CLOSED_DERIVATION_PATH_RE = /\b[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+/g;
5885
+ const CLOSED_DERIVATION_IDENT_RE = /[A-Za-z_][A-Za-z0-9_]*/g;
5886
+ function checkClosedDerivations(standard) {
5887
+ const issues = [];
5888
+ const err = (check, message) => issues.push({ check, severity: 'error', message });
5889
+ // The declared universe every derivation may reference.
5890
+ const declared = new Set();
5891
+ for (const c of standard.calculations ?? []) {
5892
+ declared.add(c.id);
5893
+ declared.add(c.name);
5894
+ }
5895
+ for (const tb of standard.tables ?? []) {
5896
+ declared.add(tb.id);
5897
+ }
5898
+ for (const s of standard.symbols ?? []) {
5899
+ declared.add(s.id);
5900
+ }
5901
+ for (const v of standard.variables ?? []) {
5902
+ declared.add(v.id);
5903
+ }
5904
+ for (const a of standard.attributeDefinitions ?? []) {
5905
+ declared.add(a.id);
5906
+ }
5907
+ for (const e of standard.enums ?? []) {
5908
+ declared.add(e.id);
5909
+ }
5910
+ for (const d of standard.dataclasses ?? []) {
5911
+ declared.add(d.id);
5912
+ }
5913
+ for (const inst of standard.instruments ?? []) {
5914
+ for (const d of inst.dimensions ?? []) {
5915
+ declared.add(d.id);
5916
+ }
5917
+ }
5918
+ for (const d of standard.dimensions ?? []) {
5919
+ declared.add(d.id);
5920
+ }
5921
+ // A calculation's own output name is declared too (requirements bind
5922
+ // it), and so is every input of every calculation.
5923
+ for (const c of standard.calculations ?? []) {
5924
+ if (c.output?.name) {
5925
+ declared.add(c.output.name);
5926
+ }
5927
+ for (const i of c.inputs ?? []) {
5928
+ declared.add(i.name);
5929
+ }
5930
+ }
5931
+ const localsOf = (extra) => new Set([...declared, ...extra]);
5932
+ const checkExpression = (expr, locals, where) => {
5933
+ if (!expr) {
5934
+ return;
5935
+ }
5936
+ // Values and lambdas are not references: single-quoted literals are
5937
+ // values; an identifier before `|` is a lambda parameter.
5938
+ const noLiterals = expr.replace(/'[^']*'/g, ' ');
5939
+ const noLambdas = noLiterals.replace(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\|/g, ' ');
5940
+ const stripped = noLambdas.replace(CLOSED_DERIVATION_PATH_RE, ' ');
5941
+ const seen = new Set();
5942
+ for (const raw of stripped.match(CLOSED_DERIVATION_IDENT_RE) ?? []) {
5943
+ if (CLOSED_DERIVATION_STOP.has(raw) ||
5944
+ locals.has(raw) ||
5945
+ seen.has(raw) ||
5946
+ /^[a-z][0-9]?$/.test(raw)) {
5947
+ continue;
5948
+ }
5949
+ seen.add(raw);
5950
+ err('C149', `${where}: expression references "${raw}", which no declared calculation, table, symbol, measurement, or attribute defines (closed-derivation-references-declared)`);
5951
+ }
5952
+ };
5953
+ for (const a of standard.attributeDefinitions ?? []) {
5954
+ checkExpression(a.derived, localsOf([]), `attribute_definition ${a.id}`);
5955
+ }
5956
+ for (const c of standard.calculations ?? []) {
5957
+ // The normative body is the declared expression; engine variants
5958
+ // are consumer code and outside the language's semantics.
5959
+ const inputs = (c.inputs ?? []).map(i => i.name);
5960
+ checkExpression(c.expression, localsOf(inputs), `calculation ${c.id}`);
5961
+ }
5962
+ for (const s of standard.symbols ?? []) {
5963
+ if (s.formula?.expression) {
5964
+ checkExpression(s.formula.expression, localsOf(s.formula.inputs ?? []), `symbol ${s.id}`);
5965
+ }
5966
+ }
5967
+ for (const v of standard.verdicts ?? []) {
5968
+ checkExpression(v.derive, localsOf(v.inputs ?? []), `verdict ${v.id}`);
5969
+ }
5970
+ return issues;
5971
+ }
5681
5972
  function checkSpellingCodes(standard) {
5682
5973
  const issues = [];
5683
5974
  const err = (check, message) => issues.push({ check, severity: 'error', message });