Browse documentation
Docs/Language

Declared Presentation — FORMAT, VALIDATE, PRESENT, ## notes

A model's meaning lives in its formulas; how a value should look, what writes it accepts, and why it exists are usually scattered across UI state. Declared presentation brings all three next…

Watch it in Grid

See the concept in Grid.

Use the film for the product interaction and this article for the complete contract and reference detail.

Declared Presentation — FORMAT, VALIDATE, PRESENT, ## notes

A model's meaning lives in its formulas; how a value should look, what writes it accepts, and why it exists are usually scattered across UI state. Declared presentation brings all three next to the cell, in source:

## Quarterly revenue, gross of returns.
input revenue is currency = 100000
  FORMAT "$#,##0.00"
  VALIDATE BETWEEN 0 AND 10000000 MESSAGE "Revenue must be a positive dollar amount."

Record input fields can instead use a standalone VALIDATE FIELD declaration. It names an exact member path on the owning input; whole-input and field rules are checked together against the proposed batch. See the field-rule reference for syntax, limits and the development verification status. These contracts do not add a browser evaluator or extend model write ownership to computed fields. Private record locals can reuse literal rules as described below.

Clauses trail the declaration — inline or on directly following indented lines. They are declarations, not statements: the cell's formula stays exactly what it was.

Layering

Presentation resolves through four layers, weakest to strongest:

tag-implied default          a currency tag suggests currency rendering
  < tag policy               top-level FORMAT <tag> <expr>
    < declared per-cell      trailing FORMAT/VALIDATE clause, ## note
      < override layer       rule FORMAT actions + UI/API edits

RESET FORMAT x (a rule action) clears the override layer and drops back to the declared layer. Overridden cells report their provenance ("overridden (declared: …)") so a declared format is never silently lost.

Doc notes — ##

A ## line documents the next declaration; a trailing ## documents its own line:

## Discount applied before tax.
## Negotiated annually with the vendor.
discount = 0.15

tax_rate = 0.0875   ## City + state combined.

A blank line detaches a ## block, so decorative comment banners stay inert. Notes ride module metadata into the UI (cell inspection, tooltips) — they are documentation, not evaluated content.

An intervening ordinary-comment line also detaches the block. Attachment is currently supported for named, cell and range assignments. Single # comments must occupy their own line; use /* … */ for trailing comments that should not become documentation metadata.

FORMAT

Three placements, one vocabulary (spreadsheet format patterns):

price = 19.99 FORMAT "$#,##0.00"       /* per-cell declaration */

FORMAT percentage "0.0%"               /* tag policy: every percentage-tagged cell */

WHEN audit_mode THEN
  FORMAT price "$#,##0.0000"           /* dynamic override from a rule */
END

Format operands are expressions: FORMAT IF(compact_view, "$#,##0,,\"M\"", "$#,##0.00") re-derives when its inputs change. Tag policies resolve most-specific-first through the type lattice, so a USD policy beats a currency policy beats a number one.

PRESENT

A trailing PRESENT <Kind> chooses the usual Grid View element for a binding:

input qty = 2 VALIDATE 1..10 PRESENT Slider
SURFACE Order
  qty
  NumberInput qty
  show qty
END

The first line inside the view uses Slider; the second explicitly uses NumberInput; show qty remains a read-only presentation. The preference never changes the cell's formula, type, ownership, or validation. A control preference applies to editable placements and a display preference applies to read-only placements. The opposite direction uses its normal presentation.

The priority is an explicit kind on the view line, then the binding's PRESENT, then the most specific tag policy (PRESENT <tag> AS <Kind>), then the inferred root default. A preferred kind must accept the binding's subject type. local declarations can also carry PRESENT. Like FORMAT and VALIDATE, it may appear inline or on a directly following line, at most once per declaration.

VALIDATE

The clause vocabulary reuses the language's predicate keywords with the subject implicit:

input qty    is integer = 1        VALIDATE BETWEEN 1 AND 100
input qty2   is integer = 1        VALIDATE 1..100          /* sugar for BETWEEN */
input margin is percentage = 5pct  VALIDATE >= 0
input region = "EMEA"              VALIDATE IN ["EMEA", "AMER", "APAC"]
input invoice = ""                 VALIDATE LIKE "INV-%"
input code   = ""                  VALIDATE STARTS WITH "GRID-"

