mcpField
G
grid-validator

grid-validator

github.com/datexTesting & quality92 installs
Repository

About grid-validator

grid-validator is a read-only skill for auditing one Datex Studio grid configuration before it is uploaded. It returns findings by severity and focuses on grid-specific contract and data-shape issues that a generic component validator may miss.

Key features

  1. 01Read-only grid configuration audits
  2. 02Severity-ranked blocker, warning, and nit lists
  3. 03Checks embedded datasource identity and shape
  4. 04Checks text coercion and entity-field synchronization
  5. 05Separates grid checks from cross-component wiring

Use cases

  1. 01Review a grid before upserting it to a branch
  2. 02Find datasource envelope or entity-shape drift
  3. 03Check non-string grid values before import

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/datex/skills --skill grid-validator

After installing, type/grid-validatorin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

evals/
evals.json
evals/files/
carrier_rates_grid-grid.json
dock_appointments_grid-grid.json
shipment_exceptions_grid-grid.json
wave_pick_summary_grid-grid.json

Details

From the project README

Grid Validator

Audit a single Datex Studio grid file (*-grid.json) against grid-specific authoring rules — the mandatory final gate after grid-creator has authored or modified a grid, and before the change is upserted to the branch. This skill is grid-only: it carries gotchas the generic component-validator does not catch (envelope-vs-body shape, text-display coercion, five-location dynamic-filter sync, imperative cell API mismatches, tailored vs custom provenance). For non-grid component files, route to component-validator instead.

This validator reads; it does not edit. It returns a structured punch-list (Blockers / Warnings / Nits) — never a rewrite. The parent (typically grid-creator, or the user directly) owns the fix.

See also: component-validator — generic dispatcher for non-grid component files (actions, functions, forms, editors, hubs, storage, selectors, customTypes, backendTests, datasources). Falls back to grid-creator/references/grids.md for grids if grid-validator is unavailable, but the generic dispatcher does not carry the grid-specific gotchas listed below.

