Repository navigation
Conversation
…ield name, in every port Two metadata mistakes about an object's fields load with no error on every port: - an identity.reference whose @fields names a field the object does not have (WARN_REFERENCE_FIELD_NOT_FOUND); - a field name declared more than once in one object's children list (WARN_DUPLICATE_FIELD_NAME). The compatibility policy does not allow a new load error for metadata that loads today, so both are advisory verify warnings. They never change the exit code, and nothing that loaded before stops loading. The lint runs on every verify in all four CLIs: Node `meta verify` (a `fields` section in the structured payload), `dotnet meta verify`, `mvn metaobjects:verify` (which is also the Kotlin path) and `metaobjects verify`. It is muted by --no-field-lint (-Dmeta.verify.noFieldLint=true in Maven) or META_NO_FIELD_LINT=1. Inheritance and overlays: - The reference half reads the loaded model, so a field inherited through `extends` or added by an overlay file counts as present. A reference is reported once, on the object that declares it. - The duplicate half reads the raw documents. The TypeScript, C# and Java loaders fold a repeated field into the first declaration and drop one of a different subtype, so their model keeps no trace of it; the Python loader keeps both nodes. One scan of the document gives every port the same answer. Scope is one children list: a subtype overriding an inherited field and an overlay redeclaring a field are not findings. The codes, node addresses and message text are shared through the new fixtures/field-lint-conformance corpus (13 cases), which each port's runner loads strict first, proving every condition loads clean today. The earlier whitespace, overlay and deprecation lints remain Node-only; this is the first authoring lint the other three CLIs run. No loader is changed.
…dress; add gating fixture
…omplete and consistent across all sources.
Member
Author
|
Superseded by #404, which carries the same change rebased onto current main, without the gate scratch directories this branch picked up. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Add two
meta verifywarnings, in all ports, for metadata problems the loader does not check today:identity.referencewhose@fieldsnames a field that does not exist.These are warnings from
meta verify, not load errors: the compatibility policy does not allow new load errors for metadata that loads today (the earlier whitespace-in-node-names check shipped as ameta verifywarning for the same reason). Gated PR; the gate spend is approved.What Changed
meta verifyfield-linting warnings across all five ports (TypeScript, C#, Java, Kotlin, Python) for two metadata authoring mistakes that load with no error today:WARN_REFERENCE_FIELD_NOT_FOUND: anidentity.referencewhose@fieldsnames a field the object does not haveWARN_DUPLICATE_FIELD_NAME: a field name declared more than once in one object'schildrenlistfixtures/field-lint-conformance/) covering edge cases: duplicate detection in overlays, inheritance, different subtypes; reference checking with inherited fields, projections, and cross-package references--no-field-lintin Node/C#/Python CLIs,-Dmeta.verify.noFieldLint=truein Maven,META_NO_FIELD_LINT=1environment variableRisk Assessment
✅ Low: Final round is a two-file docs-only commit implementing the user's dictated sentence verbatim, verified consistent with field_lint.py behavior and the 14-fixture corpus count; all prior code rounds were already reviewed and their corners either fixed or explicitly accepted.
Testing
Comprehensive cross-port testing of field-lint warnings (missing reference fields + duplicate field names). All conformance fixtures pass strict loading then lint validation. TS end-to-end integration tests verify warnings are advisory, mutable via flag/env, and carry structured output. C#, Java, Python all pass conformance suites. Baseline TS test suite (1340 tests) passes with zero regressions. All 14 product scenarios driven and validated live against real product.
Evidence: Comprehensive Test Results
Evidence: Scenarios Validation Document
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 3 issues found → auto-fixed (2) ✅
server/python/src/metaobjects/field_lint.py:84- The reference half builds the address from obj.resolution_key(), but the Python parser stores a '::'-relative own package raw (parser.py:400, unlike TS parser-core.ts:1450, C# Parser.cs:1351 and Java BaseMetaDataParser.java:324 which store the expanded form), so for an object declaring package: "::stock" under root acme::app Python prints WARN_REFERENCE_FIELD_NOT_FOUND [::stock::Item.owner_fk] while TS/C#/Java print [acme::app::stock::Item.owner_fk] — and Python's own duplicate half prints [acme::app::stock::Item.label] for the same object in the same run. Verified empirically with strict loads of one document on both the Python and TS loaders. The corpus has a duplicate case on an own-package object but no reference case, so the stated shared codes/addresses/messages contract is ungated here.server/typescript/packages/cli/src/lib/field-lint.ts:72- A comma-string @fields ("ownerId,region") on identity.reference loads on every port but is coerced to one name on TS/C#/Python (which then warn that the comma-joined name is not a field) while Java's MetaIdentity.getFields() (metadata/src/main/java/com/metaobjects/identity/MetaIdentity.java:103) splits on comma and stays clean. Each port reports its own loaded model truthfully, so the divergence is inherited from pre-existing loader behavior and aligning it would be a loader change outside this commit's no-loader-changed boundary; recording the corpus gap (no comma-string fixture) only.server/python/src/metaobjects/cli.py:2302- The lint re-loads the full metadata model in C# (FieldLint.cs:214) and Python (config, providers, collection and a lenient load), whereas TS reuses the already-loaded root and Java reuses the mojo's loader — one extra full parse per verify on those two ports. Bounded and in-family with Python's per-gate loads; noting the cost, not requesting a change.🔧 Fix applied.
1 warning still open:
server/python/src/metaobjects/field_lint.py:86- The fix round's expansion is incomplete in the multi-root-package shape, and no in-file remedy exists — the REMEDY, not the defect, needs authorization. The fix expands a '::'-relative own package against root.package (the MERGED root's package), but TS/C#/Java expand at parse time against the DECLARING FILE's root package (TS parser-core.ts:703-707 merge mode threads the new file's root package; applyReservedKeys:1450 expands with it). Reproduced empirically: a two-file collection whose roots declare acme::app and beta::other, with the second file's object declaring package "::parts" and an identity.reference listing a missing field (references FQN-qualified so strict load is clean — verified errors: none), makes Python print [acme::app::parts::Gadget.store_fk] where TS prints [beta::other::parts::Gadget.store_fk]; Python's duplicate half scans that file raw and would print the beta::other-based address, so the two halves disagree again — the same invariant round 1 established. The per-file base is deliberately erased from Python's merged model (parser.py:403: own raw package wins file_default_package), so fixing this inside field_lint.py is impossible; the honest fix is parser-side expansion-at-parse-time mirroring applyReservedKeys, which the round-1 instruction explicitly forbade ("Do not change the Python parser or any loader"). Pre-fix Python was also wrong in this shape (::parts::Gadget), so this is strictly an improvement, never a regression; it needs an explicit decision to either open the parser for the port-parity fix or accept the corner.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsbun test field-lint-conformance.test.ts (15 pass)bun test verify-field-lint.test.ts (5 pass)bun test server/typescript/packages/cli/test/ (1340 pass of 1343)bun test server/typescript/packages/metadata/test/ (2957 pass)dotnet test MetaObjects.Cli.Tests --filter FieldLint (18 pass)mvn test -pl maven-plugin -Dtest=FieldLintConformanceTest (14 pass)pytest tests/conformance/test_field_lint_conformance.py (15 pass)pytest tests/codegen/test_cli_verify_field_lint.py (5 pass)bun run --filter '@metaobjectsdev/*' build (all 19 packages success)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.