Accepted clause heads: the comparisons (=, <>, <, <=, >, >=), BETWEEN … AND … (and the lo..hi sugar), IN, LIKE / ILIKE, STARTS WITH, ENDS WITH, CONTAINS. An optional MESSAGE "…" supplies the rejection text. Bounds are expressions — VALIDATE 1..max_qty re-derives when max_qty changes. Any other body, such as a brace array VALIDATE {2, 3} or a bare VALIDATE 5, is refused with GRID_PRESENTATION_VALIDATE_CLAUSE; write a list as VALIDATE IN [2, 3].

The .. range separator does not depend on surrounding spaces or comments. Imported bounds in VALIDATE fin.low..fin.high bind exactly as in VALIDATE fin.low .. fin.high; each bound resolves independently in the current lexical scope.

VALIDATE means one of two things depending on where it sits:

On an input cell: a write contract

Every write surface — the grid UI and the model-binding ABI, so API and connector writes too — checks the incoming value (after type-tag coercion) against the clause. A rejected write returns the violation and leaves the cell unchanged. Native batched writes validate their complete proposed state before committing any member: a clause can depend on another input in the same batch, including a default restored by a clear. The detached check does not launch external work or commit rule events. Unresolved operands and unsupported clone capabilities refuse the batch. The contract gates writes only; changing a bound later does not retro-invalidate a stored value.

Grid View controls show the declared message for text predicates and text/Boolean equality before saving. Forms retain invalid drafts, read staged values for direct input operands, and refresh feedback when those inputs change. Computed operands continue to use committed native results. Numeric/structured equality feedback remains native-owned. See Grid Views for the canonical order-details example and Code's checked validation editor.

On a computed cell: a static assertion

The compiler's interval analysis propagates value ranges through formulas and discharges the assertion at compile time:

input x = 0 VALIDATE 0..1
y = x / (x + 1) VALIDATE 0..1    /* proven — recorded in module metadata */
z = x + 2      VALIDATE 0..1    /* GRID_VALIDATE_UNSATISFIABLE: can never hold */
w = x * 3      VALIDATE 0..1    /* GRID_VALIDATE_UNVERIFIED: not provable */

An input's write contract seeds the range other formulas observe — that is what lets y above be proven outright. Refuted assertions are warnings by default and hard errors under strict (see reference §5.4); unverified ones stay warnings, an honest signal that nothing checks the assertion at runtime.

Where clauses may not appear

Compound assignments (+=), graph bindings, and table bindings do not take presentation clauses — declare them where the cell is defined.

See also

Reusable record contracts

A named contract collects field rules for several model inputs. INCLUDE uses another contract at the same root; INCLUDE ... AT path prefixes its exact field paths. Definitions may refer to a later contract. Recursive inclusion is an error.

RECORD CONTRACT AddressRules
  FIELD city <> "" MESSAGE "Enter a city"
END

RECORD CONTRACT OrderRules
  FIELD amount > 0 MESSAGE "Enter a positive amount"
  INCLUDE AddressRules AT shipping
END

input Order = {amount: 2, shipping: {city: "London"}}
input RepeatOrder = {amount: 3, shipping: {city: "Paris"}}
VALIDATE RECORD Order USING OrderRules
VALIDATE RECORD RepeatOrder USING OrderRules

FIELD accepts the existing validation clauses and optional MESSAGE. Dot paths and quoted keys in brackets have the same exact-key meaning as VALIDATE FIELD; numeric and computed keys are rejected. Bounds may read model values and are checked against the complete proposed input batch. Whole-input validation and additional standalone field rules still apply.

The compiler expands each use into the existing native record contract. Bound expressions are admitted once for each used definition; unused definitions do not create executable bound cells. Reusing one rule at several paths retains a separate validation identity at every path. An application requires the same unique local input owner as VALIDATE FIELD; it does not make a calculated value writable. Empty definitions, missing includes, cycles and overflow fail admission without truncating the rules.

A source admits at most 256 named contracts and 4,096 model applications. Each contract has at most 256 direct entries and expands to at most 256 field clauses per owner. Paths have at most 32 keys, each at most 512 UTF-8 bytes. Expansion has a shared 4,096-visit and 8 MiB metadata budget and at most 32 nested inclusions. Contract names are case-insensitive; field keys preserve their exact spelling.

