expressir 2.4.37-aarch64-linux → 2.4.39-aarch64-linux

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ae81ac327a057efb9a9355bcf56862dd56078a792030bb6d817713dbaca9276
4
- data.tar.gz: 28812135bbd3a203b123144a6aea943f9cf3d04179540db475831db6c2ac628c
3
+ metadata.gz: 6acf267e7995692726ce146d2403b1d3ee5bedb0c8b900ae7073a6a64d46dafc
4
+ data.tar.gz: abda66bcc24e2be370104d4f233e42b62662dfa41df1c38089a0dd502ede67f6
5
5
  SHA512:
6
- metadata.gz: 9ea3e573d873b997e451a65a8d8cd5808bb12ccbd1e9be4f60a48160a944b7a7fabf041fd1bf3b433aeae37f2cfa5a83bb5ef8fb916751e7b934d5521e022de2
7
- data.tar.gz: b6701e43ea5a23175d4bb6c2ffc34fa398c7993ac146d1bec72b25f79c51fe20a008d309527e6f2fa32acbed7ef2f629a773c9b01550772653b81fac3bfbda87
6
+ metadata.gz: 3f44850b2b02c3f314a782e7d4a7553659285e9cde15bb24b8fdd5a9dfc88e7e73d7733513e50ebc13bf41520220b87b6dc87403c7ff3c98c912686bf291728e
7
+ data.tar.gz: 6d59bd498346c0daad89d51707d47b4293286905265974f8d31c9735e53ad046794c75fde4b97705aa759857ae25e0e90d370cc45e26b706299c662fec82825e
@@ -0,0 +1,73 @@
1
+ # TODO.instance — the instance-data program (vision)
2
+
3
+ Expressir made the schema side of ISO 10303 machine-checkable and
4
+ citable (TODO.sota). The other half of the standard is instance data:
5
+ STEP populations exchanged as Part 21 files and validated against
6
+ schema editions. This program makes Expressir the open engine for
7
+ instance-data conformance — type every instance, check every value,
8
+ evaluate every rule, at population scale, with attestable receipts
9
+ binding a dataset to a schema-library release. Same yardstick as
10
+ TODO.sota: every validator claim backed by a clause, a worked example,
11
+ and a corpus run.
12
+
13
+ ## Where we already are
14
+
15
+ - `Expressir::P21`: reader (header, simple/complex instances, nested
16
+ aggregates, typed parameters, short names) and the structural
17
+ validator (clause-12 slot mapping, arity, reference integrity,
18
+ enumeration membership, derived-slot discipline).
19
+ - `Expressir::Eval` (TODO.sota/02): full ISO 10303-11 ch.12/ch.13
20
+ evaluator; `Rules.check` evaluates entity WHERE rules over Part 21
21
+ populations with SELF and mapped-attribute bindings.
22
+ - JSON bridge: JSON Schema export of schemas (TODO.sota/05) and the
23
+ population→JSON serialization built for the Annex H consumer test.
24
+ - ISO 10303-21 conformance fixtures under `spec/fixtures/p21/conformance/`
25
+ (annexes E/I/J currently loud-fail by design — the ladder gap).
26
+ - Schema side for scale: compiled-set artifacts, LazyRepository,
27
+ subtype/interface graphs computed over the 1,307-schema SMRL set;
28
+ ItemGraph is the noted remaining class.
29
+
30
+ ## Execution order and status
31
+
32
+ | n | task | tier | status |
33
+ |---|------|------|--------|
34
+ | 01 | instance typing + value/type conformance | semantics | done (P21::Typing entity closure incl. complex/ONEOF 9.2.5/9.2.5.2; slot value/type conformance :check_p21_* per ch.8/9.2.1/9.2.2.2; SELECT membership 8.4.2; Annex H clean; mutation sweeps) |
35
+ | 02 | population-scale rule engine (UNIQUE, global rules) | execution | done (Eval::Population: typed entity bucketing, UNIQUE 9.2.2.1 across subtypes, global RULE 9.4 with FOR aggregates; 10^5-instance benchmark 29.4s deterministic) |
36
+ | 03 | P21 ladder completion + population management + receipts | platform | done (named DATA sections + ANCHOR/REFERENCE/constants + signature lines; DatasetManifest + `population validate` + `population diff`; byte-stable JSON receipts; P28 deferred — needs the -28 derived-XML-schema program) |
37
+ | 04 | ARM↔MIM instance mapping verification | semantics | done (Mapping::InstanceVerification: image-of search over P21::Typing + RefPath instance walks incl. constraints/alt_map/rules; cross-population reference correspondence; mutation-swept) |
38
+
39
+ Dependency shape: 02 consumes 01's typing (rule applicability and
40
+ SELECT scoping are typing queries). 03 is independent of 02 (files,
41
+ manifests, diffs, receipts compose whatever validator exists). 04
42
+ consumes 01 (MIM instance typing) and the RefPath mapping model
43
+ (TODO.refpath-v2, landed).
44
+
45
+ ## Sources
46
+
47
+ - ISO 10303-11 text: `~/src/mn/iso-10303-11/sources/sections/`
48
+ (ch.8 data types incl. domain WHERE, 9.2 attributes/UNIQUE,
49
+ 9.4 global rules, ch.12/13 already evaluated).
50
+ - ISO 10303-21: `~/src/mn/iso-10303-detached-docs/sources/iso-10303-21/`
51
+ (annex E named data sections, annex I ANCHOR/REFERENCE, annex J
52
+ constants; annex H worked example already a fixture).
53
+ - stepmod schema slices: `~/src/mn/iso-10303-stepmod/data/` (fast
54
+ <250 KB slice; full 1,307-schema set) and
55
+ `~/src/mn/iso-10303/schemas/modules/` (mapping.yaml corpus).
56
+ - ELF 5006 mapping specification:
57
+ `~/src/expresslang/docs/sources/express-mapping/`.
58
+ - eengine oracle: `~/bin/eengine` — `-MODE RESOURCE -STEPMOD`; note
59
+ `--validate` aborts on bare p11, so differentials use mutation
60
+ sweeps and, where schemas load, eeng compare.
61
+ - Instance corpora: AP 242 / Thurman validation-proposal datasets
62
+ (`pr463_validation_proposal.pdf`), plus round-trip corpora from the
63
+ JSON bridge and the Annex H fixture.
64
+
65
+ ## Environment notes
66
+
67
+ - Run suites in chunks (full single-process runs OOM on this box);
68
+ gate corpus tiers behind env flags as in TODO.sota/03.
69
+ - Standing rules carry over: semantics in Ruby (expressir-rs native
70
+ only on measured, benchmarked need); lutaml-model mappings only;
71
+ Array()-guard nil collections; real model instances, never doubles;
72
+ spec paths match describe subjects; rubocop -A every touched file;
73
+ one PR per task, branch off origin/main, rebase-merge when green.
@@ -0,0 +1,81 @@
1
+ # 01 — Instance typing + value/type conformance
2
+
3
+ Everything downstream (rule scoping, SELECT checking, ARM-view
4
+ queries) needs the answer to one question about every instance: which
5
+ entities does it conform to?
6
+
7
+ ## Parts (each ships)
8
+
9
+ 1. Typing engine: for every instance in a population, the set of
10
+ entities it conforms to, through the subtype graph. Simple records
11
+ (one keyword + supertype closure), complex records (multi-entity
12
+ AND instantiation validated against the supertype constraints:
13
+ ANDOR admissible, ONEOF exclusive, total-over respected),
14
+ short-name resolution via the clause-12.3 map. Cached per
15
+ population; public API `Expressir::P21::Typing#of(instance)`.
16
+ 2. Slot value/type conformance: the clause-12 slot mapping (already
17
+ in `P21::Validator`) extended with value-domain checking —
18
+ primitive domains (INTEGER, REAL precision, NUMBER, STRING, BINARY
19
+ width, BOOLEAN, LOGICAL), defined types unfolded to their base
20
+ with the defined type's WHERE rules evaluated (ch.8 domain rules;
21
+ the evaluator supplies evaluation), aggregates (LIST/ARRAY/SET/BAG
22
+ bounds, SET uniqueness, element types, nested aggregates, ARRAY
23
+ OPTIONAL elements), enumeration membership, and instance references
24
+ checked against the target instance's typing from part 1.
25
+ 3. SELECT membership: an attribute typed by a SELECT requires the
26
+ referenced instance's typing to hit one of the SELECT's arms,
27
+ through BASED_ON inheritance chains; findings name the arm tried.
28
+
29
+ ## Acceptance
30
+
31
+ - One firing and one clean spec example per rule, citing the clause.
32
+ - Annex H population: every instance's typing computed; every slot
33
+ value-conformant (zero findings).
34
+ - Mutation sweep: corrupting one value (wrong enum item, bound
35
+ overflow, wrong reference type, bad complex combination) produces
36
+ exactly the intended finding and no other.
37
+ - A complex-record population exercises ANDOR/ONEOF admissibility.
38
+
39
+ ## Status: done
40
+
41
+ - Part 1 (typing engine): `Expressir::P21::Typing` — `of(record)`
42
+ returns the entities an instance conforms to (keyword + supertype
43
+ closure; complex records union their records' closures),
44
+ `issues(record)` reports an unknown keyword, a repeated leaf in one
45
+ complex instance (9.2.5) and ONEOF exclusivity violations
46
+ (9.2.5.2 — the ONEOF groups of the supertype expressions and the
47
+ ONEOF subtype constraints (9.7) reachable from each leaf).
48
+ Short names resolve per ISO 10303-21 12.3.
49
+ - Part 2 (slot value/type conformance): `P21::Validator` now walks
50
+ each clause-12 slot's declared type — primitives (8.1.1-8.1.7,
51
+ with .T./.F./.U. as the Part 21 spelling of BOOLEAN/LOGICAL),
52
+ defined types unfolded to their base with their WHERE domain rules
53
+ evaluated through `Expressir::Eval` (9.2.2.2), aggregates (8.2.1
54
+ ARRAY bounds + non-OPTIONAL unset elements, 8.2.2 LIST bounds,
55
+ 8.2.3 BAG, 8.2.4 SET uniqueness, nested element typing), instance
56
+ references checked against the target instance's typing (9.2.1),
57
+ and typed parameters unwrapped (`TYPED(v)` single-element
58
+ unwrapping). Findings use `:check_p21_value_domain`,
59
+ `:check_p21_aggregate`, `:check_p21_reference_type`,
60
+ `:check_p21_select_arm`, `:check_p21_complex_instantiation`, each
61
+ message citing its clause. `$` in a top-level slot stays tolerated
62
+ (absent), matching the pre-existing contract; `:unset` inside a
63
+ non-OPTIONAL ARRAY is a finding.
64
+ - Part 3 (SELECT membership): an attribute typed by a SELECT accepts
65
+ a reference whose instance's typing hits an arm, a typed parameter
66
+ naming an arm, or a literal accepted by a type arm — arms follow
67
+ item entities, item defined types, nested selects and the BASED_ON
68
+ chain (8.4.2).
69
+ - Parser note: `ANDOR` takes binary form (`a ANDOR b`), not a comma
70
+ list; `FIXED` is reserved and cannot name an attribute.
71
+ - Also: `Expressir::Eval` is autoloaded from the gem root (the eval
72
+ specs previously required it by hand).
73
+
74
+ ## Validation
75
+
76
+ - `spec/expressir/p21/typing_spec.rb` (7) and
77
+ `spec/expressir/p21/validator_types_spec.rb` (23: clean/firing per
78
+ rule, the five-corruption mutation sweep where each corruption
79
+ yields exactly one intended finding, and the Annex H population
80
+ clean end to end through the full stack) — 61 P21 examples, 0
81
+ failures; parser + mapping regression chunk 56/0.
@@ -0,0 +1,82 @@
1
+ # 02 — Population-scale rule engine
2
+
3
+ TODO.sota/02 made rules runnable; this task makes them runnable over
4
+ whole populations, at corpus scale, with the cross-instance semantics
5
+ ISO 10303-11 requires.
6
+
7
+ ## Parts (each ships)
8
+
9
+ 1. Compiled rules: parse and resolve each WHERE / UNIQUE / global rule
10
+ once per schema edition; apply the compiled form to many instances.
11
+ The Ruby evaluator stays the semantics home — the native path is a
12
+ measured-need escalation only (standing rule), with a benchmark to
13
+ justify any move.
14
+ 2. UNIQUE rules: per entity, each UNIQUE label's expression evaluated
15
+ across the whole population with duplicate detection; labels
16
+ combining attributes and aggregates handled; self-referential
17
+ population patterns (instances referencing instances of the same
18
+ entity) are just population lookups.
19
+ 3. Global rules: `RULE ... FOR (e1, e2)` — the FOR set is all
20
+ instances of the named entities; the rule body evaluates once per
21
+ rule over that population (SIZEOF/QUERY/USEDIN over the whole
22
+ population, not per instance); violation only on proven false
23
+ (9.4); indeterminate is not a violation.
24
+ 4. Derived and inverse consistency: derived slots must be `*` per the
25
+ Part 21 mapping (validator has it); DERIVE expressions recomputed
26
+ and cross-checked where a value is present; INVERSE attributes
27
+ validated as population-consistent (every inverse view of the
28
+ explicit relation is materializable).
29
+ 5. Scale and determinism: an SRL-scale synthetic population (10^5
30
+ instances) rule-checked within a documented runtime budget,
31
+ published as a rake benchmark; identical population + schema
32
+ edition → byte-identical report.
33
+
34
+ ## Acceptance
35
+
36
+ - Spec examples per rule (firing + clean) citing 9.4 / 9.2.x.
37
+ - Annex H population: every WHERE rule provably passes at engine
38
+ level; the seeded-fault population fails exactly the intended rule
39
+ and no other.
40
+ - UNIQUE: a duplicated label pair is found regardless of file order;
41
+ global rule: an aggregate violation (e.g. a FOR-set size rule) is
42
+ caught once, with instance evidence.
43
+ - The benchmark artifact records the 10^5-instance run on this box.
44
+
45
+ ## Status: done
46
+
47
+ - Part 1 (compiled rules): `Expressir::Eval::Population` — the
48
+ engine resolves each rule once per schema edition: instances are
49
+ bucketed per entity via the task-01 typing (`instances_for`,
50
+ subtypes included), slot maps are cached per entity, and the Ruby
51
+ evaluator stays the semantics home (no native path, per the
52
+ standing rule).
53
+ - Part 2 (UNIQUE, 9.2.2.1): each UNIQUE label's attribute tuple is
54
+ evaluated per instance and compared across the whole population —
55
+ entity plus subtypes, file order irrelevant; an attribute outside
56
+ the entity is an error finding, not a silent pass.
57
+ - Part 3 (global rules, 9.4): `RULE ... FOR (...)` evaluates once
58
+ per rule with each FOR entity bound to the aggregate of all its
59
+ instances (`SIZEOF`/`QUERY` over the population); a rule naming no
60
+ resolvable entity is an error finding. Violation only on proven
61
+ false; indeterminate is not a violation.
62
+ - Part 4 (derived/inverse): the Part 21 derived-slot discipline
63
+ (`*`) is already enforced by the validator (p21_derived_slot);
64
+ Part 21 files carry no derived values to recompute, and INVERSE
65
+ attributes are population-derived (nothing to evaluate beyond the
66
+ schema-side checker) — recorded as scope, not gaps.
67
+ - Part 5 (scale + determinism): `rake instance:benchmark`
68
+ (POPULATION_SIZE overrides, default 100000) generates a synthetic
69
+ schema+population, runs WHERE + UNIQUE + global checks, writes
70
+ `instance-benchmark.json` (timings, findings, deterministic flag)
71
+ and runs the check twice to prove byte-identical results.
72
+
73
+ ## Validation
74
+
75
+ - `spec/expressir/eval/population_spec.rb` (9): WHERE applicability
76
+ through subtypes, UNIQUE duplicates across subtypes regardless of
77
+ order + unknown-attribute error, global rule firing/clean over the
78
+ FOR aggregate, determinism, and Annex H clean at engine level;
79
+ the eval chunk is 52 examples, 0 failures.
80
+ - 10^5-instance benchmark on this box: load + parse + check,
81
+ `check_first` 29.36 s, `check_second` deterministic, findings 0
82
+ (`/tmp/instance-bench-100k.json` from `rake instance:benchmark`).
@@ -0,0 +1,97 @@
1
+ # 03 — P21 ladder completion + population management + receipts
2
+
3
+ The file format, its management, and the citable validation artifact.
4
+
5
+ ## Parts (each ships)
6
+
7
+ 1. Ladder completion: annex E named data sections (multiple DATA
8
+ sections, per-section schema binding), annex I ANCHOR/REFERENCE
9
+ (anchors as cross-file identity; REFERENCE parameter binding),
10
+ annex J constants and extended literals — the currently
11
+ loud-failing fixtures become positives; genuinely malformed input
12
+ still fails loudly with the intended finding.
13
+ 2. Dataset manifests: a population manifest YAML binding dataset files
14
+ to a schema-library release (file list, schema edition, short-name
15
+ maps, section inventory); `expressir population validate MANIFEST`
16
+ runs the whole stack (01 + 02) over every file and writes the
17
+ receipt. Mirror of the schema-side ELF manifest.
18
+ 3. Population diff/patch: instance-level compare (added/removed/
19
+ changed instances keyed by identity and value digest — the
20
+ population sibling of SchemaCompare), patch application, and
21
+ subset extraction by entity closure.
22
+ 4. Round trips: P21→model→P21 with a byte-stable mode; P21↔JSON
23
+ promoted from the test bridge to public API (JSON Schema exporter
24
+ exists; the population serializer is published with it); P28
25
+ (ISO 10303-28 XML) read/write per the -28 mapping.
26
+ 5. Validation receipts: deterministic JSON/YAML report — dataset
27
+ digest, library release, validator version, per-rule verdicts with
28
+ instance ids, summary counts — reproducible byte-for-byte. The
29
+ citable artifact binding dataset to schema release (validator
30
+ version included).
31
+
32
+ ## Acceptance
33
+
34
+ - Annex E/I/J fixtures: positives parse and validate clean; malformed
35
+ negatives fail with exactly the intended finding.
36
+ - Manifest-driven validation of a sample dataset produces two
37
+ byte-identical receipts on consecutive runs.
38
+ - Population diff: a known edit yields exactly the expected deltas;
39
+ P21↔JSON↔P21 is value-identical; P28 round trip validated against
40
+ the -28 examples in the detachment.
41
+
42
+ ## Status: done
43
+
44
+ - Part 1 (ladder completion): the P21 grammar and builder now carry
45
+ the full ISO 10303-21:2016 ladder — named DATA sections with schema
46
+ contexts (annex E; clause 11.2), the signature `..` header lines
47
+ (clause 14), the ANCHOR section with promissory unset bindings
48
+ (clause 9), the REFERENCE section binding local ids to external
49
+ anchors (clause 10), and `#CONSTANT` references in parameters. The
50
+ validator accepts REFERENCE-bound ids as known references and warns
51
+ when a section's schema contexts miss the declared schema list
52
+ (`:p21_section_context`). Annex E/I/J fixtures are positives;
53
+ two extraction errata in the published detachment (annex E's
54
+ unterminated string, annex J's missing paren plus an Asciidoc
55
+ `....` delimiter — recorded in the detachment's own document.err)
56
+ are corrected in the fixtures and cited in the spec header.
57
+ - Part 2 (dataset manifests): `Expressir::P21::DatasetManifest`
58
+ (lutaml-model) binds files to a schema, a library release and
59
+ short-name maps; `expressir population validate MANIFEST` runs the
60
+ full stack (parse, typing, value conformance, rules) and writes the
61
+ receipt; errors exit 1.
62
+ - Part 3 (population diff): `Expressir::P21::Diff.diff` —
63
+ added/removed/changed by #id with keyword+parameter digests,
64
+ complex records included; `expressir population diff A B` prints
65
+ the deltas.
66
+ - Part 5 (receipts): `Expressir::P21::Receipt.build` emits the
67
+ deterministic JSON receipt — release, schema, validator version,
68
+ per-file SHA256 digests, per-note findings, summary. Same manifest
69
+ + same files → byte-identical output (verified by consecutive CLI
70
+ runs and by spec).
71
+ - Part 4 (round trips): `Expressir::P21::Writer.emit` re-emits a
72
+ population deterministically (HEADER in insertion order, DATA
73
+ instances in id order, anchors/references/named sections when
74
+ present) with parse(emit(pop)) value-identical to pop;
75
+ `Expressir::P21::Json` is the value-identical JSON interchange with
76
+ explicitly tagged Part 21 value kinds (refs, enums, binaries,
77
+ unset/derived, typed parameters).
78
+ - Part 4b (P28): deferred with reason. ISO 10303-28:2003+ encodes
79
+ instances through per-schema DERIVED XML schemas (early binding,
80
+ -28 clauses 8-9): a conforming reader/writer must first implement
81
+ the -28 XML-schema derivation for arbitrary EXPRESS schemas, which
82
+ is its own program sized like TODO.sota/03. The -28 source is
83
+ present in the detachment (`sources/iso-10303-28/`) for that future
84
+ work; nothing here pretends conformance.
85
+
86
+ ## Validation
87
+
88
+ - `spec/expressir/p21/parser_conformance_spec.rb` (11): annex E
89
+ sections/contexts, annex I anchors + SCHEMA_POPULATION +
90
+ signature/reference variants, annex J constant references through a
91
+ complex instance, malformed-input negatives.
92
+ - `spec/expressir/p21/{dataset_manifest,receipt,diff}_spec.rb` and
93
+ `spec/expressir/commands/population_spec.rb` (10): manifest
94
+ loading + short-name normalization, byte-identical receipts,
95
+ finding records, diff deltas, CLI exit codes.
96
+ - p21 + commands chunks: 71 examples, 0 failures; eval + mapping
97
+ chunks re-run green.
@@ -0,0 +1,83 @@
1
+ # 04 — ARM↔MIM instance mapping verification
2
+
3
+ The Thurman end-game: verify that a MIM population is the image of an
4
+ ARM population through the module's mapping tables — RefPath applied
5
+ to instances, closing the loop between the schema-side mapping
6
+ validator (TODO.refpath-v2) and real data.
7
+
8
+ ## Parts (each ships)
9
+
10
+ 1. Mapping semantics over instances: for a module's mapping.yaml,
11
+ given an ARM-typed population and the MIM population, follow each
12
+ mapped reference path from each ARM instance through the MIM
13
+ population and match the resolved target against the mapped
14
+ attribute's expected value.
15
+ 2. Instance-level RefPath: every path step resolves to actual
16
+ instances (typing from TODO.instance/01); constraint values matched
17
+ against actual attribute values; the `rules` lists checked as
18
+ instance predicates; `alt_map` alternatives accepted when the
19
+ primary path is unfollowable.
20
+ 3. Report + triage: per-module verification receipts (ARM instance id,
21
+ path, step, finding); drift classified as mapping drift vs
22
+ population drift; feeds the WG12 corpus-drift policy program with
23
+ instance-level evidence instead of schema-level inference.
24
+ 4. Thurman interop: output comparable with the annotated-links
25
+ validation of the PDES proposal (`pr463_validation_proposal.pdf`);
26
+ AP 242 ed5 sample datasets as the acceptance corpus (DIS
27
+ December 2026; AP 209/210/239/242 follow in 2027).
28
+
29
+ ## Acceptance
30
+
31
+ - Synthetic ARM/MIM pair built from a shipped module: the image
32
+ verifies clean; each seeded divergence (dropped instance, wrong
33
+ target, violated constraint) produces exactly the intended finding.
34
+ - An AP 242 sample dataset verifies to completion; every finding
35
+ carries path, step, and instance ids (triage-ready).
36
+ - No finding on any mapping the corpus proves valid — the standing
37
+ no-new-errors oracle, now at instance level.
38
+
39
+ ## Status: done (v1)
40
+
41
+ - `Expressir::Mapping::InstanceVerification` verifies that a MIM
42
+ population is the image of an ARM population through a
43
+ `Mapping::Document`: every ARM instance of a mapped entity must
44
+ have a MIM image whose typing includes the mapped MIM entity
45
+ (P21::Typing, task 01), whose element refpath resolves from that
46
+ image, whose attribute-mapping refpaths terminate at values equal
47
+ to the ARM attribute values, and whose inline element rules
48
+ evaluate true (`Eval::Rules.environment_for`, the task-02 WHERE
49
+ machinery, with self bound to the image).
50
+ - Instance-level RefPath walking covers the corpus constructs: bare
51
+ and attribute-qualified nodes, `->`/`<-` navigation, `<=`/`=>`
52
+ restatements, the §5 `||supertype||` and `*tree*` markers, and
53
+ `{entity.attr = literal}` constraints with `!` negation.
54
+ `alt_map` routes are accepted when the primary route is
55
+ unfollowable — the alt route is an instance predicate, exactly as
56
+ in the schema-side validator.
57
+ - Reference values correspond across the two populations through the
58
+ mapping itself: an ARM `#n` terminal matches the MIM image of ARM
59
+ instance n under the element mapping its keyword (memoized, so
60
+ correspondence stays linear).
61
+ - Scope lines, stated rather than silent: rules fields that name
62
+ global rules (the corpus norm) remain the domain of
63
+ `Mapping.rule_issues` — only inline expressions are instance
64
+ predicates; image search is existential, so a MIM population with
65
+ extra instances is not an error (no bijection is enforced); and
66
+ complex (multi-role) records fail their route rather than silently
67
+ passing.
68
+ - Validation: `spec/expressir/mapping/instance_verification_spec.rb`
69
+ — synthetic ARM/MIM pair in the shape of the smallest shipped
70
+ module mappings, clean image verified, and a mutation sweep: each
71
+ seeded divergence (missing image, constraint-path name divergence)
72
+ produces exactly one finding, and repairing it removes exactly that
73
+ finding; rule gating is proven by flipping the element rule, which
74
+ unmasks exactly that element's instances.
75
+ - AP 242 sample (acceptance part 2): no paired ARM+MIM sample dataset
76
+ ships in this repository or the detachment, and fabricating one
77
+ would test nothing but itself — so the synthetic-pair acceptance
78
+ stands in per the goal's missing-dataset provision. Finding
79
+ records carry ARM instance id and entity (triage-ready); path/step
80
+ attribution rides on RefPath::Report (TODO.refpath-v2) and the
81
+ per-module receipts of 03 when datasets exist. Thurman interop
82
+ (part 4) remains open for the WG12 corpus-drift program, which
83
+ owns the AP 242 DIS dataset sourcing.
data/lib/expressir/cli.rb CHANGED
@@ -139,6 +139,9 @@ module Expressir
139
139
  desc "validate SUBCOMMAND", "EXPRESS schema validation commands"
140
140
  subcommand "validate", Expressir::Commands::Validate
141
141
 
142
+ desc "population SUBCOMMAND", "Part 21 dataset validation commands"
143
+ subcommand "population", Expressir::Commands::Population
144
+
142
145
  desc "coverage *PATH",
143
146
  "List EXPRESS entities and check documentation coverage"
144
147
  method_option :format, type: :string,
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "thor"
5
+
6
+ module Expressir
7
+ module Commands
8
+ # `expressir population validate|diff` (TODO.instance/03):
9
+ # manifest-driven dataset validation with a deterministic receipt,
10
+ # and instance-level comparison of two populations.
11
+ class Population < Thor
12
+ desc "validate MANIFEST", "Validate the dataset and write a receipt"
13
+ method_option :output, type: :string, aliases: "-o",
14
+ desc: "Receipt path (defaults to stdout)"
15
+ def validate(manifest_path)
16
+ manifest = Expressir::P21::DatasetManifest.from_file(manifest_path)
17
+ schema = Expressir::Express::Parser.from_file(manifest.schema_path)
18
+ .schemas.first
19
+
20
+ results = {}
21
+ manifest.file_paths.each do |path|
22
+ population = Expressir::P21::Parser.parse(File.read(path))
23
+ results[path] = Expressir::P21::Validator.validate(
24
+ population, schema, short_names: manifest.short_name_map
25
+ )
26
+ end
27
+
28
+ receipt = Expressir::P21::Receipt.build(manifest, results)
29
+ if options[:output]
30
+ File.write(options[:output], receipt)
31
+ say "receipt written to #{options[:output]}"
32
+ else
33
+ $stdout.write(receipt)
34
+ end
35
+
36
+ errors = JSON.parse(receipt)["summary"]["errors"]
37
+ return if errors.zero?
38
+
39
+ raise Thor::Error, "dataset validation produced #{errors} error(s)"
40
+ end
41
+
42
+ desc "diff FILE_A FILE_B", "Compare two Part 21 populations by instance"
43
+ def diff(path_a, path_b)
44
+ base = Expressir::P21::Parser.parse(File.read(path_a))
45
+ revised = Expressir::P21::Parser.parse(File.read(path_b))
46
+ deltas = Expressir::P21::Diff.diff(base, revised)
47
+ say "added: #{deltas[:added].join(' ')}"
48
+ say "removed: #{deltas[:removed].join(' ')}"
49
+ say "changed: #{deltas[:changed].join(' ')}"
50
+ end
51
+
52
+ def self.exit_on_failure?
53
+ true
54
+ end
55
+
56
+ private
57
+
58
+ def say(message)
59
+ $stdout.puts(message)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -23,6 +23,7 @@ module Expressir
23
23
  autoload :ChangesImportEengine, "#{__dir__}/commands/changes_import_eengine"
24
24
  autoload :Version, "#{__dir__}/commands/version_command"
25
25
  autoload :Package, "#{__dir__}/commands/package"
26
+ autoload :Population, "#{__dir__}/commands/population"
26
27
  autoload :Manifest, "#{__dir__}/commands/manifest"
27
28
  autoload :MappingValidate, "#{__dir__}/commands/mapping_validate"
28
29
  autoload :Xsd, "#{__dir__}/commands/xsd"