@primmel/primmel 1.19.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.
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/src/check-rules.d.ts +1 -1
- package/dist/src/check-rules.d.ts.map +1 -1
- package/dist/src/check-rules.js +53 -15
- package/dist/src/check-rules.js.map +1 -1
- package/dist/src/check.d.ts +4 -0
- package/dist/src/check.d.ts.map +1 -1
- package/dist/src/check.js +519 -132
- package/dist/src/check.js.map +1 -1
- package/dist/src/export/cddal.d.ts +25 -0
- package/dist/src/export/cddal.d.ts.map +1 -0
- package/dist/src/export/cddal.js +213 -0
- package/dist/src/export/cddal.js.map +1 -0
- package/dist/src/export/retrieval.d.ts +147 -6
- package/dist/src/export/retrieval.d.ts.map +1 -1
- package/dist/src/export/retrieval.js +254 -25
- package/dist/src/export/retrieval.js.map +1 -1
- package/dist/src/export/unitsdb.d.ts +19 -0
- package/dist/src/export/unitsdb.d.ts.map +1 -0
- package/dist/src/export/unitsdb.js +82 -0
- package/dist/src/export/unitsdb.js.map +1 -0
- package/dist/src/export/yaml-lite.d.ts +13 -0
- package/dist/src/export/yaml-lite.d.ts.map +1 -0
- package/dist/src/export/yaml-lite.js +219 -0
- package/dist/src/export/yaml-lite.js.map +1 -0
- package/dist/src/instance-resolution.d.ts +19 -0
- package/dist/src/instance-resolution.d.ts.map +1 -1
- package/dist/src/instance-resolution.js +87 -1
- package/dist/src/instance-resolution.js.map +1 -1
- package/dist/src/model-diff.d.ts.map +1 -1
- package/dist/src/model-diff.js +0 -5
- package/dist/src/model-diff.js.map +1 -1
- package/dist/src/runtime.d.ts +143 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +648 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/ser-des/config/artifact.d.ts.map +1 -1
- package/dist/src/ser-des/config/artifact.js +6 -0
- package/dist/src/ser-des/config/artifact.js.map +1 -1
- package/dist/src/ser-des/config/attestation.d.ts +5 -0
- package/dist/src/ser-des/config/attestation.d.ts.map +1 -0
- package/dist/src/ser-des/config/attestation.js +143 -0
- package/dist/src/ser-des/config/attestation.js.map +1 -0
- package/dist/src/ser-des/config/conformanceTest.d.ts.map +1 -1
- package/dist/src/ser-des/config/conformanceTest.js +310 -0
- package/dist/src/ser-des/config/conformanceTest.js.map +1 -1
- package/dist/src/ser-des/config/data.d.ts.map +1 -1
- package/dist/src/ser-des/config/data.js +98 -0
- package/dist/src/ser-des/config/data.js.map +1 -1
- package/dist/src/ser-des/config/documentModule.js +5 -5
- package/dist/src/ser-des/config/documentModule.js.map +1 -1
- package/dist/src/ser-des/config/index.d.ts +8 -0
- package/dist/src/ser-des/config/index.d.ts.map +1 -1
- package/dist/src/ser-des/config/index.js +22 -49
- package/dist/src/ser-des/config/index.js.map +1 -1
- package/dist/src/ser-des/config/instance.d.ts.map +1 -1
- package/dist/src/ser-des/config/instance.js +24 -0
- package/dist/src/ser-des/config/instance.js.map +1 -1
- package/dist/src/ser-des/config/process.d.ts.map +1 -1
- package/dist/src/ser-des/config/process.js +8 -0
- package/dist/src/ser-des/config/process.js.map +1 -1
- package/dist/src/ser-des/config/processSequence.d.ts +19 -0
- package/dist/src/ser-des/config/processSequence.d.ts.map +1 -0
- package/dist/src/ser-des/config/processSequence.js +77 -0
- package/dist/src/ser-des/config/processSequence.js.map +1 -0
- package/dist/src/ser-des/config/quantity.d.ts +4 -1
- package/dist/src/ser-des/config/quantity.d.ts.map +1 -1
- package/dist/src/ser-des/config/quantity.js +63 -1
- package/dist/src/ser-des/config/quantity.js.map +1 -1
- package/dist/src/ser-des/config/subject.d.ts.map +1 -1
- package/dist/src/ser-des/config/subject.js +14 -0
- package/dist/src/ser-des/config/subject.js.map +1 -1
- package/dist/src/ser-des/index.d.ts +4 -1
- package/dist/src/ser-des/index.d.ts.map +1 -1
- package/dist/src/ser-des/index.js +3 -1
- package/dist/src/ser-des/index.js.map +1 -1
- package/dist/src/ser-des/package.d.ts.map +1 -1
- package/dist/src/ser-des/package.js +9 -7
- package/dist/src/ser-des/package.js.map +1 -1
- package/dist/src/ser-des/parse.d.ts +24 -0
- package/dist/src/ser-des/parse.d.ts.map +1 -1
- package/dist/src/ser-des/parse.js +69 -33
- package/dist/src/ser-des/parse.js.map +1 -1
- package/dist/src/ser-des/shape-front-end.d.ts +8 -0
- package/dist/src/ser-des/shape-front-end.d.ts.map +1 -0
- package/dist/src/ser-des/shape-front-end.js +215 -0
- package/dist/src/ser-des/shape-front-end.js.map +1 -0
- package/dist/src/ser-des/types.d.ts +4 -10
- package/dist/src/ser-des/types.d.ts.map +1 -1
- package/dist/src/types/Artifact.d.ts +7 -0
- package/dist/src/types/Artifact.d.ts.map +1 -1
- package/dist/src/types/Attestation.d.ts +60 -0
- package/dist/src/types/Attestation.d.ts.map +1 -0
- package/dist/src/types/Attestation.js +20 -0
- package/dist/src/types/Attestation.js.map +1 -0
- package/dist/src/types/ConformanceTest.d.ts +86 -0
- package/dist/src/types/ConformanceTest.d.ts.map +1 -1
- package/dist/src/types/DocumentModule.d.ts +1 -1
- package/dist/src/types/DocumentModule.d.ts.map +1 -1
- package/dist/src/types/Instance.d.ts +6 -2
- package/dist/src/types/Instance.d.ts.map +1 -1
- package/dist/src/types/ProcessModel.d.ts +0 -11
- package/dist/src/types/ProcessModel.d.ts.map +1 -1
- package/dist/src/types/ProcessModel.js +2 -28
- package/dist/src/types/ProcessModel.js.map +1 -1
- package/dist/src/types/Quantity.d.ts +22 -0
- package/dist/src/types/Quantity.d.ts.map +1 -1
- package/dist/src/types/Standard.d.ts +2 -13
- package/dist/src/types/Standard.d.ts.map +1 -1
- package/dist/src/types/Subject.d.ts +14 -0
- package/dist/src/types/Subject.d.ts.map +1 -1
- package/dist/src/types/data.d.ts +13 -0
- package/dist/src/types/data.d.ts.map +1 -1
- package/dist/src/types/process.d.ts +8 -0
- package/dist/src/types/process.d.ts.map +1 -1
- package/dist-browser/index.mjs +1659 -1629
- package/dist-cli/primmel.mjs +2015 -875
- package/package.json +15 -3
- package/dist/src/ser-des/config/processModel.d.ts +0 -13
- package/dist/src/ser-des/config/processModel.d.ts.map +0 -1
- package/dist/src/ser-des/config/processModel.js +0 -163
- package/dist/src/ser-des/config/processModel.js.map +0 -1
- package/dist/src/ser-des/config/storyline.d.ts +0 -7
- package/dist/src/ser-des/config/storyline.d.ts.map +0 -1
- package/dist/src/ser-des/config/storyline.js +0 -264
- package/dist/src/ser-des/config/storyline.js.map +0 -1
- package/dist/src/ser-des/config/workflowConfig.d.ts +0 -5
- package/dist/src/ser-des/config/workflowConfig.d.ts.map +0 -1
- package/dist/src/ser-des/config/workflowConfig.js +0 -129
- package/dist/src/ser-des/config/workflowConfig.js.map +0 -1
- package/dist/src/ser-des/config/workflowStage.d.ts +0 -5
- package/dist/src/ser-des/config/workflowStage.d.ts.map +0 -1
- package/dist/src/ser-des/config/workflowStage.js +0 -96
- package/dist/src/ser-des/config/workflowStage.js.map +0 -1
- package/dist/src/types/Storyline.d.ts +0 -62
- package/dist/src/types/Storyline.d.ts.map +0 -1
- package/dist/src/types/Storyline.js +0 -51
- package/dist/src/types/Storyline.js.map +0 -1
package/dist/src/check.js
CHANGED
|
@@ -360,17 +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
363
|
//
|
|
375
364
|
// Levels (TODO.roadmap/17): the DEFAULT level runs the normal-level
|
|
376
365
|
// rules at their catalog severities. --audit additionally runs the
|
|
@@ -397,6 +386,10 @@
|
|
|
397
386
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
398
387
|
exports.checkPackage = checkPackage;
|
|
399
388
|
exports.checkManifestResolution = checkManifestResolution;
|
|
389
|
+
exports.checkArtifactContracts = checkArtifactContracts;
|
|
390
|
+
exports.checkTypedPayloads = checkTypedPayloads;
|
|
391
|
+
exports.checkAttestations = checkAttestations;
|
|
392
|
+
exports.checkClosedDerivations = checkClosedDerivations;
|
|
400
393
|
exports.checkSpellingCodes = checkSpellingCodes;
|
|
401
394
|
exports.checkDeclaredPredicates = checkDeclaredPredicates;
|
|
402
395
|
exports.checkTermAliases = checkTermAliases;
|
|
@@ -421,6 +414,7 @@ const FormulasUsed_1 = require("./types/FormulasUsed");
|
|
|
421
414
|
const tokenize_1 = require("./ser-des/tokenize");
|
|
422
415
|
const operational_state_1 = require("./operational-state");
|
|
423
416
|
const check_rules_1 = require("./check-rules");
|
|
417
|
+
const instance_resolution_1 = require("./instance-resolution");
|
|
424
418
|
const check_allowlist_1 = require("./check-allowlist");
|
|
425
419
|
const text_coverage_1 = require("./text-coverage");
|
|
426
420
|
const spelling_1 = require("./spelling");
|
|
@@ -485,7 +479,12 @@ function checkPackage(dir, options = {}) {
|
|
|
485
479
|
});
|
|
486
480
|
standard = loaded.standard;
|
|
487
481
|
for (const i of loaded.issues) {
|
|
488
|
-
|
|
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') {
|
|
489
488
|
warn('C30', i.message);
|
|
490
489
|
}
|
|
491
490
|
else if (i.code === 'extends-deprecated') {
|
|
@@ -540,11 +539,20 @@ function checkPackage(dir, options = {}) {
|
|
|
540
539
|
issues.push(...checkReferences(standard));
|
|
541
540
|
// ── C116/C117: the verdict chain (MN 114 v3.2, clause 11.3) ────────
|
|
542
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));
|
|
543
550
|
const reqIds = new Set((standard.requirements ?? []).map((r) => r.id));
|
|
544
551
|
const testIds = new Set((standard.conformanceTests ?? []).map((t) => t.id));
|
|
545
552
|
const attrIds = new Set((standard.attributeDefinitions ?? []).map((a) => a.id));
|
|
553
|
+
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
546
554
|
const behaviorIds = new Set((standard.behaviors ?? []).map((b) => b.id));
|
|
547
|
-
const subjectIds = new Set((standard.subjects ?? []).map(
|
|
555
|
+
const subjectIds = new Set((standard.subjects ?? []).map(s => s.id));
|
|
548
556
|
// Observables live in the symbols registry, not the attribute layer —
|
|
549
557
|
// binds_to / limit.uses may reference them (e.g. sample.test_context
|
|
550
558
|
// quantities that are measured test outputs).
|
|
@@ -1157,7 +1165,9 @@ function checkPackage(dir, options = {}) {
|
|
|
1157
1165
|
// C45 — artifact-def-contract: the content contract is well-formed.
|
|
1158
1166
|
for (const d of standard.artifactDefinitions ?? []) {
|
|
1159
1167
|
const c = d.contentContract;
|
|
1160
|
-
|
|
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) {
|
|
1161
1171
|
err('C45', `artifact definition ${d.id}: content contract declares no fields — a definition must contract SOME content (artifact-def-contract)`);
|
|
1162
1172
|
}
|
|
1163
1173
|
const seenFields = new Set();
|
|
@@ -1692,15 +1702,6 @@ function checkPackage(dir, options = {}) {
|
|
|
1692
1702
|
}
|
|
1693
1703
|
}
|
|
1694
1704
|
}
|
|
1695
|
-
for (const m of standard.processModels ?? []) {
|
|
1696
|
-
const where = `process_model ${m.id}`;
|
|
1697
|
-
for (const s of m.sequence ?? []) {
|
|
1698
|
-
resolveProcessRef(where, 'sequence', s);
|
|
1699
|
-
}
|
|
1700
|
-
for (const r of m.registers ?? []) {
|
|
1701
|
-
resolveOrganAp(`${where} register ${r.id}`, 'maintainer', r.maintainer);
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
1705
|
}
|
|
1705
1706
|
// ── C84: constraint-shape (TODO.roadmap/51 — BUG.R60-SSOT gap 7) ─────
|
|
1706
1707
|
// The subject-intrinsic constraint's DECLARATION shape — the kernel
|
|
@@ -2034,7 +2035,6 @@ function checkPackage(dir, options = {}) {
|
|
|
2034
2035
|
// escalation ACTIONS are runtime — the smart app's monitor service owns
|
|
2035
2036
|
// them; these rules guarantee the declarations the runtime needs.
|
|
2036
2037
|
{
|
|
2037
|
-
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
2038
2038
|
const KNOWN_OUTCOMES = new Set(Monitor_1.MONITOR_OUTCOMES);
|
|
2039
2039
|
const KNOWN_ACTIONS = new Set(Monitor_1.MONITOR_ESCALATION_ACTIONS);
|
|
2040
2040
|
const KNOWN_STREAMS = new Set(Monitor_1.MONITOR_STREAMS);
|
|
@@ -2338,6 +2338,7 @@ function checkPackage(dir, options = {}) {
|
|
|
2338
2338
|
};
|
|
2339
2339
|
const instrumentIds = new Set((standard.instruments ?? []).map(i => i.id));
|
|
2340
2340
|
const instanceIds = new Set((standard.instances ?? []).map(i => i.id));
|
|
2341
|
+
const dataclassIds = new Set((standard.dataclasses ?? []).map(c => c.id));
|
|
2341
2342
|
const attrScopes = new Map((standard.attributeDefinitions ?? []).map(a => [a.id, a.scope]));
|
|
2342
2343
|
// Classification-dimension scopes, by every spelling a classification
|
|
2343
2344
|
// map may use: the instrument dimension id, the is_dimension attribute
|
|
@@ -2431,9 +2432,53 @@ function checkPackage(dir, options = {}) {
|
|
|
2431
2432
|
}
|
|
2432
2433
|
}
|
|
2433
2434
|
// C20 — instance-of-resolves: the `of` reference names a declared
|
|
2434
|
-
// subject (v3)
|
|
2435
|
-
|
|
2436
|
-
|
|
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)`);
|
|
2437
2482
|
}
|
|
2438
2483
|
}
|
|
2439
2484
|
// C19 — chain-acyclic (integrity part 2): the resolved link graph is
|
|
@@ -3171,6 +3216,97 @@ function checkPackage(dir, options = {}) {
|
|
|
3171
3216
|
}
|
|
3172
3217
|
}
|
|
3173
3218
|
}
|
|
3219
|
+
// ── C147–C148: the executable test programs (smart TODO.twin-demo/03 —
|
|
3220
|
+
// the model-drive bindings) ──
|
|
3221
|
+
// The conformance test's `preparation { … }` / `stimulus { … }` facets
|
|
3222
|
+
// are the machine-executable half of the test declaration: the
|
|
3223
|
+
// preparation program (warm-up, preload discipline, zero check) and the
|
|
3224
|
+
// ordered measurement program (drive, argument expressions over the
|
|
3225
|
+
// subject's declared parameters, hold, the read's freshness bound, the
|
|
3226
|
+
// per-point acceptance reference). C147 test-program-shape: every
|
|
3227
|
+
// program entry's order is a positive integer unique in the program; a
|
|
3228
|
+
// preparation step carries a prose action AND/OR a drive reference; a
|
|
3229
|
+
// step's verify block carries read AND tolerance; a stimulus point
|
|
3230
|
+
// carries a drive and an acceptance reference. C148
|
|
3231
|
+
// test-program-duration: a hold / fresh_within value parses as a
|
|
3232
|
+
// duration window (the serve contract's vocabulary — shorthand 30min or
|
|
3233
|
+
// ISO 8601 fixed-length PT30M), and a stimulus point declares
|
|
3234
|
+
// fresh_within (§14.12's no-stale-semantics doctrine: a measurement
|
|
3235
|
+
// point's read carries its freshness bound, like C63's live bindings).
|
|
3236
|
+
// Drive/acceptance/tolerance REFERENCE RESOLUTION (the world-vocabulary
|
|
3237
|
+
// operation names, the requirement nodes) is the consumer-side
|
|
3238
|
+
// conformance leg's job — the kernel checks syntax/shape only (the
|
|
3239
|
+
// C92/C93 vs R39 split). Packages without test programs are untouched —
|
|
3240
|
+
// the loops are empty.
|
|
3241
|
+
{
|
|
3242
|
+
for (const ct of standard.conformanceTests ?? []) {
|
|
3243
|
+
const prep = ct.preparation;
|
|
3244
|
+
if (prep) {
|
|
3245
|
+
const orders = new Set();
|
|
3246
|
+
for (const step of prep.entries) {
|
|
3247
|
+
const at = step.order !== null
|
|
3248
|
+
? `preparation step ${step.order}`
|
|
3249
|
+
: 'a preparation step';
|
|
3250
|
+
if (step.order === null ||
|
|
3251
|
+
!Number.isInteger(step.order) ||
|
|
3252
|
+
step.order < 1) {
|
|
3253
|
+
err('C147', `conformance_test ${ct.id}: ${at} declares no positive-integer order (got ${step.order === null ? 'none' : step.order}) — the step order is a positive integer (test-program-shape)`);
|
|
3254
|
+
}
|
|
3255
|
+
else if (orders.has(step.order)) {
|
|
3256
|
+
err('C147', `conformance_test ${ct.id}: preparation step order ${step.order} is declared twice — the order is unique within the program (test-program-shape)`);
|
|
3257
|
+
}
|
|
3258
|
+
else {
|
|
3259
|
+
orders.add(step.order);
|
|
3260
|
+
}
|
|
3261
|
+
if (step.action === '' && step.drive === '') {
|
|
3262
|
+
err('C147', `conformance_test ${ct.id}: ${at} declares neither action nor drive — a preparation step carries a prose action AND/OR a drive reference (test-program-shape)`);
|
|
3263
|
+
}
|
|
3264
|
+
if (step.verify &&
|
|
3265
|
+
(step.verify.read === '' || step.verify.tolerance === '')) {
|
|
3266
|
+
err('C147', `conformance_test ${ct.id}: ${at} declares a verify block without ${step.verify.read === '' ? 'read' : 'tolerance'} — the verification names what to read AND the tolerance reference (test-program-shape)`);
|
|
3267
|
+
}
|
|
3268
|
+
if (step.hold !== '' && (0, time_1.parseFreshnessWindow)(step.hold) === null) {
|
|
3269
|
+
err('C148', `conformance_test ${ct.id}: ${at} hold "${step.hold}" is not a parseable duration (shorthand 500ms/5s/1min/1h/1d or ISO 8601 with fixed-length components, e.g. PT30M) (test-program-duration)`);
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
const stim = ct.stimulus;
|
|
3274
|
+
if (stim) {
|
|
3275
|
+
const orders = new Set();
|
|
3276
|
+
for (const point of stim.entries) {
|
|
3277
|
+
const at = point.order !== null
|
|
3278
|
+
? `stimulus point ${point.order}`
|
|
3279
|
+
: 'a stimulus point';
|
|
3280
|
+
if (point.order === null ||
|
|
3281
|
+
!Number.isInteger(point.order) ||
|
|
3282
|
+
point.order < 1) {
|
|
3283
|
+
err('C147', `conformance_test ${ct.id}: ${at} declares no positive-integer order (got ${point.order === null ? 'none' : point.order}) — the point order is a positive integer (test-program-shape)`);
|
|
3284
|
+
}
|
|
3285
|
+
else if (orders.has(point.order)) {
|
|
3286
|
+
err('C147', `conformance_test ${ct.id}: stimulus point order ${point.order} is declared twice — the order is unique within the program (test-program-shape)`);
|
|
3287
|
+
}
|
|
3288
|
+
else {
|
|
3289
|
+
orders.add(point.order);
|
|
3290
|
+
}
|
|
3291
|
+
if (point.drive === '') {
|
|
3292
|
+
err('C147', `conformance_test ${ct.id}: ${at} declares no drive — a stimulus point names the drive operation (test-program-shape)`);
|
|
3293
|
+
}
|
|
3294
|
+
if (point.acceptance === '') {
|
|
3295
|
+
err('C147', `conformance_test ${ct.id}: ${at} declares no acceptance — a stimulus point names the MPE/requirement node it is judged against (test-program-shape)`);
|
|
3296
|
+
}
|
|
3297
|
+
if (point.hold !== '' && (0, time_1.parseFreshnessWindow)(point.hold) === null) {
|
|
3298
|
+
err('C148', `conformance_test ${ct.id}: ${at} hold "${point.hold}" is not a parseable duration (shorthand 500ms/5s/1min/1h/1d or ISO 8601 with fixed-length components, e.g. PT30M) (test-program-duration)`);
|
|
3299
|
+
}
|
|
3300
|
+
if (point.freshWithin === '') {
|
|
3301
|
+
err('C148', `conformance_test ${ct.id}: ${at} declares no fresh_within — a measurement point's read carries its freshness bound (§14.12: no stale semantics) (test-program-duration)`);
|
|
3302
|
+
}
|
|
3303
|
+
else if ((0, time_1.parseFreshnessWindow)(point.freshWithin) === null) {
|
|
3304
|
+
err('C148', `conformance_test ${ct.id}: ${at} fresh_within "${point.freshWithin}" is not a parseable freshness window (shorthand 500ms/5s/1min/1h/1d or ISO 8601 with fixed-length components, e.g. PT5S) (test-program-duration)`);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3174
3310
|
// ── C94: the per-test evaluation-formula traces (smart gap-close E11,
|
|
3175
3311
|
// analysis/architecture-gaps-2026-07.md; the smart contract
|
|
3176
3312
|
// data/schemas/formulas-used.yaml) ──
|
|
@@ -3598,68 +3734,6 @@ function checkPackage(dir, options = {}) {
|
|
|
3598
3734
|
}
|
|
3599
3735
|
}
|
|
3600
3736
|
}
|
|
3601
|
-
// ── C129: storyline-shape (smart TODO.roadmap/40 batch 4; the ──────
|
|
3602
|
-
// packages-as-SSOT epic) ────────────────────────────────────────────
|
|
3603
|
-
// The demo-seed shapes: the id_prefix pattern (the register-free shape
|
|
3604
|
-
// leg); the party laboratory/authority resolve against the package's
|
|
3605
|
-
// participant seeds WHEN a demo_world with participants is in scope
|
|
3606
|
-
// (per-register gating, the C58 doctrine); the record stores resolve
|
|
3607
|
-
// against the declared entity-class stores WHEN that register is in
|
|
3608
|
-
// scope; and the in-construct cross-references resolve — a record
|
|
3609
|
-
// field keyed by a sibling record's store names a sibling record of
|
|
3610
|
-
// that store (in-construct, ungated). The field-level discipline (the
|
|
3611
|
-
// schema's per-slot required fields) stays app-side.
|
|
3612
|
-
{
|
|
3613
|
-
const participantSeeds = new Set();
|
|
3614
|
-
for (const w of standard.demoWorlds ?? []) {
|
|
3615
|
-
for (const section of w.participants ?? []) {
|
|
3616
|
-
for (const e of section.entries ?? []) {
|
|
3617
|
-
participantSeeds.add(e.id);
|
|
3618
|
-
}
|
|
3619
|
-
}
|
|
3620
|
-
}
|
|
3621
|
-
const entityStores = new Set((standard.dataclasses ?? [])
|
|
3622
|
-
.map(c => c.store)
|
|
3623
|
-
.filter((s) => !!s));
|
|
3624
|
-
for (const s of standard.storylines ?? []) {
|
|
3625
|
-
const where = `storyline ${s.id}`;
|
|
3626
|
-
if (s.idPrefix && !/^[a-z0-9-]+$/.test(s.idPrefix)) {
|
|
3627
|
-
err('C129', `${where}: id_prefix "${s.idPrefix}" does not match ^[a-z0-9-]+$ — the id-minting prefix is a lowercase slug (storyline-shape)`);
|
|
3628
|
-
}
|
|
3629
|
-
if (participantSeeds.size > 0) {
|
|
3630
|
-
for (const [slot, ref] of [
|
|
3631
|
-
['laboratory', s.party.laboratory],
|
|
3632
|
-
['authority', s.party.authority],
|
|
3633
|
-
]) {
|
|
3634
|
-
if (ref && !participantSeeds.has(ref)) {
|
|
3635
|
-
err('C129', `${where}: party ${slot} "${ref}" is not a declared participant seed (storyline-shape)`);
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3638
|
-
}
|
|
3639
|
-
const siblingStores = new Map();
|
|
3640
|
-
for (const r of s.records ?? []) {
|
|
3641
|
-
if (entityStores.size > 0 && !entityStores.has(r.store)) {
|
|
3642
|
-
err('C129', `${where}: record store "${r.store}" is not a declared entity-class store (storyline-shape)`);
|
|
3643
|
-
}
|
|
3644
|
-
let ids = siblingStores.get(r.store);
|
|
3645
|
-
if (!ids) {
|
|
3646
|
-
ids = new Set();
|
|
3647
|
-
siblingStores.set(r.store, ids);
|
|
3648
|
-
}
|
|
3649
|
-
ids.add(r.id);
|
|
3650
|
-
}
|
|
3651
|
-
for (const r of s.records ?? []) {
|
|
3652
|
-
for (const [key, value] of Object.entries(r.fields)) {
|
|
3653
|
-
const targets = siblingStores.get(key);
|
|
3654
|
-
if (targets !== undefined &&
|
|
3655
|
-
!Array.isArray(value) &&
|
|
3656
|
-
!targets.has(String(value))) {
|
|
3657
|
-
err('C129', `${where}: record ${r.store} ${r.id}: ${key} "${String(value)}" is not a sibling ${key} record of this storyline (storyline-shape)`);
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
}
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
3737
|
// ── C130: identity-and-aspect-references (smart TODO.roadmap/40 ─────
|
|
3664
3738
|
// batch 3; the packages-as-SSOT epic) ────────────────────────────────
|
|
3665
3739
|
// The documentary identity slots + the qualitative aspect register
|
|
@@ -4030,7 +4104,6 @@ function checkPackage(dir, options = {}) {
|
|
|
4030
4104
|
// (the R26 field-resolution precedent). The phase vocabulary is
|
|
4031
4105
|
// parse-enforced upstream (no check leg).
|
|
4032
4106
|
{
|
|
4033
|
-
const roleIds = new Set((standard.roles ?? []).map(r => r.id));
|
|
4034
4107
|
// Class ids may carry a namespace suffix (`Application#data`) while
|
|
4035
4108
|
// the workflow's inputs/outputs spell the bare entity name — index
|
|
4036
4109
|
// both spellings (the semantic-status leg's precedent).
|
|
@@ -4041,28 +4114,6 @@ function checkPackage(dir, options = {}) {
|
|
|
4041
4114
|
dataclassIds.add(c.id.slice(0, c.id.indexOf('#')));
|
|
4042
4115
|
}
|
|
4043
4116
|
}
|
|
4044
|
-
const CLEAN_TOKEN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4045
|
-
for (const w of standard.workflowConfigs ?? []) {
|
|
4046
|
-
for (const s of w.steps ?? []) {
|
|
4047
|
-
const where = `workflow_config ${w.id}: step ${s.id}`;
|
|
4048
|
-
if (s.actor && roleIds.size > 0 && !roleIds.has(s.actor)) {
|
|
4049
|
-
err('C137', `${where}: actor "${s.actor}" is not a declared role (workflow-config-references)`);
|
|
4050
|
-
}
|
|
4051
|
-
for (const [facet, list] of [
|
|
4052
|
-
['inputs', s.inputs],
|
|
4053
|
-
['outputs', s.outputs],
|
|
4054
|
-
]) {
|
|
4055
|
-
for (const entry of list) {
|
|
4056
|
-
if (!CLEAN_TOKEN.test(entry)) {
|
|
4057
|
-
continue; // a composite documentary string — never resolved
|
|
4058
|
-
}
|
|
4059
|
-
if (dataclassIds.size > 0 && !dataclassIds.has(entry)) {
|
|
4060
|
-
err('C137', `${where}: ${facet} entry "${entry}" is not a declared data class (workflow-config-references)`);
|
|
4061
|
-
}
|
|
4062
|
-
}
|
|
4063
|
-
}
|
|
4064
|
-
}
|
|
4065
|
-
}
|
|
4066
4117
|
}
|
|
4067
4118
|
// ── C138: verification-pathway-references (smart TODO.roadmap/40 ───
|
|
4068
4119
|
// batch 3; the packages-as-SSOT epic) ────────────────────────────────
|
|
@@ -4309,26 +4360,6 @@ function checkPackage(dir, options = {}) {
|
|
|
4309
4360
|
}
|
|
4310
4361
|
// The workflow_stage members-resolve legs (step 5d) — the events
|
|
4311
4362
|
// stay documentary, never resolved.
|
|
4312
|
-
const stageApprovalIds = new Set((standard.approvals ?? []).map(a => a.id));
|
|
4313
|
-
const stageGatewayIds = new Set((standard.gateways ?? []).map(g => g.id));
|
|
4314
|
-
for (const s of standard.workflowStages ?? []) {
|
|
4315
|
-
const where = `workflow_stage ${s.id}`;
|
|
4316
|
-
for (const el of s.elements ?? []) {
|
|
4317
|
-
if (gwProcessIds.size > 0 && !gwProcessIds.has(el)) {
|
|
4318
|
-
err('C142', `${where}: element "${el}" is not a declared process (gateway-edges-resolve)`);
|
|
4319
|
-
}
|
|
4320
|
-
}
|
|
4321
|
-
for (const a of s.approvals ?? []) {
|
|
4322
|
-
if (stageApprovalIds.size > 0 && !stageApprovalIds.has(a)) {
|
|
4323
|
-
err('C142', `${where}: approval "${a}" is not a declared approval (gateway-edges-resolve)`);
|
|
4324
|
-
}
|
|
4325
|
-
}
|
|
4326
|
-
for (const g of s.gateways ?? []) {
|
|
4327
|
-
if (stageGatewayIds.size > 0 && !stageGatewayIds.has(g)) {
|
|
4328
|
-
err('C142', `${where}: gateway "${g}" is not a declared gateway (gateway-edges-resolve)`);
|
|
4329
|
-
}
|
|
4330
|
-
}
|
|
4331
|
-
}
|
|
4332
4363
|
}
|
|
4333
4364
|
// ── C143: approval-references-resolve (smart TODO.roadmap/40 batch ──
|
|
4334
4365
|
// 5; the packages-as-SSOT epic) ─────────────────────────────────────
|
|
@@ -5582,6 +5613,362 @@ function textAddressKey(item, segment) {
|
|
|
5582
5613
|
* (titular:source:target:identifying); a `zz-` user-assigned code
|
|
5583
5614
|
* validates with a warning (user-assigned codes are not portable).
|
|
5584
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
|
+
}
|
|
5585
5972
|
function checkSpellingCodes(standard) {
|
|
5586
5973
|
const issues = [];
|
|
5587
5974
|
const err = (check, message) => issues.push({ check, severity: 'error', message });
|