Sources
Docs: Panes, Plots, Policies, and Preferences. Web App: Select a Pane or Page and open Perception to compare its declaration with the read-only platform base.Schema
A Perception is a TypeScript.d.ts declaration exporting Perception and
conforming to BasePerception. It describes allowed values and references.
It does not select another actor’s saved data. A Perception is part of its Page
or Pane: it is saved and versioned with the rest of that Page or Pane, and it
is not a Piece of its own.
type (through BasePerception<T>), panes, plots and policies are required. Extra root fields and index signatures are refused. preferences, primitive, Live<> and Versioned<> are from before Pane types; each is a Warning & Error with what to write instead.
A Pane has one type, so a Pane that shows Meetings never keeps a chosen
window of its own. The window is a row of a saved-data Pane of its own, which
the Meetings Pane declares and reads with panes.window.preferences.get(id):
BasePerception<Type, DeclaredPlots> or a structured
plots field. The resolved policies contract cannot be replaced or narrowed.
Kinds of Pane
The type says which kind of Pane a Pane is:
A new kind of thing is a new Pane, and many things of one kind are rows. A
field becomes a bound Pane of its own only when it needs its own controls;
other fields use the design system’s default control for their type. Any Pane
can also own data of its own.
Bound Panes
A bound Pane shows one field of its parent’s row. The parent declares it inpanes with Bound, naming the field:
props.value, so it needs no read of its own, and it resolves once for each
Page. It uses its parent’s Policies and cannot narrow them yet, so its own
Policies stay at the defaults
(bound-policy).
Pane data
A Pane can own typed data that the Parable owns, such as an ontology, the descriptions of Provider tables, or the rules of a mapping. Declare it as a list of a named interface with anid field:
data.get(id) reads one
item, data.list({ limit, after }) reads one page of limit items after the
item after, and data.list() and the data.useItems() hook read every item.
The gets of one moment go to the server in one request, and each answer is
kept for the Version. A Pane it declares is read as panes.<name>.data.list();
and SQL reads parables.<parable>.<pane>_data.
Only a proposal changes Pane data. The Pane’s code calls
data.propose({ upsert, remove }), the reader confirms the change in a dialog
that the host draws, and the change goes into the reader’s set of Changes. It
reaches readers when the set publishes. The code of a Pane also proposes to
the data of each Pane that its Perception declares in panes, as
panes.<name>.data.propose({ upsert, remove }), typed by that Pane’s items.
So a parent edits the data of the Panes it shows. A Pane that it does not
declare is refused with PA-AU-071. propose.change(pane, { data }) is the
same call by handle or key. The Policy target data of the Pane whose data
changes decides who reads the data (view.data) and who may propose a new,
changed or removed item (create.data, edit.data, delete.data). A change
that also changes the Pane’s Policies needs edit.policies under the Policies
the Pane had. Its data is allowed when the Policies before the change or the
Policies that the change sets allow it. So one Release can give a Pane its
data grants and its first data, or remove both, and a copy takes that Release
in one Pull. The
proposal also needs the same right for every author of the calling Pane’s
code. Edits to
different items apply on the newest data, and two edits to the same item get
their own refusal, PA-AU-059.
Typed links
A field that points at another item is a typed reference,Ref<T>, imported
from parable/foundation. On the wire it is the item’s id. TypeScript refuses
a link of the wrong type, a save checks that each linked item exists and that
the saver can see it, and the row conversion checks the links of each row it
converts. Ref<T> names a
named interface with an id field, declared in this Perception or imported
from another Pane’s Perception. A link that is text, such as
parent: "growth", is not checked; Prepare names such a field in Pane data
(use-ref).
A Pane that only serves other Panes, such as shared state, a kit of helpers or
a preview, says so with @plumbing in the doc comment on its Perception.
The tag is stored in the Pane’s manifest (plumbing: true), and Plotted Path
never offers such a Pane as a part to open
(Writing Panes that explain well).
@plottedPath publish or @plottedPath report in the same comment asks the
platform to write the Pane’s Plotted Path ahead of its first reader, as each
Version is published or as each Report run completes. It is stored as
plottedPath in the manifest
(Write it ahead).
Scalar-lib types
Every field of a Pane’s type and its data is a scalar-lib type, the types of the Provider Pool, imported from@psgen/scalar-lib, a Ref<T>, or a named
interface of these:
A plain
string, number or boolean is not a scalar-lib type, and neither
is a JavaScript Date: an instant is a Temporal.DateTime, an ISO 8601
string. A literal union such as "7d" | "30d" is text with fixed values. A
type the platform declares, such as ParticipantGrant or ProviderRef from
parable/foundation, is accepted as it is written.
A column that holds a type’s values in another width or encoding is cast to it
where the Plot returns: every integer width, REAL and DECIMAL to a number
field, DATE64 to a Temporal.Date, a time of day to a Temporal.Time, a
TIMESTAMP of any unit or zone to a Temporal.DateTime in UTC, and a
dictionary-encoded string to text. Only a value of another kind, such as text
in a number field, is PA-AU-047, and a day or an instant held as text needs a
CAST in the SQL.
A value with several fields is a named interface of these types, declared in
the Perception. An array holds one scalar-lib type or one named interface, and
an array never holds arrays. There is no inline object type, no tuple and no
union of different types. Raw JSON is not a type: Generic.JSON, unknown,
any and Readonly<Record<string, T>> are refused
(no-raw-json).
Items keyed by text are rows of a Pane, or a list of a named interface with an
id field. Time is
always a Temporal.Date, Temporal.DateTime or Temporal.Time, never
Generic.Text or a number: published_at is a Temporal.DateTime and day a
Temporal.Date. A span is a named interface with a field for each end, such as
started_at and ended_at, or first_day and last_day.
PA-AU-051, naming the Pane, the field and what to write instead:
“The Perception’s type.repository is a plain string. Give it a scalar-lib
type: Generic.Text, or the one it is, such as Identity.UUID or Contact.Email.”
A time is judged by the field’s name. A text field such as day, week,
published_at or starts_on holds a time. A number field holds a time only
when its name ends in at, date, timestamp or epoch, such as createdAt
or sent_timestamp. A count of days, a duration in minutes, an amount per day
(peak_day) and an amount to date (completed_quarter_to_date) stay numbers.
A label such as period with values "current" | "previous" is not a time. A
refused save stores nothing, so the Pane’s Warnings & Errors cannot show the
refused field. A field the published Version already holds is history: it shows as info in the
Pane’s Warnings & Errors, and it never stops a read, a draw, or another change
from saving and publishing.
Do not import Text, Number, YesNo, Empty, List, Day, Moment,
Time, Duration or Map from parable/foundation. Write the scalar-lib
names instead. Each of these names a Perception imports is a PA-AU-051 finding that says what to
write: “The Perception’s import of Empty from parable/foundation is a retired
friendly name. Write null, as in X | null.”
A failed Preferences save rejects the Pane’s request and shows the reason in
the Parable’s window, next to the control that asked. Invalid nested input
names the field that could not be sent.
Providers a Parable reads
A Parable names the Providers it reads once, on its Home Page’s Perception, as named groups. Every Parable uses the same structure, and a Provider can be in several groups:
Write every value in quotes: the compiler reads the declaration from its
types. A save refuses a group whose fields it cannot read, a name that is
not a catalog name, a Provider listed twice in one group, and
providers on
any Page or Pane but the Home Page. Prepare refuses a Provider the catalog
does not have, naming it and the groups that name it; a Provider the
published Version already names is not judged again.
What a Platform Parable gives
A Platform Parable can give its own Pieces to named places in the product, such as a tab beside every Parable. Its Home Page’s Perception declares each gift once, as a named entry incontributes. Each entry names its place and
the Pieces that it gives, by handle:
Write every value in quotes: the compiler reads the declaration from its
types. A save refuses each of these, with a sentence that names the entry:
- a place that is not in the table, or a field that its place does not take;
- a field that its place needs and that the entry leaves out;
- a Page, Plot, Pane or Report that the Parable does not have, by its handle;
- a second
ponder.taborpublish.checksentry; contributeson any Page or Pane but the Home Page.
contributes on a Parable that is
not a Platform Parable is refused (PA-AU-085). A save that deletes or
renames a Piece that the declaration names is refused too, so a Release never
names a Piece that it does not have. The declaration is stored with each
Release, and the product reads it from the Release that each Deployment runs,
never from a Patch. A Platform Parable is never forked, so every copy runs the
Releases that Parable staff publish
(Forking).
The Parable’s tab shows the ponder.tab Page, and that Page knows the
Parable on screen beside it
(The subject). A Parable
that declares no ponder.tab keeps its Home Page as its tab. Later releases
wait for the publish.checks Plot at Publish and run the react Report.
Named types and their Providers
A named type is vendor neutral: a Document is a Document whether it comes from Google Docs or Confluence. No type is one Provider’s version of another. A Provider feeds a type through a mapping Pane: its Perception imports the type from its own Pane and uses it as its type, names the Provider with@provider, and its undeclared Plot returns that Provider’s rows of the
type. The type’s Pane owns an undeclared Plot that combines its mapping Panes
by name.
provider_refs, a list
of ProviderRef from parable/foundation: provider, the Provider’s catalog
name as in Plot SQL, and provider_id, the record’s ID in that Provider, as
text. A record from one Provider has one entry; a record merged from several,
such as one Meeting from Google Calendar and Zoom, has one per source record.
A type does not name Providers in its own fields, so it has no zoom_id.
A reference to a thing that can be one of several types is two fields: its ID
and its kind, such as subject_id and subject_kind, with the kind a literal
union of the type names. Any Plot can then resolve the reference by joining on
both: the kind picks the table and the ID the row. A Pane’s
_by_actor rows do the same
with actor_kind and actor_id. A link to items of one type is a Ref<T>.
A Plot reference is not a Pane reference. CSS is attached by the renderer and
is not a Palette configuration in the Perception declaration.
A Page’s Perception is BasePerception<null>, and every Page is composition
only: its code may import only the Panes its Perception declares and the runtime
layout modules. The compiler refuses any other import, such as a chart or
control library, and names the import it refused. The rule is the Page’s alone:
a Pane whose type is null holds no value and owns no Plot, but can
still import the shared controls and read the Plots of the Panes it declares.
Export interfaces and type aliases from a Perception to reuse them in another
Pane’s React source. Use import type from the host declaration module
parable/panes/<Pane ID>/perception, using its stable Pane identity. Type-only
imports do not mount the target Pane or grant access to its settings or
runtime API. Runtime Pane imports still require a declared panes reference.
Read a named type at runtime
TypeScript types disappear when a Pane’s code compiles, so a type-only import types the code but cannot show what a named type is. To draw one on screen, such as a Parable’s ontology page listing each type’s fields, read it at runtime. The compiler emits it from the declarations themselves, so it cannot drift from the types the Plots are checked against.- A Pane reads the named type it shows, its type’s, as
namedTypefrom its module:import { namedType } from '@parable/<parable>/<pane>'. - It reads the one a Pane it declares shows as
panes.<name>.namedType, from thepanesprop or the module’spanes. - It follows a key to any other named type through the module’s
namedTypes: the types a named type extends, the named type a field holds, and the type that declares a field.
namedTypes holds each named type once, by key, so a parent many types extend
is not repeated for each. A key is the declaring Pane’s key, or the platform module, then # and
the name: <Pane key>#Meeting, parable/foundation#ProviderRef.
namedType is null when the type is null or is not one named type:
an inline object, a scalar, a list, or a union of several. It is also null
when the reader may not view the Perception that declares the type: a named
type is part of its Perception, so a reader without view permission on that
Perception receives neither the type nor the fields it gives another type.
Otherwise a named type is:
namedType shows and never makes a stored Refresh
unreadable.
Declare a Plot
A Plot Pane’stype is its kind: a named interface, in
scalar-lib types, of the rows its undeclared Plot
returns. The one Plot that the Pane owns and does not declare returns rows of
that type, picked to the columns its SQL returns, with no parameters. A Pane
may own several Plots, and each other Plot is declared. A Plot that takes
parameters, or returns rows of another shape, is declared: pass it as the
second type argument of BasePerception, under the handle of the Plot the Pane
owns, with an array of the kind as its rows:
plots.openIssuesByPriority() and the Plot editor’s Preview runs. Without
it the Pane has no such function and Preview reports that the Plot’s contract
is unavailable.
PlotContractcomes fromparable/foundation. A bare row type, a function type, or an import from any other module is refused.- The type takes null,
OpenIssues | null, so the Pane draws on a Page, which hands it no value.T | nullis not anulltype. - A Plot that the Perception does not declare returns rows of the Pane’s
type, so a Pane has at most one such Plot, and then its type is not
null. A save, Prepare or Publish that adds a second undeclared Plot, or an undeclared Plot onBasePerception<null, { ... }>, is refused withPA-AU-049, which names the Pane and those Plots. A Pane whose Plots are all declared may have anulltype. - The name must match the handle of a Plot this Pane owns when the declaration is first saved. Save the Plot before the Perception or in the same save; a name with no owned Plot is refused as “does not bind to an owned Plot”. Renaming the Plot later keeps the binding. A Page cannot declare Plots.
- The first argument is the tuple of positional SQL parameters:
readonly []for none, or named members such asreadonly [since: Generic.Text, limit: Generic.Int64]. - The second argument is the array of rows of the kind, which has one field for every column the SQL selects, under the exact column name, and no others.
string, number or boolean is PA-AU-051:
Cast a decimal, or an identifier wider than a safe integer, to
DOUBLE or
VARCHAR in the SQL so the planned type stays fixed. Plots
lists the row shapes SQL output supports.
When a save is refused, the response names the Pane and the line the compiler
rejected. Ponder receives the same lines for the declarations it submitted and
corrects them in its next step.
Storage
The declaration is authored source. Its Page or Pane stores it together with its compiled manifest, and versions both with the rest of its definition. Saving checks the declaration with the TypeScript compiler and derives value validation, typed links and reference bindings. The manifest records a hash of the type; each saved row records the hash it was checked against, and a Release may only grow the type of a Pane that holds rows: add optional fields or widen a union (shape-grow). The manifest cannot be edited on its own to bypass the declaration: a save may omit it or send back the stored value, and the server compiles it from the source. The editor presents readable handles. Stored references use stable identities, so renaming a Pane does not redirect existing references to a new Pane that later takes the old name.Security
Types describe what a value can contain; Policies decide which actions the requester may perform.BasePerception.policies and React’s props.policies
contain the resolved Policy decisions (ResolvedPolicies in parable/foundation). The Policies themselves
are a field of the Page or Pane, shown in its Policies tab.
The host always supplies the required props. A component may ignore props it
does not use, but that does not remove the Pane’s schema or access checks.
A reader does not need view permission on the Perception to render an allowed
value.
Stability
Invalid references, missing base fields, and attempts to replace the fixed contract are refused. Repair the declaration before saving; a preview error does not itself grant or remove source-inspection permission.Temporal.Date is a validated calendar-day string. Temporal.DateTime is an
ISO 8601 string. Branded timestamp strings retain their supported fractional precision.
@psgen/scalar-lib supplies the same scalar parsers used by the platform.
A published Version can stop compiling without changing, when a platform
release narrows the types a Perception may use. Its published Plots then cannot
be read until the Parable publishes a Version that compiles, or is pocketed.
The published Plots of every other Parable in the Workspace stay readable.
Each compile type-checks the declaration against the ES2023, DOM and
DOM.Iterable libraries.