You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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-Licensesafter 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 refusal — IN-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 declaredhoist=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.