See also: component-wiring-check — cross-component reference contracts (one component pointing at another). This validator only audits a single grid file in isolation; it flags obvious wiring drift it can see in that one file (e.g. a configParameters block that does not mirror the file's own inParams) but does not chase external references.

References

Dependencies

  • grid-creator — the creator skill whose references/grids.md is the authoritative rulebook this validator audits against. Update that document and this validator picks up the new rules automatically.
  • tailoring-overlay — overlay-specific shadow-marker rules invoked when the grid is a tailored variant rather than a base grid.
  • datasource-creator — backing-datasource rules (OData and flow) applied to the embedded datasources[0] block.
  • component-wiring-check — cross-component audit invoked when the punch-list surfaces wiring drift the parent wants to chase further.
  • datex-studio-conventions — generic cross-cutting rules (file format, naming, defaults) that apply on top of the grid-specific checklist.
  • datex-studio-shared / datex-studio-runtime — branch-setup primitives and platform-runtime globals referenced by the grid rule doc.
  • component-validator — sibling generic dispatcher for non-grid component files. Mutually exclusive: a grid file is grid-validator's territory; any other suffix routes to component-validator.

Workflow

This skill is typically invoked by grid-creator at the end of its authoring loop, or by the user directly with "audit this grid before merge". The invocation pattern is a Task-tool sub-agent dispatch — give the prompt template in the ## Sub-agent section below to the sub-agent along with the target file path.

Invocation (orchestrator side)
  1. Confirm the target is a grid body — the grid the parent has just authored or modified. The branch is the source of truth, so the target is either the staged scratch body.json about to be dxs configuration upsert-ed, or a fresh fetch (dxs source explore config <referenceName> --branch <id>, or dxs configuration get grid <id> -b <id> -O envelope.json && jq .json envelope.json > body.json). Its referenceName must end in -grid (or tailored_*-grid / custom_*-grid for overlay variants). If it's any other component type, stop and route to component-validator instead. Never audit a persistent local src/ copy as authoritative.
  2. Dispatch the sub-agent — use the Task tool with the prompt template from the ## Sub-agent section. The sub-agent has Read, Grep, Glob access; it does not edit.
  3. Apply the punch-list — the sub-agent returns a markdown punch-list grouped by severity. Treat each finding as follows:
    • Blockers — must be fixed before upserting. Route the fix back to grid-creator.
    • Warnings — review and decide. May be deliberate; the sub-agent never assumes intent.
    • Nits — optional cleanup. Defer unless the parent is already touching that area.
  4. Re-validate after fixes — if Blockers were fixed, run the validator again. The validator is cheap (one Read + one Grep through the rule doc) and should be the last step before the push.
Scope discipline (validator's contract)
  • The validator reads; it does not edit. The parent owns the fix.
  • The validator returns a punch-list; it does not return a rewrite.
  • The validator audits one grid file; it does not chase cross-component references (that is component-wiring-check's territory).
  • The validator does not load raw OData schema documents. If the embedded backing datasource needs entity / property validation against the live schema, the validator recommends the parent invoke schema-explorer separately.
  • The validator does not speculate about intent. If a rule violation could be deliberate, it is flagged as a Warning with a note, not as a Blocker.

Sub-agent

The prompt template below is what the orchestrator passes to the Task-tool sub-agent dispatch. The sub-agent is read-only (Read, Grep, Glob).


You validate a single Datex Studio grid component file (*-grid.json) against the platform's grid authoring rules. You do not edit files. You return a punch list.

Workflow
  1. Load the target grid body. The parent gives you the path to a single JSON file — the scratch body.json staged for upsert, or a config just fetched from the branch (its referenceName is <name>-grid, tailored_<name>-grid, or custom_<name>-grid). This is a throwaway temp file, not a persistent source-of-truth copy. Read it in full. The minified JSON envelope is the surface you will audit against the checklist.

  2. Load the grid rulebook. Read ../grid-creator/references/grids.md — its checklist sections are the rulebook. If the file is a tailored or custom-provenance overlay, also read ../tailoring-overlay/references/tailoring.md. The docs explain the why behind each rule; don't re-derive them from memory.

  3. Walk the checklist item-by-item. For each issue, record:

    • Severity — blocker (silent runtime failure, import error, or clear rule violation), warning (drift or inconsistency that may or may not bite), nit (style / naming / optional cleanup).
    • Rule — which checklist item it maps to.
    • Location — <filename>:<line> or JSON path (e.g. datasources[0].queryOptionsObjectTypeDef).
    • Evidence — short quote or description.
  4. If the grid is OData-backed, verify queryOptions.selects includes every field declared in the entity-shape locations. Consult ../datasource-creator/references/odata-datasources.md for the OData envelope rules. If the parent authorizes invoking the schema-explorer skill, use it to confirm entity / property / navigation names against the live OData schema; otherwise flag unverified names as warnings.

    If the grid is flow-backed (the backing datasource resolves to a function rather than an OData entity), consult ../datasource-creator/references/flow-datasources.md for the flow-datasource envelope rules.

  5. Always probe these grid-specific failure modes explicitly — they are not caught by a casual checklist walk and the generic component-validator does not carry them:

    a. Embedded datasource component-identity envelope (envelope shape). Confirm datasources[0] carries all of referenceName (must equal datasourceConfig.configId verbatim), title, description, hasKey, hasResult, id, linkedDatasources, customColumns, inParams, outParams, vars, events, accessModifier, and the correct configurationTypeId per ../datex-studio-conventions/file-format.md. Also confirm the datasourceConfigs array and componentReference (when present) line up with the grid's own envelope. Missing referenceName is a blocker — it produces Invalid contract. Referenced own configuration <name> does not exist or has been renamed and cascades into Cannot find name 'get'/'getList'/'inParams'/'refresh' TS errors at import.

    b. Text-display string coercion. For every column with displayControl.type: "text" whose textConfig.value dereferences $row.entity.<Field>, look up <Field>'s declared type in the finalized outParams[result].objectTypeDef. If it's anything other than string, the binding must coerce (e.g. "$row.entity.Id?.toString()" for numbers, template-literal backtick-wrapping for compositions, $utils.date.format(...) for dates). An uncoerced non-string binding is a blocker — import fails with Type '<number|boolean|...>' is not assignable to type 'string'. Apply the TS-expression encoding rules from ../datex-studio-conventions/file-format.md: declarative string slots that are literal text need 'literal' single-quoting; compositions need backtick-wrapping.

    c. Entity-shape five-location sync. When a base entity field is bound anywhere in the grid, confirm it appears in all five entity-shape locations per grids.md → Datasource Wiring — Five Places Must Stay in Sync: (1) datasources[0].queryOptionsObjectTypeDef, (2) datasources[0].outParams[result].objectTypeDef, (3) datasources[0].getListFlow.outParams[result].objectTypeDef, (4) datasources[0].getByKeysFlow.outParams[result].objectTypeDef, (5) datasourceConfig.configOutParameters[result].objectTypeDef. For an OData-backed datasource, locations 3 and 4 are null — skip them, but the field must also appear in datasources[0].queryOptions.selects (the sixth, runtime-only location that actually retrieves it). Drift in any required location is a blocker — the field is undefined on $row.entity or import fails with a contract error.

    For linked/custom output fields, check the finalized public result and its consumer snapshot against output-contracts.md, rather than inserting the derived field into the base entity. Use server-generated context to check method result shapes. Flow pagination totalCount belongs to getListFlow, not the top-level component/reference.

    d. Dynamic-filter / sort registration sync. When a column declares a dynamic filter or sort, confirm both halves of grids.md → Dynamic Filters and Sorting: (1) the field is registered at both top-level sites — datasources[0].dynamicFilters/dynamicOrderBys (embedded-datasource side) and datasourceConfig.dynamicFilters/dynamicOrderBys (consumer side) — with identical entries; and (2) the column carries its per-column wiring (dynamicFilter, dynamicOrderBy, dynamicFilterType, dynamicFilterControl), with dynamicFilter using the registered plain property path (e.g. Lines.Quantity) and dynamicOrderBy matching scalar sort registrations/context. For collection-navigation filters, follow collection-filter-authoring: verify nested object boundaries, preserved flags, generated quantifier constants, grid mode, and actual flow execution; keep generated $any/$all runtime constants out of persisted property IDs and the grid column's plain-path dynamicFilter. Drift between the two registration sites is a blocker — import fails with Outdated contract. Type mismatch for dynamic filtering/sorting. Do not flag a missing $filter/$orderby in any inParams list — those are auto-injected from the registrations and a manual entry is itself the bug.

    e. Imperative cell API mismatches. A column wired for cell-click fires the flow named in its onCellClickFlowConfig (typically under rowFlows); confirm that flow exists in the grid's flows / rowFlows block (or a linked flow file). Imperative writes in row/grid flows use the cell surface from grids.md → Imperative Cell API: $row.cells.<col>.displayControl.text / .displayControl.value, $row.cells.<col>.editControl.value, and the read-only $row.cells.<col>.editControl.isChanged. A cell-click flow reference with no matching definition is a blocker; an imperative write to a cell surface or column that doesn't exist is a warning.

    f. Tailored vs custom provenance conformance. If the file stem starts with tailored_, the file is an overlay and the shadow-marker rules in ../tailoring-overlay/references/tailoring.md apply on top of the base grid's checklist. If the file stem starts with custom_, the file is a customer-specific variant and the custom_ provenance rules in ../datex-studio-conventions/naming-conventions.md apply. Mixing the two prefixes (e.g. tailored_custom_foo-grid.json) or omitting the prefix on a tailored variant is a blocker.

    g. $grid.* runtime usage. If the grid uses $grid.* / $row.* / $col.* globals inside TS expressions, confirm the globals exist and are used per ../datex-studio-runtime/runtime-globals.md. Unknown global identifiers are a blocker (TS error at import); deprecated globals are a warning.

  6. Always probe the universal cross-cutting failure modes even if the grid checklist does not restate them. These are enumerated once in ../datex-studio-conventions/universal-checklist.md — walk that list (description ≤ 100 chars, accessModifier set, referenceName ↔ stem, single-line minified JSON, correct configurationTypeId, snake_case new inParams/outParams ids, id: 0 if net-new). Plus one grid-relevant wiring check:

    • configParameters block, when present, mirrors the file's own inParams per ../component-wiring-check/references/component-wiring.md. A mismatch the validator can see from the grid file alone is a warning (recommend the parent invoke component-wiring-check for the full cross-component audit).
  7. Report. Return a short markdown punch list grouped by severity, each item one or two lines. No preamble, no rewrites, no code suggestions beyond one-line pointers. If nothing is wrong, say No issues found.

Scope Discipline
  • You read. You do not edit.
  • You return a punch list. You do not return a rewritten file.
  • You do not chase references into other component files — cross-component wiring is component-wiring-check's territory. If a wiring issue is obvious from the grid alone, flag it and let the parent decide whether to follow up.
  • You do not load raw OData schema documents. If entity / property / navigation validation is needed, recommend the parent invoke the schema-explorer skill separately.
  • You do not speculate about intent — if a rule violation could be intentional (e.g. an empty displayControl.value that's legitimate imperative population), flag it as a warning with a note, not a blocker.
  • If the file is not a grid (suffix is not -grid.json and not a tailored_*-grid.json / custom_*-grid.json overlay variant), reply Cannot validate: file is not a grid. Route to component-validator instead. and stop.
Output Format
## Blockers
- [`datasources[0].queryOptionsObjectTypeDef`] `warehouse_id` missing (present in locations 2, 3, 4, 5). Entity-shape five-location sync drift.
- [`datasourceConfig.dynamicFilters`] `warehouse_id` registered on `datasources[0].dynamicFilters` but missing here. Dynamic-filter registration drift.
- [`columns[2].textConfig.value`] `$row.entity.OrderTotal` is `number`; needs `.toString()` coercion or template-literal wrapping.

## Warnings
- [`columns[3].dynamicFilter`] targets `accounts` (primitive array) directly; needs a scalar sidecar for joined-display matching. Object-collection scalar leaves use the quantified-filter workflow instead.
- [`columns[5].onCellClickFlowConfig`] references flow `confirmDelete`, which is not defined in `flows` / `rowFlows`.

## Nits
- [`description`] 118 chars — exceeds the ≤100-char soft limit.
- [`title`] equals `referenceName`; a human-readable title is more discoverable in Datex Studio listings.

Omit any bucket that is empty. If all three buckets are empty, return No issues found.

Comments

Markdown supported