Skip to main content

Sources

Docs: Panes, Perceptions, Preferences, Plots, and Policies. Web App: Each Piece’s editor lists its Warnings & Errors under the code, and the Publish action stays off until none is left.

Structure

Warnings & Errors are what the checks find in a Piece: the compile, the type check, the Code check (React rules and classes that nothing styles), a Plot’s SQL check, and the Parable rules on this page. Every Parable rule is an error. Each finding names its rule, such as parable(versioned-removed), says what is wrong and what to write instead, and links to its section below. The rules keep every Parable on one model:
  • A Pane has one type. The type is what the Pane shows and what people save on it.
  • People save values as rows of that type, one row for each item and each person, team, Workspace or default.
  • Values that the Parable owns are the Pane’s own data, and only a proposal changes them.
  • Links between items are typed references, and the platform checks them.

When the rules run

  • Save. A save is refused when it adds a finding. A Parable that still holds an older pattern keeps saving each step that removes it. An archive import is a save: it is refused for a finding it adds, with the same message.
  • Prepare and Publish. They are refused while any finding is left, even one that the last Release already held. A small change also removes every older pattern.
  • Reading. Opening a Piece shows the findings of the tree it reads. The server judges each tree once and keeps the findings, so a read of the same tree does not run the checks again.
The server refuses the same things at run time, so the checks and the server never disagree: a value above 64 KB, raw JSON, a link to an item that does not exist, a row whose id differs from its value’s id, and a customer’s write of a Platform Parable’s defaults, data or Pieces.

Perception rules

These rules read a Page’s or Pane’s Perception.

versioned-removed

Finds: Versioned<T> or Versioned<PlotSql> in a Perception. Why: a Pane could change the Parable itself by saving a value, outside the review that every other change gets. A Pane that uses Versioned<> gets only this finding, so two rules never send you two ways. Write instead:
  • If the Parable owns the value, such as an ontology or a list of Provider descriptions, declare it as the Pane’s data and change it with propose.change(...).
  • If each person or Workspace sets the value, make it rows of a saved-data Pane.
  • For Plot SQL, propose a change to the Plot.

live-wrapper

Finds: Live<T> in a Perception. Why: every Preference is Live now, so the wrapper says nothing. Write instead: remove the Live<> wrapper.

one-type

Finds: a preferences field in a Perception, also readonly preferences: null. Why: a Pane saves rows of its own type. A second type for saved values splits one thing into two shapes that nothing keeps in step. Write instead:
  • For readonly preferences: null, remove the field and add | null to the Pane’s type, as in BasePerception<Meeting | null>. The field let the Pane draw with no value from its parent, and | null keeps that. The Plots that the Pane owns still return rows of its type.
  • If preferences is the same type as the Pane’s type, remove the field.
  • If the Parable owns the saved value, declare it as the Pane’s data.
  • If each person or Workspace sets it, move it into its own saved-data Pane, and read that Pane with panes.<name>.preferences.get(id).

no-raw-json

Finds: a field of a Pane’s type, or of its data, that is raw JSON: Generic.JSON, unknown, any, or Readonly<Record<string, T>>. Why: nothing can check raw JSON, Plots cannot read it as columns, and a change of its shape breaks saved values without a warning. Write instead: give the field a real type: a scalar-lib type, a Ref<T>, a named interface, or a list of those. If it holds many items, make them rows of a Pane, keyed by an id field.

bound-field

Finds: a bound Pane whose field is missing from its parent’s type, has a different type, or is bound twice. Why: a bound Pane takes one field of its parent’s row as its value (props.value). The field must exist, with the bound Pane’s type, and feed one Pane. Write instead: bind the Pane to a field that the parent’s type has, with the same type, once:

bound-once

Finds: a Pane that two Panes bind, each with Bound<>. Why: a bound Pane uses the Policies of the one Pane that binds it. Two parents would give it two sets of Policies, and a reader that one parent refuses could read the field through the other. Write instead: bind the Pane from one Pane only. If two Panes need the same kind of field, give each parent a bound Pane of its own.

bound-policy

Finds: a bound Pane whose Policies differ from the defaults that a new Pane gets. Why: a bound Pane uses its parent’s Policies and never reads its own. Policies of its own would look like protection that does not apply. A bound Pane cannot narrow its parent’s Policies yet. Write instead: reset the bound Pane’s Policies to the defaults, and set who may see and change the field on the parent Pane.

primitive-renamed

Finds: a Perception that still declares readonly primitive: .... Why: the Perception field is now type. extends BasePerception<T> already declares type: T. Write instead: remove the primitive line, and put the type in BasePerception<T>.

Policy rules

setting-item-id

