expressir 2.4.37-arm-linux → 2.4.38-arm-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 +4 -4
- data/TODO.instance/00-overview.md +73 -0
- data/TODO.instance/01-instance-typing.md +81 -0
- data/TODO.instance/02-population-rules.md +82 -0
- data/TODO.instance/03-p21-ladder-and-population-management.md +97 -0
- data/TODO.instance/04-arm-mim-instance-mapping.md +83 -0
- data/lib/expressir/cli.rb +3 -0
- data/lib/expressir/commands/population.rb +63 -0
- data/lib/expressir/commands.rb +1 -0
- data/lib/expressir/eval/population.rb +186 -0
- data/lib/expressir/eval/rules.rb +0 -1
- data/lib/expressir/eval.rb +1 -0
- data/lib/expressir/mapping/instance_verification.rb +405 -0
- data/lib/expressir/mapping.rb +1 -0
- data/lib/expressir/p21/builder.rb +78 -2
- data/lib/expressir/p21/dataset_manifest.rb +45 -0
- data/lib/expressir/p21/diff.rb +40 -0
- data/lib/expressir/p21/grammar.rb +48 -6
- data/lib/expressir/p21/json.rb +128 -0
- data/lib/expressir/p21/population.rb +6 -2
- data/lib/expressir/p21/receipt.rb +45 -0
- data/lib/expressir/p21/typing.rb +116 -0
- data/lib/expressir/p21/validator.rb +419 -8
- data/lib/expressir/p21/writer.rb +107 -0
- data/lib/expressir/p21.rb +6 -0
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +1 -0
- data/lib/tasks/instance.rake +77 -0
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f572192157dec33033849b26db762ff1d5e3c9caf508c7ab16f3da300192097
|
|
4
|
+
data.tar.gz: 870fe1765370ae70bcc895bcb74144433cf117e491293fca56b829cb816e3315
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d543548d4cb72e64667b3c62bbc3ee7764a3a277a4c3396c113a0a7fd295183b843b2f02f0c48f857bf6f0a7a7fc8c7c8ca68450f978e7dc92933ba1c4a1cb62
|
|
7
|
+
data.tar.gz: d63dd1ec741f056b8143d7ef4ab899e636773110db5c76dda1e53062190937d44f5ff2b4bae9c4565c9f90c5c825e7d202f98138204fa5a6e775ddeb79677d16
|
|
@@ -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
|
data/lib/expressir/commands.rb
CHANGED
|
@@ -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"
|