VALIDATE ROWS Lines USING LineRules checks each record in a model-input vector. Use an exact member path such as Order.lines for a nested collection; quoted keys retain their spelling. Flat, horizontal and vertical vectors admit at most 65,536 records; empty vectors satisfy the row clauses. Missing members and malformed collection shapes fail validation. Bounds use the complete proposed input batch. A rejected edit leaves the input unchanged and reports the first failing row and exact field path for each clause. The model row example shares a live-bound contract between root and nested collections.

Definitions-module imports retain contracts with USE "rules.gs" AS Rules or USE "rules.gs" EXPOSING (LineRules). Use Rules.LineRules with a namespace. Selected contracts keep their private includes and transitive definitions. Bounds close over immutable library values using hygienic compiler substitution; a same-named caller value cannot change their meaning. LET and lambda bindings retain their scope, and value substitution leaves builtin call names intact. Literal library bounds may also be reused by private View records and rows. Unused imported definitions create no executable bound cells.

Open model-input captures, cell-address/external-qualified captures, cycles, missing includes and collisions fail admission. Importing a model owner with mandatory rules remains refused. Parameterize a dynamic model bound in a caller-owned contract that includes the shared rules. Contract names allow at most 32 namespace segments and 512 bytes when qualified; imported value closure uses the existing syntax budgets plus a shared 4,096-visit/8 MiB module budget. See the imported contract example and its library.

A .grid View library may declare and export RECORD CONTRACT alongside its Elements. Its private drafts resolve contract names in the defining library, including nested library dependencies; a caller's same-named contract cannot replace them. USE "rules.gs" AS Shared inside a View library imports reusable contracts with their immutable bounds, without exposing module values or callables. That path resolves relative to the defining library. Imported names remain private dependencies unless the library declares a public wrapper contract with INCLUDE Shared.Rule. Callers can apply that exported wrapper to model RECORD/ROWS owners or their own private drafts. See the View-library example and its component library.

Grid source component packages retain contracts used by private RECORD/ROWS owners, including transitive includes and closed imported bounds. Rules receive package-owned names that change with the imported component, so a same-named receiving rule cannot replace them. Re-export keeps the closure once; unused rules are excluded. Exact keys, messages, clause kinds and embedded payloads retain their meaning. Captured model bounds, missing closure and occupied helper names refuse the import. See the packaged source example.

Shared-contract editing in Properties remains under development. The standalone field-rule editor excludes shared definitions, and source rename refuses references whose shared-contract meaning it cannot yet prove. Edit the shared definition in Code to change all its applications.

The reusable record example combines a live bound with repeated nested addresses.

Private record applications

Inside a View or an element, the same application syntax targets a private record local in that lexical scope:

RECORD CONTRACT AddressRules
  FIELD city LIKE "_%" MESSAGE "Enter a city"
END

SURFACE Addresses
  local Draft = {city: "London"}
  VALIDATE RECORD Draft USING AddressRules
  edit Draft AS "Address"
END

Private applications accept literal operands, including literal lists. A rule that reads a model binding or calculates a bound is refused; those rules belong on model inputs. Applying a literal contract does not create model bound cells or send the private value to the host. A nested scope cannot add rules to a parent's local, and an imported element cannot capture a receiving model's contract definition.

Applications may precede their local declaration. Repeated components and repeated-row locals retain separate owners. Whole-local VALIDATE clauses still apply. Missing members and arrays used as records are refused; quoted keys stay exact. LIKE "_%" requires nonempty text, including refusing Blank. A simple <> "" rule alone allows other literal types that differ from empty text.

Rules are checked on direct local writes, action writes and Form submission. An invalid private draft prevents subsequent model writes or actions and stays available for correction. Field errors appear on the matching control; errors without a matching field remain at Form level. Private bounds have a 64 KiB literal-source limit; a View has an 8 MiB expanded-contract budget in addition to its ordinary document limit. Field rule lookup is indexed at View admission.

The private record example uses two independent component instances with the same nested rules.

VALIDATE ROWS Lines USING LineRules applies a contract to every row of a private list of records (or a one-row or one-column matrix of records), up to 65,536 rows. Each field rule is checked against each row, and a failure names the row and field path. The other rules above apply unchanged. The private row contract example validates two independent draft tables with one nested contract.