Finds: a Policy that reads a row by a legacy handle: fromSetting: { paneKey, handle } or enabledFromSetting: { paneKey, handle }. Why: Typed Panes name each row by its item id. The row conversion keeps a single value’s handle as its item id, but a value that held a list of items becomes one row for each item, so the handle names no row any more. The Policy would fall back to its own grant without a word. Write instead: name the row by its item id:

Pane code rules

The Code check runs these rules on a Page’s or Pane’s code.

values-api

Finds: a read of preferences.values, preferences.mode or preferences.plotSql, on the Pane’s own module or on a module of a Pane it declares. Why: preferences.values saved one whole value under a handle, outside the Pane’s type, and the modes are gone. Write instead:
  • To show a value, use preferences.get(id), or the preferences.useItem(id) hook. The value is already layered for the reader.
  • To edit one actor’s value, read it with preferences.get(id, { for }) and save it with preferences.save({ id, value, for }), where for is 'me', 'group:<id>', 'workspace' or 'default'.
  • To change a Plot’s SQL, propose it with propose.change(plot, { sql }, { expectedRevision }).

data-in-code

Finds: one literal of data above 8 KB in a Page’s or Pane’s code, such as a long list of objects or a long string. Why: the checks cannot see data in code, and nobody can change or customize it without changing the code. Write instead: move the data into the Pane’s data with a proposal, or into Preference rows if each actor needs a copy of their own.

Plot SQL rules

These rules read a Plot’s SQL.

typed-read

Finds: Plot SQL that reads workspace.preferences. Why: workspace.preferences holds every Pane’s saved values as raw JSON, with no type. It is only for looking at values in the SQL console. Write instead: read the Pane by name.
  • parables.<parable>.<pane> returns typed rows of the Pane’s type, layered for the reader.
  • parables.<parable>.<pane>_by_actor returns each actor’s own rows, with actor_kind and actor_id, for example every team’s.
  • parables.<parable>.<pane>_data returns the Pane’s own data.

filters-in-comments

Finds: a SQL comment that starts with -- Filter:. Why: nothing checks a filter that is written in a comment, and a Policy cannot read it. Write instead: make each filter a typed row of a saved-data Pane, and read it in the SQL by name. If a Policy reads these rows, let only people who may edit Policies write them.

Data and Release rules

use-ref

Finds: a text field of a Pane’s data whose values are all ids of one Pane’s items. Why: a link that is text is not checked. A rename or a delete leaves it pointing at nothing. Write instead: make the field a typed reference, such as Ref<Concept>. A save checks that each linked item exists and that the saver can see it. The one-time row conversion checks the links of each row it converts, and fails a row whose link finds no item. This rule reads Pane data only. A text link between Preference rows is not found by it. Make such a field a Ref<T> when you rewrite the Pane, so a save and the conversion check it.

data-grants

Finds: a Pane that holds data, and whose Policies name no view.data grant. Why: a missing grant admits nobody, so nobody can read the data. When the Policies name no data grant at all, nobody can change the data either. A Pane that was converted before Convert a Versioned Pane copied the grants holds its data under such Policies. Write instead: add the data grants to the Pane’s Policies. To keep the access that the Pane’s values had before they became data, give each data grant the same grant as preferences has for that action:
Changing Policies needs edit.policies. Prepare, Publish and the Warnings & Errors view run this rule. A save does not, so a set of Changes can hold the steps that add the grants. A bound Pane uses its parent’s Policies, so this rule does not judge it.

shape-grow

Finds: a change to the type of a Pane that holds saved rows, compared with the line’s last Release, that removes, renames, retypes or narrows a field, or adds a required one. Why: saved rows were checked against the old type. A Release may only add optional fields or widen a union, so every saved row still fits. Write instead: keep the old field and add a new optional one. A union may take a new member, and a field may become optional or take | null.

saved-data-plot

Finds: a Pane that owns a Plot and has saved rows, and that does not declare overrides: true:
  • a Pane that declares preload, so it shows the rows that people save, and also owns a Plot;
  • a Pane that was a saved-data Pane in the line’s last Release and that now owns a Plot.
Why: a Pane that owns a Plot is a Plot Pane. It shows the Plot’s rows and takes no saved rows. Without this rule, a Plot that you add to a saved-data Pane makes it a Plot Pane with no warning, and every save of a row on it is refused (PA-AU-040). Write instead: move the Plot to a Pane of its own, and read that Pane’s Plot from the saved-data Pane by name. If people save rows over the Plot’s rows, declare overrides: true in the Pane’s Perception instead. The Warnings & Errors view, a save (an archive import too), Prepare and Publish run the first check. Only Prepare and Publish run the second, because it compares with the last Release.