Sources
Docs: Panes, Perceptions, Policies, Plots, Warnings & Errors, and Workspace Pool. Web App: A Pane’s own controls save a value when you choose to keep it, such as a category, a date range or a judgment, and show it again when the Page reloads. Other people see a shared change at once, without a reload.Structure
A Preference is a value that people save on a Pane: a category, a date range, a judgment, an audience, a switch. Preferences keep the choices people make while they use a Parable, let them change, and keep their history, without a change to the Parable itself. Every Preference is Live: a save applies at once, is not part of a Version, and never publishes or opens a set of Changes. A Pane has one type, declared in its Perception: what the Pane shows and what people save on it. Preferences are rows of that type. The type’sid
field, Generic.Text or Identity.UUID, names each item, such as the
category m-customer-care, and Parable stores it byte for byte. Parable keeps
one row for each item and each actor.
removed: true.
Actors
Every row belongs to one actor. The product names each actor the same way wherever one is shown or chosen:
Each Workspace’s copy of a Parable has its own This Parable rows, so the
actor reads This Parable, not Workspace. A team names its group by the id that
a Plot of the Parable gives it, in canonical form. Parable keeps no list of
teams: a Pane offers the teams that its own Plot reads, one section per level,
named as the Parable names the level. A Default row never names people,
Roles, teams or Workspaces, and every copy of the Parable gets the Default
rows with each Release.
Layers
A read merges the rows of an item field by field, narrowest first:- Me.
- A team, only when the read names it:
preferences.get(id, { group }). - This Parable.
- Default.
- The default in the Pane’s code, for fields that no row sets.
Fixed defaults
A Pane can declarereadonly fixedDefaults: true in its Perception. Then no
Me, team or This Parable row can change a Default row:
- A save of a Me, team or This Parable row with the id of a Default row is
refused: “The Parable’s Default sets this row, and no other row can change
it. Add a new row instead.” (
PA-AU-094). - A row with that id that was saved before the Default row arrived, or before
the Pane declared
fixedDefaults, stays stored, but it changes no read. The Pane, Plot SQL, a Refresh and Ponder read the Default row as it is, and a removed row does not hide it. Its actor can still delete it, and_by_actorstill shows it as that actor’s own row. - People still add their own rows with new ids.
id field can declare it. For example, a Pane
of Proofs keeps Default Proofs that run in every Workspace: a Workspace or a
person adds Proofs, and never changes or turns off a Default Proof.
Rows about a Parable
A Pane can hold rows about another Parable, such as reviews of a Patch. Give the Pane’s type one top-level field of the typeParable.Subject. Each row
names, in that field, the Parable that it is about: a Patch at a revision, a
Release, a Refresh, or one Piece at one of those
(Field types). A field of the type
Identity.Signer holds the person who signed the row.
- Who reads a row. A row shows only to a person who can read the Parable that it is about, with the rule of a Platform Parable’s tab. For a row about one Piece, the person’s Pieces tree must also list that Piece. Peeking lists Pages only. Every read applies this rule: the Pane, its first load, the row list, the item’s history, Plot SQL and Ponder’s tools. Ordinary SQL reads no row about a subject. A Plot must be bound to that subject to read its rows.
- Which rows a Pane reads. In a Platform Parable’s tab, the Pane reads the rows about the Parable beside the tab and about its Pieces. Outside a tab, the Pane reads no rows of this kind.
- Changing the subject. When you navigate to another composed Parable, Patch, Release, Refresh or Piece, the tab clears its previous reviews and reads the current subject’s reviews. A response from the previous subject cannot replace them, even when you return to that subject.
- Who saves a row. The Pane’s Policies decide who saves, as for every row.
The person must also read the Parable that the row is about: “You cannot read
the Parable that this row is about.” (
PA-AU-095). - Who signs a row. The server sets the signer of a Me, team or This Parable
row to the person who first saves it. Leave the field out. A value that names
another person is refused (
PA-AU-096). Default rows have no signer. - Who changes a row. Only the person who signed a row changes or removes
it: “Only the person who signed this row can change it.” (
PA-AU-096). This holds for Primary on the Parable too. After the Patch is published, Pocketed or closed, or after a newer Release or Refresh replaces the one that the row is about, Primary on the Parable’s Deployment can remove the row. Its history keeps the signer.
Identity.UUID ids, so that an id never
says which Parable a row is about: live notices carry ids. It takes no Default
rows, and it cannot share its rows across Workspaces. The Perception check
refuses a text id, a second Parable.Subject field, a field below the top
level, and acrossWorkspaces on such a Pane. In Pane code, subjectKey()
gives the Parable beside the tab, and subjectKey({ piece: true }) gives the
Piece in focus there. For a Patch, the key names the Changes revision sealed
by Propose, not its observed working revision. It stays the same after a clean
move onto a newer Release. A Pending Patch without retained Changes has no
key for a new review. Earlier signed rows keep their original addresses.
A Plot of the tab reads the rows about the Parable
beside the tab that its reader can read, and a read with no subject reads none
(Parable Pool).
A Refresh reads none of these rows, and workspace.preferences holds none.
A retained Refresh result is withheld if its captured Preferences include a
private row, a row from a Subject Pane, or a row whose privacy cannot be
checked. This applies to the selected Release, the current Pane, and the
accepted retained definition of a removed Pane. The saved Preferences and
result stay unchanged. A result computed from private inputs cannot be made
safe by hiding individual output rows, because those inputs may affect a total.
Removing an ordinary Page or Pane does not by itself hide its retained
Refresh results. A later ordinary Preference Policy denial still limits raw
captured Preferences for that person. It does not alone revoke a stored Plot
result whose existing output and recorded-source Policies permit that person.
A Refresh with no captured Preferences keeps those existing result checks.
Rows for named people
A Pane can hold rows that are for named people, such as Pings. Give the Pane’s type one top-level list of the typeIdentity.Recipient, and a field of the
type Identity.Signer. Each row names, in the list, the people that it is for
(Field types).
- Who reads a row. A row shows only to the person who signed it and the
people it names. No other person reads it: not Primary, not Parable staff,
and not View as. Every read applies this rule: the Pane, its first load,
the row list, the item’s history, a Ref from another row, live updates and
Ponder’s tools. SQL reads none of these rows:
workspace.preferences, Plot SQL and Report runs leave out every row of such a Pane. A row keeps this rule after a later Version removes the field. - Who saves a row. The Pane’s Policies decide who saves, as for every
row. Each row is a This Parable row. A Me, team or Default row is refused
(
PA-AU-065). - Who a row names. A row names 1 to 100 people (
PA-AU-101). Each person must be an active member of the Workspace: “This row names a person who is not an active member of this Workspace.” (PA-AU-100). - Who signs and changes a row. The server signs the row with the person
who first saves it, as on a Pane of rows about a Parable. Only that person
changes or removes the row (
PA-AU-096). - How many rows. One actor keeps at most 100,000 rows on such a Pane, because every person of the Workspace saves This Parable rows there.
Identity.Recipient field, a field
below the top level or one that is not a list, a type with no
Identity.Signer field, and a type that also has a Parable.Subject field.
What Preferences are not
Values that the Parable itself owns, such as an ontology or the descriptions of Provider tables, are the Pane’s own data, declared asdata in its Perception.
Pane data travels with each Release, and only a proposal changes it. The app
itself — its Pages, Panes, Perceptions, Policies, Palettes, Plots and Pane
data — is versioned with the Parable. A Preference never holds React, CSS or
SQL, and never changes the text of a Policy, though a Policy can read one
(Security).
Services
In Pane code
A Pane reads and saves through its generated module’spreferences. The
binding belongs to the calling Pane; a Pane it declares is read as
panes.<name>.preferences, under that Pane’s Policies. Rendering and reading
never create a row.
preload gets its rows with its load: 'all'
for a bounded set, such as categories, so its reads send no request, or
'page' for rows that grow, such as judgments, so the load carries one page.
Without preload, rows load when the code reads them, and the ids that one
moment asks for go to the server together.
A save takes expectedRevision. A positive number replaces the row at that
revision, and the save fails with PA-AU-059 when the row is missing or has
another revision. 0 or no number saves on the newest row. Only two edits to
the same item conflict: read the item again, show both values, and save again
without losing the draft. Saves to different items never conflict.
preferences.values, preferences.mode and preferences.plotSql are
removed. The Code check refuses them
(values-api).
In Ponder
The Workspace API has these operations, and Ponder can use each as a tool:
A save names the Pane, the actor, and the rows:
createOnly. true creates the row and fails
with PA-AU-059 when the actor already has one. The API reads 0 and null
alike, so create-only is its own field.
It returns each row’s id and new revision, not its value. Ponder asks you
before it saves a Default row or a row that a Policy reads, and names the
change. In a shared Problem, a tool result never shows another person’s own
rows.
Live updates
A save sends every reader that the Pane admits a short notice with no value, and each open Page reads the changed rows and updates them in place, without a reload. Only the Plots that read the changed Pane by name, or read a Plot that does, run again. A person’s own save reaches only that person. On a Pane of rows about a Parable, the notice holds only the row’s random id, and each open Page reads the row again with the rule of Rows about a Parable.Storage
Each row records the item id, the actor, the value, a revision, and the hash of the type that checked it. A row is at most 64 KB, and one actor can keep at most 10,000 rows on one Pane. Every write keeps the earlier value in the item’s history. A Pane of rows about a Parable has other caps. One actor keeps at most 100,000 rows on it. One Parable that rows are about has at most 1,000 rows there: “This Patch already has 1,000 rows here.” (PA-AU-097). One person signs at most 100
rows about one Parable: “You already saved 100 rows about this Patch.”
(PA-AU-091). The counts include rows about Patches that are closed.
The server checks every value against the Pane’s type as a partial value when
it is saved. That type comes from the line’s current definition: the newest
Release on a published Parable, and the Parable as it stands on a Personal
Parable. It never comes from a set of Changes or from the Release that the
reader views. So a Pane that exists only in an open Patch takes no rows until
that Patch is published. A save, a removal or an Undo of its rows is refused
with HTTP 409 (PA-AU-005) and this sentence: “This Pane is new in a Patch
that is not published yet. You can change its Preferences once that Patch is
published.” Only a person whose seat on the Parable opens that Patch gets
this sentence. A person with no seat gets not found (HTTP 404, PA-AU-001),
as for a Pane that no open Patch holds. The server
refuses a value that does not fit (PA-AU-040), raw JSON (PA-AU-075), a row
whose id differs from the value’s id (PA-AU-062), a row above 64 KB (PA-AU-060), and a row past the
cap (PA-AU-061). A Release may only grow a Pane’s type, by optional fields or
a wider union, so every saved row still fits
(shape-grow).
A Personal Parable has no Release, so its save checks the rows instead. A
save that changes a Pane’s type, or gives a Pane with saved rows a Plot, is
refused only when a saved row would stop fitting (PA-AU-073). The refusal
names the Pane and up to 10 of its rows with the reason for each. It counts
the rows of other people, signed rows, rows about a subject and rows for
named people, without naming them.
It gives the steps that work: keep the old field and add a new optional one,
or first make the saver’s own rows fit the new type. The saver cannot change
the rows of other people. Ponder gets these steps without the rows
(Perceptions). A change that every saved row
still fits saves at once.
A setting saved before its Pane’s type changed in another way can stop
fitting. A typed Pane reads each part that no longer fits as not set and
keeps the parts that fit. A missing required part at the top level is not a
misfit. A nested object that is set must still include its required parts.
A top-level part that the viewed type does not declare is not a misfit
either. A newer Release can add an optional part, so an older Release reads
that part as it was saved, with no notice. A save from the older Release
keeps the part when the Pane code passes it back. Inside a nested object, a
part that the viewed type does not declare still makes that object a misfit.
For a part that no longer fits, the window says
A saved setting no longer fits, with the Pane’s name, once per Pane per
page. For a merged item, the notice names no layer and
offers no action. A read of a named row says whose setting it was, including
The Default setting for a Default row. When that row is your own setting
(Me), the notice has Remove. History reads omit parts that no longer fit
without a notice. These reads do not change the saved setting. A legacy Pane
keeps its existing whole-setting check and notice for each saved revision.
If Pane code saves a row again, the save replaces that row and removes the
parts that no longer fit, because the Pane did not receive them. An older
Release then no longer reads those parts from that row.
Remove deletes the setting at the revision the read saw, and the window says
Removed your saved setting. The Pane can then save a new setting under
that id. If the delete is refused, for example because the setting changed
since the read, the window says Could not remove your saved setting and
the reason. A shared setting’s notice has no Remove. For a team or This
Parable setting, a person with Primary, Publishing, Pinging or Peeking whom
the Pane’s delete Preferences Policy allows can delete it, or ask Ponder
to. Only Primary may change a Default setting.
Rows are not part of a Version: publishing does not copy, reset or roll them
back, and an older Version reads today’s rows under today’s Policies. The one
exception is Default: each Release stores the line’s Default rows with it.
When a Workspace copy takes a Release, the copy’s Default rows become the ones
that Release stores. A Delivery can apply itself. Otherwise, a person with
Publishing or Primary on the copy Pulls the Release and Publishes it.
A Pull alone does not change the Default rows.
The server’s read of rows checks only the reader’s access, with the Policies
of the line’s current definition, and never checks a row’s shape. A Pane that
the current definition does not hold shows its rows to no reader, in any
Release.
A Plot reads a Pane’s rows by name:
NULL). The other fields and rows still show. This includes a choice that
the type no longer lists. A top-level field that a saved row leaves out reads
as not set without a warning, even when the type requires it. Nested required-field checks are not active yet.
Pane data items must fit as whole values. A read leaves out an item with a
value that does not fit these checks, and returns the other items. This includes a missing
top-level required field that does not allow null.
Open Plot shows warnings in the Results caption in Ponder. Each warning
names the Pane relation and field. A warning for an omitted Pane data item
also names its id. No warning shows the stored value. The read changes
nothing that is saved.
These checks cover scalar reads, listed plain-text choices, and top-level required fields
that do not allow null. Choice limits on numbers or true/false values, and the
presence requirement for a field that allows null, are not checked. Those
constraints keep today’s reading behavior.
A Report run keeps the moment it started, and its Plots read the rows as they
were then, layered with no reader: an old run reads the same rows later. A run
reads a Pane’s rows only when the Pane’s view.preferences grant admits the
person who runs it (for a scheduled run, the person who holds Primary on the line), and a
Pane’s data only when view.data admits them. A person who reads a Plot as of
a stored run reads only the Panes that their own grants admit. When
a row that a Pipeline read changes after the Refresh that made its result
started, the result says These settings changed since the last run and
offers Preview and Run. workspace.preferences holds every Pane’s
rows as raw JSON for the SQL console; a Plot never reads it
(typed-read).
Values that Panes saved before Preferences had one type are converted once,
when the line’s first Version on this model publishes: a whole value that held
a list becomes one row for each item, and every other row takes its value’s
id, else its old handle, as its item id. A value saved alone, such as a
switch saved as true, sets the type’s one field. A saved setting’s name and
icon go into the type’s name and icon fields when the type has them.
Prepare reports how many rows the
conversion converts, drops and fails, with the reason of each group. Publish
refuses a drop or a failure until the publisher accepts that report; a report
that changed after Prepare needs Prepare again. On a Personal Parable, which
has no Publish, the save that makes a Pane typed converts its rows, and a drop
or a failure refuses the save with each group and its reason.
IDs are Identity UUIDs in the canonical base62 form the API returns. Pass the
IDs that reads and saves return rather than a hyphenated UUID. A new item id
is a short, stable text id or newIdentityUUID() from @psgen/scalar-lib,
never crypto.randomUUID().
Security
Preferences need an active Workspace membership on a live account. A suspended, revoked or missing member, or a deleted account, reaches no rows, even with a seat on the Parable. A person’s own rows are theirs alone, in every read: the Pane, SQL,_by_actor, history, live notices and Ponder’s tools. The one exception is a
Pane that shares its rows across Workspaces (below): it shows the value of each
row, without who saved it. A person reads their
own rows only while the Pane’s view.preferences admits them, in every one of
those reads, workspace.preferences included. A shared row, a team’s,
This Parable’s or the Default, follows the Pane’s current Policies on the
line: view.preferences permits reading, create.preferences the first save,
edit.preferences a change, and delete.preferences a delete. These are
separate grants. A team’s and This Parable’s writes also pass the Pane’s edit
filters. Selecting an older Version does not restore its former grants. Under
View as, every save, undo and proposal is refused (PA-AU-044).
The server checks whose row a write names (PA-AU-065). Only Primary on the
line that owns the Parable writes a Default row. The Pane’s current
create.data, edit.data, or delete.data Policy must also allow that
action, because a Default row is the Parable’s own value for every reader,
like the Pane’s data (PA-AU-065). A Default row that names
people, Roles, teams or Workspaces is refused (PA-AU-066). On a Platform
Parable, a Workspace writes Me, team and This Parable rows only (PA-AU-067).
On a Pane that declares fixedDefaults, a Me, team or This Parable row with
the id of a Default row is refused (PA-AU-094,
Fixed defaults).
A row about a Parable shows only to a person who can read that Parable, and
only its signer changes it (Rows about a Parable).
A Pane of a Platform Parable can share its rows across Workspaces, for
example the ideas and votes of a feedback portal. Such a Pane takes Me and
This Parable rows only, and a row that names people, Roles, teams or
Workspaces is refused (PA-AU-066). Every Workspace reads the shared rows and
their count. Only Parable staff, on the Parable’s own line in Dogfood, read
which Workspace saved each row, and no reader reads which person saved it.
A link to another item is a typed reference, Ref<T>. A save checks every link
of a row in one read, as the saver: an item that the saver cannot see is the
same as a missing one (PA-AU-063), and a row links only to items of its own
layer or a wider one (PA-AU-064). Deleting an item that narrower rows link
to gives no warning, and those links then show Unknown. The row
conversion checks the links of each row it converts.
A row is data unless a Policy reads it. A grant in a Page’s or Pane’s
Policies can take its value from one item
of a Pane (fromSetting, { paneKey, itemId }), and a filter can switch on one
(enabledFromSetting). The Policy reads This Parable’s row of that item, else
the Default row. Changing that row also needs permission to edit the Policies
that read it, its value must be a valid grant, and every change is recorded.
A Policy of another Parable can also read the row
(Read another Parable).
Then the Policies of this Pane alone decide who may change the row.
Set the Pane’s own Policies so that only people who may edit those Policies
write it. A save or Prepare after which no active member could edit the
Policies that a row governs is refused (PA-AU-068); a grant that admits
nobody is still allowed.
The first load of every preloading Pane uses shared values for any item that
a Policy reads, just as later reads do. Me and team rows do not change that
item’s value.
A Pane’s code never acts with more rights than its author. A save from Pane
code of a row that a Policy reads, and every proposal, ask the reader to
confirm the change in a dialog that the host draws outside the Pane
(PA-AU-069), and the server refuses the write unless the Pane’s author also
holds the right (PA-AU-070). Removing someone from the Parable’s
participants removes their access to its shared rows on their next request.