|
| 1 | +# `verify` field-lint conformance corpus |
| 2 | + |
| 3 | +Every port's `verify` command prints an advisory **field authoring lint**. It reports two |
| 4 | +metadata mistakes that load with no error: |
| 5 | + |
| 6 | +| Code | What it reports | |
| 7 | +|---|---| |
| 8 | +| `WARN_REFERENCE_FIELD_NOT_FOUND` | An `identity.reference` whose `@fields` names a field its object does not have. | |
| 9 | +| `WARN_DUPLICATE_FIELD_NAME` | A field name declared more than once in one object's `children` list. | |
| 10 | + |
| 11 | +Both are warnings and never a load error: the compatibility policy |
| 12 | +(`docs/compatibility-policy.md`) does not allow a new load error for metadata that loads |
| 13 | +today. Neither reaches an exit code. |
| 14 | + |
| 15 | +This corpus is the shared source of truth for the codes, the node addresses and the message |
| 16 | +text. Each port's own test suite runs it, so the ports cannot drift. |
| 17 | + |
| 18 | +## Fixture format |
| 19 | + |
| 20 | +Each case is a directory: |
| 21 | + |
| 22 | +- `input/` holds one or more metadata documents (`.json` or `.yaml`). |
| 23 | +- `expected.json` holds `{ "findings": [{ "code", "path", "message" }] }`. |
| 24 | + |
| 25 | +A runner does three things, in this order: |
| 26 | + |
| 27 | +1. Load `input/` with the port's loader, strict, and assert **no load errors**. This is |
| 28 | + what proves each condition loads today. |
| 29 | +2. Run the reference half over the loaded model and the duplicate half over the raw files |
| 30 | + in `input/`. |
| 31 | +3. Compare the findings with `expected.json` as an unordered set of |
| 32 | + `(code, path, message)`. |
| 33 | + |
| 34 | +`path` is the declaring object's resolution key (`<package>::<name>`), a dot, then the |
| 35 | +identity name or the field name. In Python, for a multi-file collection whose roots declare |
| 36 | +different packages, the address printed for a `::`-relative package is expanded against the |
| 37 | +merged root's package and can differ from the other ports. |
| 38 | + |
| 39 | +## The two halves read different things |
| 40 | + |
| 41 | +**The reference half reads the loaded model.** Whether an object has a field is a question |
| 42 | +about its *effective* field set, so the lint counts: |
| 43 | + |
| 44 | +- a field **inherited** through `extends` (`reference-field-inherited-clean`); |
| 45 | +- a field added by an **overlay** file (`reference-field-from-overlay-clean`). |
| 46 | + |
| 47 | +A reference is reported once, on the object that declares it, and is checked against that |
| 48 | +object's effective fields. An inherited reference is not repeated on every subtype |
| 49 | +(`reference-declared-on-base-reported-once`). |
| 50 | + |
| 51 | +**The duplicate half reads the raw documents.** The TypeScript, C# and Java loaders fold a |
| 52 | +repeated field into the first declaration and drop one of a different subtype, so their |
| 53 | +loaded model keeps no trace of the duplicate. The Python loader keeps both nodes. One scan |
| 54 | +of the document gives every port the same answer. The scan covers root-level objects, and |
| 55 | +accepts the YAML authoring sugar (a bare `field` key, a scalar body, a `[]` key suffix). |
| 56 | + |
| 57 | +The scope is **one `children` list**. These are not findings: |
| 58 | + |
| 59 | +- a subtype redeclaring an inherited field. That is an override |
| 60 | + (`duplicate-inherited-override-clean`). |
| 61 | +- an overlay file redeclaring a field of its base. That is the overlay merge |
| 62 | + (`duplicate-across-overlay-clean`). |
| 63 | + |
| 64 | +## Who asserts it |
| 65 | + |
| 66 | +| Port | Runner | |
| 67 | +|---|---| |
| 68 | +| TypeScript (reference) | `server/typescript/packages/cli/test/field-lint-conformance.test.ts` | |
| 69 | +| C# | `server/csharp/MetaObjects.Cli.Tests/FieldLintConformanceTests.cs` | |
| 70 | +| Java / Kotlin | `server/java/maven-plugin/src/test/java/com/metaobjects/mojo/FieldLintConformanceTest.java` | |
| 71 | +| Python | `server/python/tests/conformance/test_field_lint_conformance.py` | |
| 72 | + |
| 73 | +Kotlin has no CLI of its own. Its codegen runs through the Maven `metaobjects:verify` goal, |
| 74 | +so the Java runner covers it. |
0 commit comments