Skip to content

25R2-0.52.0

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Sep 19:22

Added

  • GI alignment diagnostics + a /docs/admin/gi-alignment console page. resolveFields refuses a GI's curated column annotations wholesale when the design-row → OData-property mapping isn't provably unique — the right call, since a shifted mapping attributes every description to the wrong column and nothing downstream can detect it. But the refusal was invisible: describe_inquiry simply returned bare names while the descriptions sat correctly in Acumatica. resolveFields now reports its own verdict through an optional sink into GiRegistry.alignment (GiAlignmentDiagnostic[] — status plus activeRows/properties/captioned/described/hoisted per GI), and the new admin page renders it worst-first with a per-status remedy. The statuses separate causes that need different fixes: alignmentAmbiguous (caption the ambiguous columns), feedMissingActiveFlag / feedRowsExceedProperties (re-import MCPGIFields.xml, clear the GI cache), noMetadata, noFieldRows. The page reads only the cached registry from KV — no Acumatica request, no borrowed user token, nothing in Acumatica's audit trail. The diagnostic lives on GiRegistry, never on GiRegistryEntry, so nothing model-facing changed; registries built before this version simply report no alignment data and the page says how to force a rebuild. Tool count unchanged at 51.

Fixed

  • expectedTypeFamily() classified any field merely starting with "is"/"has" as boolean, refusing whole GIs. The prefix test ran against the lowercased field name, so /^(is|has)[a-z]/ matched issueDate ("is" + "sueDate"). That made it contradict its own Edm.DateTimeOffset property, score as an impossible pairing, and refuse the entire GI's annotation. Found on FS-Licenses after the operator had captioned every hoisted key exactly as prescribed — and because the conflict sat on an uncaptioned row, no caption edit could ever have fixed it. Now tested as /^(is|has)[A-Z]/ against the original camelCase, where real DAC booleans (isActive, hasChildren) have the uppercase boundary and issueDate/issuedBy/hashValue do not; an all-lowercase field yields no constraint rather than a wrong one, which is the safe direction for a check whose false positives silently drop annotations. Two regression tests, including an end-to-end case built from the real FS-Licenses shape.

Notes

  • Re-measured the refusals for the first time since 0.48.2: 118 of 121 gated GIs align, 3 refuse, and the refusals hide ZERO curated descriptions. Every GI that has column descriptions aligns. The long-quoted "13 refuse" figure predated the 0.49.1 bitmask-DP fix (which existed to stop false refusals) and had never been re-measured. Also disproved: caption density does not predict refusalIN-StockItem aligns at 7 % captioned and GL-Journal Transactions at 0 %, because the declared-type constraint resolves them.
  • Caption-pinning validated live, and it is a declaration rather than a guess. Acumatica derives a property name from the column's display name, so captioning a row causes its property name and the mapping is then correct by construction. HPL-PHYINVDATADB and HPL-AcuStockIssues both flipped to aligned, with values confirming the pairings. Two cautions learned in the process: a typo in a caption becomes the property name (DcoumentType_2, LastMoidified are live examples — correcting one renames the property and breaks consumers, so fix before any description references it), and collision suffixes must be captioned literally (ReferenceNbr_2).
  • Known gap recorded, not fixed: a GI whose name contains a URL path separator is registered but unusable. A GI named with / resolves in $metadata (the server matches via normalizeName, which strips punctuation) so the registry builds a full entry — while run_inquiry 404s and list_generic_inquiries never returns it. It had always been undiscoverable despite being tagged ExposedToMCP; the operator un-exposed it. align_columns.mjs checks for this and the server does not. Same file also models a dropped-row state resolveFields lacks — that drift is now recorded in CLAUDE.md.
  • Dead end, so it isn't retried: do not derive the hoist count by sweeping candidate values. On one GI the declared hoist=2 refuses while both H=0 and H=1 align, so a sweep produces multiple candidate answers and must refuse anyway. Relatedly, Acumatica sometimes hoists columns that are not declared keys, a shape the hoist-prefix model cannot express at all.