← Catalog

List page recipe

How any list page in Bolster is described, and the rules it follows. One set of files serves two readers: this page (the back office, for people) and the bolster-page skill (the front office, for Claude), which explores a live page (or reads a screenshot) and, with a short survey, turns it into a page description. 27 rules, 26 decided and 1 proposed. Generated from system/recipes/list/rules.json and schema.json by scripts/build-rules.mjs; don't edit it by hand.

How a page gets made

Tested on four of Bolster's live lists (jobs, invoices, customers, vendors) and its catalog sheet, Oct 2026: each list became a passing description with 20 to 22 assumptions; the sheet was rightly turned away. Their gaps shaped the fields, types and questions here.

  1. 1 · Screenshot or briefSomeone shares a page from Bolster, or describes one.
  2. 2 · ReadClaude lists what it sees: columns, statuses, cards, toolbar, actions.
  3. 3 · SurveyUp to seven questions for what a picture can't show, each with a default.
  4. 4 · DescribeA page description: data, not drawing. Where the original breaks a rule, the rule wins.
  5. 5 · Checkcheck.mjs holds it to the rules below.
  6. 6 · Renderrecipe.js draws it with the system's components and sample data, and names anything it can't draw yet.

Rules

Enforced byMeans
schemaThe description can't be written any other way: schema.json allows nothing else.
checkcheck.mjs fails the description if it breaks the rule.
rendererThe page's code does it; the description has no say.
surveyThe front office asks, unless exploring the page already answered it.

L-source A source is evidence of meaning, never of design

Decided

A live page, a screenshot, a brief or a PRD is read for what it means: the things on the page, what each knows, how it moves through its life, what people do with it and what they look for first. That reading is written down twice, as the thing's model (system/model/, what exists, shared by every page that shows it) and the page's intent (what this page is for). The page description is then built from those by the rules (build.mjs), never transcribed from the original: how the original draws, places, orders or clicks things is left behind, and only becomes evidence when it says something about meaning (a per-row Send button says a draft's next step is Send). Questions are about meaning only; what the system decides is never asked. Where neither meaning nor a rule decides something (whether a date shows its time of day), the default is the option closest to the existing product; the builder may suggest another, as a suggestion. Identifiers keep the source's format exactly (“#0003”, “#29599975514”): people recognise and search them; the description records it (format) and sample data follows it. When the source shows none, it's a question. A label that puts several values in one line (“Vendor 100001846 #0003”, “Mykola • Job”) is read apart into its fields, each with the thing it belongs to (the vendor's name, the vendor order's own number): that's a requirement, written in the model; how the parts show is the rules'. When the source doesn't say whose a value is, it's a question.

Why: Copying a page's presentation carries its accidents into the system, and every list ends up looking like the page it came from instead of like the system. Reading meaning is also how a PRD will be read, so existing pages and new requirements go through the same door.

survey · Product designer, 15 Oct 2026

L-title The page title is the menu item's name

Decided

A list page's title is the label of the menu item that opens it, word for word; the description names the menu item, never a title.

Why: One source: renaming the menu renames the page, and the selected menu item and the header can't disagree.

schema check · D28

L-row-click A row opens the thing's own window, if it has one

Decided

What clicking a row does is the system's, decided by the thing, never copied from the original. A thing with its own full-screen window (a workspace like the job's, opened by Edit or something like it; the model's workspace) opens it: the row, the name link and Open in the ··· menu (named after it: Open job, with the expand icon) all go there, and the record beside the list is Quick look, second in the ··· menu. Anything else peeks: the record beside the list from 1280 up, a side sheet below. Checkbox clicks never open; the row's name is always a real link.

Why: Where a thing has a window of its own, that's where people work on it: a quick look first is a detour on every row. The record beside the list stays a click away for a glance (for now). Lists of things without one are triaged, and a peek suits them.

renderer · D30, D69, D17, I6, I7, L-source, Product designer, 19 Oct 2026: a thing with its own full-screen window opens it from the row

L-types Every column has a type

Decided

Each column declares one of the types in the types table. The type decides alignment, format, sort direction, the filter picker and where the value sits on a phone; the description never sets those one by one. A value in the name's lines or the record's heading reads as its type does in its column: “5 items”, “$1,900.00”, “Labor and materials”, never a bare number.

Why: One answer per column covers six decisions, so pages come out alike and the survey stays short.

schema renderer check · Tables on phones research (Figma, Oct 2026), Product designer, 14 Oct 2026, Product designer, 15 Oct 2026

L-icons Icons come from the system's vocabulary, one meaning one icon

Decided

Every field (in the filter, sort, group and column menus) and every action (on the record, the bulk bar and in menus) has an icon, and it comes from the vocabulary (system/model/icons.json), never picked for a page: a field by what its key means, else by the record it links to, else by its type; an action by its label, else by its verb; a “Change <field>” action takes the field's icon. One meaning, one icon, everywhere: the pencil is Edit and nothing else. A meaning the vocabulary doesn't cover stops the build: the builder proposes a Font Awesome icon, and it joins the vocabulary once approved. No stand-in icon, ever. A source's own icons aren't copied (L-source); its tooltip words are evidence of meaning.

Why: Icons picked page by page drift: the same action gets different icons, and an unknown one gets whatever's nearest (every new action showed as Edit). A vocabulary keeps them learnable across the product.

check renderer · D58, Product designer, 15 Oct 2026

L-priority Every column has a priority, and one is the name

Decided

Priority 1 is the row's name: exactly one column, type name, never hidden. 2 is the key figure (left unused when the list has none), 3 the status; higher numbers leave first as the screen narrows and beside an open peek. Numbers needn't be consecutive.

Why: What survives at each width is decided once, not per breakpoint. NN/g: lead with a readable name.

schema check · D17, Tables on phones research, Product designer, 14 Oct 2026

L-name-first Rows lead with a name, not a code

Decided

The name column's title starts with words a person recognises (customer, job, item); numbers and codes go to its detail line. A field with its own column in the table isn't repeated on the name's second line: one place per row. On a phone, where the columns go, the row's bottom line shows the first of them (the job), so nothing is lost there.

Why: People read about the first two words of each row (NN/g); “INV-1199 · Lucia Ruiz-Ortega” spends them on the number.

check · D32, Tables on phones research, Product designer, 15 Oct 2026

L-slot The slot before the name shows who or what the row is known by

Proposed

What sits before a row's name is a slot (leading), filled from the model, not from what the original draws there: a thumbnail when the thing has pictures (a placeholder for rows without one); otherwise an avatar when the row is a person or company, or is known by one (its title leads with a customer or vendor: the avatar is theirs); otherwise nothing. An icon only when the model gives the thing one. Never by habit, never because another list has one.

Why: The slot helps a scanning eye find a row by who or what it's about. Filled for any other reason it's decoration, and filled by copying it carries the original's accidents.

check · D55, L-source, Product designer, 15 Oct 2026

L-status-stage Status values take their look from a stage

Decided

A status column lists its values in lifecycle order, each with a stage (new, draft, waiting, won, underway, done, lost, late, neutral). The stage picks the colour: a dot on the status's card, a plain tinted badge in the rows; a list never picks colours itself. The status shapes (dashed, quarter, half, three quarters, slash) are the job list's own and appear nowhere else. When the source colours its statuses, the stage keeps the same colour family (its red is our red, its amber our amber), within what the status means; a colour no fitting stage has is a question. When cards and badges name a status differently (Unsent, Not sent), the badge's word wins.

Why: The same stage looks the same in every list. The shapes were drawn for a job's progress; on another list's statuses they'd promise a meaning they don't have. The label carries the meaning, so colour is never the only signal.

schema renderer · D12, Product designer, 14 Oct 2026: shapes for the job list only, dots elsewhere, Product designer, 15 Oct 2026

L-sample Sample data tells each row's story, from the model

Decided

Sample data is built from the model, one coherent story per row: its status decides the steps it went through (the lifecycle's path, endings from where they come from), the steps decide its dates (in order, never after today), and a field that belongs to a step it didn't reach stays empty (no sent date on a draft, no decline reason on a cancelled order). The record's activity is that story: each step it reached as its event (“Sent to {vendor}”, with when, who and a note where the model has them), then the next step, dashed. The sample covers what testing needs: every status has rows, with zeros and empty fields among them, and one row in twelve carries the longest kind of name (a vendor's full legal name with its trades), so the layout is tested at its worst; ids in the source's format; a category's values only as the model lists them (a waiver that's only been seen as None stays None until someone says what else it can be); reasons and names that read like real ones, and a linked company a real company, never a placeholder such as the catalog's “In-house”; a company's name, its trade and what it provides agree (Cedar Electric does electrical work); what's set aside (cancelled, inactive) is rare, as in a real list. The renderer checks its sample against the lifecycle and says on the page where it doesn't hold.

Why: Sample data that contradicts itself (a declined order that was never sent) hides real problems and invents fake ones; read from the model, it tests the design the way real data will.

renderer · D62, Product designer, 15 Oct 2026, D63

L-summary A summary is KPIs, or the lifecycle

Decided

Above the list is one of two summaries, never tabs. KPIs, the default: the few figures that say how the list is doing and what needs someone (money owed, overdue, paid in the last 30 days, missing documents, a rate), at most four, as a row of cards. A KPI that's a slice of rows filters the list when picked, one at a time, its chip named after it; a calculated one (win rate, collected) doesn't. KPIs may overlap; they don't add up. The lifecycle, when the page is where people move rows through their statuses (its tasks act on two statuses or more: jobs, vendor orders): All, then the statuses in the lifecycle's order, endings included, one card each, adding up to All. An ending counts the last 30 days and says so; a rare ending (lost or set aside, under a tenth of rows) stays in the Status filter unless a figure needs it (Declined, for win rate). One calculated figure may join the row. Either way: money where the key figure is, the count beside it, any window named; a filter the page opens with is its default view; segments (Top customers, Submitted by vendors) are saved views; a list with no KPI worth showing (the catalog) has no summary.

Why: Products split this in two (research board, 19 Oct): lifecycle rows that show every status, endings included (Stripe, Xero, Deel), and KPI figures that cut across them (Mercury, QuickBooks, Midday). A row of work queues did neither: it dropped endings people need (Paid, Declined) and gave a steady state a card. Tabs were ruled out to keep one summary pattern.

schema check · D17, I8, D66, D67, Research: What earns a summary card (Figma, 19 Oct), Product designer, 19 Oct 2026

L-create One create action, named for the noun

Decided

At most one main action: “Create <noun>” for records the business makes, “Add <noun>” for records it keeps (customers, items). In the page header from tablet up; floating above the tab bar on phones.

Why: One primary per view (D4); a name that says what you'll get.

check renderer · D4, D29

L-selection Checkboxes only where there are bulk actions

Decided

Selection is on only when the list has bulk actions, and then the bulk bar replaces the page's floating action. On phones selection is a mode (long press, or Select) rather than a checkbox column. When checkboxes show but the actions don't, the actions stay unresolved until someone says (the checker accepts that while the question is open).

Why: A checkbox column costs every row width; on a phone it buys a rare task.

Survey asks when the page doesn't show it: “I can see checkboxes. What can people do to several {nouns} at once, and is any of it destructive?” Default: Leave the bulk actions unresolved and say so; never invent them.

check renderer survey · D17, Tables on phones research, Bulk actions bar research, Product designer, 14 Oct 2026

L-bulk-slots Three slots on the bulk bar, then More

Decided

The bulk bar shows at most three actions (two from 768 to 1099 wide), in the order the description lists them, most important first; the rest go in More. More never holds just one action: if only one would be left over, the third slot goes into More with it. Delete lives at the end of More, in red after a rule; it comes out onto the bar, as the last slot, only when every action fits without a More. A menu (Change status) is one slot. Phones have their own rule: up to two actions, More, then Delete when the selection allows it; one action fewer while a label wouldn't fit its place.

Why: A bar that grows with the list's actions turns into a toolbar; three slots and More is as many as anyone scans. A phone's bar has four places a quarter of the width each, so there the labels decide.

renderer · D36, Bulk actions bar research

L-risky Risky actions go last and are confirmed

Decided

Destructive actions (tone danger) come last in any list of actions, after a rule, and always ask first; the confirmation's button is named for the outcome (“Delete 3 jobs”) beside a safe way out, focused first.

Why: Nothing irreversible sits where a hurried thumb lands.

check renderer · D4, Secondary, ghost and danger buttons research

L-next-step No buttons on rows: the next step leads the record

Decided

A thing's next step comes from its lifecycle: for each status, the one action that moves it on (Send for a draft, Mark approved while it waits), where there's an obvious one. A per-row button in the original is evidence of one, nothing more. Rows carry no buttons: the step is the record's first action for that status and the first in the row's ··· menu (after Open); everything else a row can do follows it in the record's More and the ··· menu.

Why: Bolster's lists lean on a per-row next step (Pending jobs, Send, Archive). The record, a click away, now does that job: the step stays one click from the row, and the rows stay readable.

Survey asks when the page doesn't show it: “When a {noun} is {status}, what moves it on, if anything?” Default: No next step for a status whose next move isn't plain from the evidence.

schema renderer survey · Dry run on Bolster's lists, Oct 2026, Product designer, 14 Oct 2026: no Next step column; the record replaces it

L-sort One default sort

Decided

Every list declares one default sort on a sortable column. One arrow at a time: a second arrow in an original is noise, not a second sort. People change it from the column headers; that's a view state, not a new default.

Why: The order a list opens in is a decision, not an accident of the database.

Survey asks when the page doesn't show it: “Which order should the list open in?” Default: What people look for first, from the page's intent; otherwise the latest activity, newest first, or the main date if there's no activity column.

schema check survey · Product designer, 14 Oct 2026

L-filters Filters come from columns; three at most are pinned

Decided

Each filter is a column, and its picker follows the column's type. The toolbar shows Add filter and the filters that are applied, never empty ones. At most three are pinned (usually status, owner and a date): they come first in Add filter's menu, the rest after them.

Why: A toolbar of empty filters is a form; one Add filter keeps it calm, and pinning puts the filters people reach for most at the top of its menu.

check · List page layout research, Filter value pickers research, Product designer, 14 Oct 2026, Add filter, then chips (concept round 2, 8 Oct 2026)

L-date-range Lists with a date get a date range

Decided

A list with a date column offers the date range picker on its main date (created, issued, due), even if the original has none: ten presets, then Custom with From and To and two months (D37). Lists without a meaningful date leave it out. Saved views hold a preset id or a span relative to today (..-91 is more than 90 days ago).

Why: “Last 30 days” is the most common question asked of any list.

check survey · List page layout research, Product designer, 14 Oct 2026

L-group Grouping is asked for; status, person and category by default

Decided

The survey asks whether people need to group the list at all; when they don't, Display settings has no Group by. When they do, it offers the list's status, person and category columns by default. Other groupings can be suggested where values repeat across many rows (a customer or vendor, a city, a trade); never by values that differ on nearly every row (names, numbers, amounts, exact dates).

Why: Grouping helps some lists and not others, so it's asked, not assumed. Status, person and category cover most needs; a suggestion can cover the rest, but a group per row isn't a group.

Survey asks every time: “Do people need to group this list? I'd offer {defaults}; {suggestions} could work too, or something else they sort it into piles by.” Default: Grouping offered by the list's status, person and category columns; the list opens ungrouped.

check survey · Product designer, 14 Oct 2026

L-words Counts, empty states and confirmations use the noun

Decided

“132 jobs”, “Jobs you create show up here”, “Delete 3 jobs?”: every sentence about rows is built from the noun, so no list writes its own.

Why: Consistent copy without anyone writing it.

schema renderer · D28

L-explain Explanations use the screen's own words

Decided

A figure's explanation names statuses and labels exactly as the screen shows them: {status} for a status, [Label] for any other label.

Why: The note beside the cards can't say “won” when the cards say Booked.

check · D27

L-phone-row On phones, a row has four corners and an unbroken name

Decided

Below 768 a row is an item, not a table row: the id and a date top left, the key figure top right, the name across the full width (wrapping to two lines, never cut on the first), then the place or description bottom left and the status bottom right. No checkbox column: selecting is a mode (Select, or a long press).

Why: Our phone rows cut 24 of 25 job names; every source says lead with the name and let it wrap.

renderer · D32, Tables on phones research

L-ask Explore the page first, then ask what it can't show

Decided

The front office reads the source for meaning (L-source): on a live page it explores read-only (its menus, filters, a row's record, the bulk bar, every card) for evidence of what things exist, their lifecycle and actions, and what people need. It asks, rather than guesses, only what that evidence can't settle about meaning: what a column holds, what a status means, what people can do to several at once. Never how the page should look or behave: the system decides that. Seven questions at most in one message, most important first; the rest take their defaults and are listed as assumptions. Contradictions in the evidence (a card count the rows contradict) are always worth a question. A value the builder can't name for sure (a bare number on a line, a column of dashes) is always asked, never guessed.

Why: The live page answers most questions a screenshot leaves open (cut-off cards, menu items, what a click opens), and a confident guess that's wrong costs more than a question.

Survey asks when unclear: “Which of these statuses means the work is won, underway, done or lost?” Default: Read from the status names when they're unambiguous (Paid is done, Declined is lost).

survey · Product designer, 14 Oct 2026: explore pages in Chrome rather than describe screenshots

L-case Sentence case everywhere

Decided

Headers, statuses, cards, views and buttons are in sentence case: “In progress”, “Past due”, “Create job”. Names of people, companies and products keep their capitals.

Why: The system writes everything in sentence case; Title Case reads as shouting in a dense table.

check · CLAUDE.md

The survey's questions

What the front office asks when exploring the page can't answer, most important first. Seven at most in one message; the rest take their defaults and are listed for review.

AsksWhenDefaultRule
“Part of the {what} is cut off at the edge. What is it?”when columns or cards run off the edgeLeft out of the model and listed as dropped.L-ask
“I can see {statuses}. Are there others the page doesn't show? Which means won, underway, done or lost?”when statuses are seenOnly the ones seen, with stages read from their names.L-status-stage
“Is this page where people move {things} through their statuses, or where they check how things stand? The first gets a card per status; the second gets a few figures (owed, overdue, paid lately).”when the source has cards or tabs above the list, or the page's tasks act on statusesKPIs, unless the page's tasks act on two statuses or more.L-summary
“What can people do to several {nouns} at once, and is any of it destructive?”when checkboxes show without a bulk barUnresolved: asked, not invented.L-selection
“When a {noun} is {status}, what moves it on, if anything?”when a status's next move isn't evidentNo next step where the evidence doesn't make one plain.L-next-step
“What do you call one row, and what does + make?”when the title names two kinds, or the + has no labelThe broader noun in the title (“customer” for Leads and customers); Add for kept records, Create for made ones.L-words
“What do people look for first on this list: the newest, what's due, what's waiting on them?”when the intent doesn't say what people look for firstAs L-sort says.L-sort
“Do people need to group this list? I'd offer {defaults}; {suggestions} could work too, or something else they sort it into piles by.”always, merged into another questionGrouping offered by the list's status, person and category columns; the list opens ungrouped.L-group
“Is {value} the total, or what's still owed?”when a number has no labelThe total.L-types
“What do people look at first in a {noun}, after its name, and what do they search by?”always, merged into another questionThe fields the original puts first, as evidence of what matters; search by the name's words and the number.L-search
“Do people take this list elsewhere (a spreadsheet, the accountant)?”never on its ownYes when the original offers export (evidence of the need); otherwise no.L-ask

Dropped by design

What an original page may have that a rebuilt one won't, and why.

WhatWhy
A refresh button beside the titleLists stay current on their own; live areas update in place (I13).
A page title that differs from its menu itemL-title.
An icon-only + for createL-create: it says what it makes.
“Search this table…”L-search: it says what it looks in.
Times of day in list cells, except today and yesterdayL-types: the record has the full time.
A second sort arrowL-sort: one sort.
Shell controls (company switcher, “Use old navigation”, global search)The shell, not the page.

Column types

One word per column decides six things (rule L-types).

TypeAlignsShows asSortsFilterOn a phone
namestartTwo lines: title and detail, from the column's templatesA–Znone (search finds it)the name line, full width
textstartAs is, one lineA–Znone, or searchleaves first
idstartAs is, tabular figuresA–Znone (search finds it)overline, top left
moneyend$1,234.56, tabular; the column's empty text when there's nonelargest firstrange (min, max)top right
numberend1,234, tabularlargest firstrangetop right if it's the key figure
percentend12%, tabularlargest firstrangeleaves
countend“3 estimates”, or the column's empty textlargest firstrangeleaves
datestartRelative within a week (“2 days ago”), else 7 Oct; the year only when it isn't this onenewest firstdate presets (last 7, 30, 90 days …)overline, after the id
statusstartBadge with the stage's colour and shapelifecycle ordermulti-select of values, as badgesbottom right
personstartAvatar and nameA–Zmulti-select of people, “Me” firstleaves
categorystartNeutral tagA–Zmulti-select of valuesoverline or bottom left
addressstartStreet, city; one lineA–Z by citymulti-select of citiesbottom left
contactstartEmail over phoneA–Znone (search finds it)leaves
datetimestartLike date, with the time when it's today or yesterday (“Today 7:30pm”); the time otherwise in the record. Chosen when the source shows the time of day (L-source: closest to the existing product)newest firstdate presetsoverline, after the id
durationendWhole units with the column's unit: “40 days”largest firstrangeoverline or leaves
recordstartAvatar (initials of the record) and name; a link to that record. Any linked record: a customer, vendor, quote or jobA–Zmulti-select of records, searchablein the name's title, or leaves
flagsstartUp to two attention badges, then “+n” (“COI missing”, “Licence missing”); nothing when clearmost flags firsthas any, or a chosen flagunder the name
kindsstartIts own column of small icons, one per kind the row holds, in the model's order of kinds, sharing one tooltip that names them together (“Labor and materials”); the record says the same in wordsby kinds heldmulti-select of its kinds, with their icons; a row matches if it holds anythe overline, before the id
imagestartA photo's address, never a column: the name's thumbnail shows it (leading thumbnail, thumbFrom), a rounded square with a placeholder where there's no photo or its link breaksnonenonethe thumbnail down the left, when the list has photos; dropped when none has

The page description

Everything a list page needs, as data. Claude fills this from a screenshot plus a short survey; the renderer turns it into the page; check.mjs holds it to the rules in rules.json. Anything the description doesn't say comes from the rules, never from the page's author.

Page

FieldTakesMeans
recipe required“list”The page type. Only lists for now.
schemaVersion required“1”
nav requiredstringThe menu item that opens this page (its id in the shell's menu). The page title is that item's label (rule L-title), so there is no title field.
noun requiredobjectWhat one row is, in the product's words, lower case. Drives the count (“132 jobs”), the create label, empty states and confirmations.
sourceobjectWhere the description came from, for review.
columns requiredlist of columnsIn reading order, left to right. Hidden columns are offered under Columns in Display settings.
statusobjectThe lifecycle column, if the list has one. Each value takes its colour and shape from its stage (rule L-status-stage).
summaryobjectThe cards above the list: work queues, each a filter and a figure (rule L-summary).
search requiredobject
dateRangeobjectThe date the toolbar's date range picker applies to. Leave out for lists without a meaningful date.
filters requiredlist of filtersWhat “Add filter” offers, in order. The picker comes from the column's type (rule L-types).
sort requiredobjectThe one default sort (rule L-sort).
groupobjectOnly when people need grouping (rule L-group, asked in the survey); without it, Display settings has no Group by.
viewslist of viewsSaved views offered in the views menu. The first is the default.
rowClick requiredpeek · openWhat clicking a row does (rule L-row-click): peek opens the record beside the list (a side sheet below 1280); open goes to the record itself.
selection requiredobjectCheckboxes and the bulk bar (rule L-selection).
createobject or nullThe page's one main action (rule L-create). Null when the list can't create.
exportbooleanWhether the page header offers Export. False unless the original shows it or the person asks.
actionsobjectActions on one row: in the row's ··· menu and the record.
recordobjectWhat the peek (or the record) shows first. Left out: the name, status, key figure, the fields of columns priority 4 and up, and the activity.
fieldslist of fieldsData the page uses but doesn't show as a column: keys for the name's templates, search, the record and notes (client, number, address). Columns are fields too, so don't repeat them here.
unresolvedlist of objectQuestions asked but not answered yet. The checker treats these fields as pending: what would be an error becomes a warning.
droppedlist of objectParts of the original left out, and why (a rule, a duplicate, nothing to map it to).
assumptionslist of assumptionsWhat was assumed rather than seen or answered, so a reviewer can check it.
iconsobjectThe page's icons, resolved from the vocabulary (system/model/icons.json, rule L-icons) by build.mjs: one per column and field (menus: filter, sort, group, columns) and one per action (the record, the bulk bar, menus). Never picked by hand; the checker holds them to the vocabulary.

Column

FieldTakesMeans
key requiredstring
label requiredstringThe header, sentence case.
type requiredname · text · id · money · number · percent · count · duration · date · datetime · status · person · category · address · contact · record · flags · kinds · imageDecides alignment, format, sorting, the filter picker and where the value sits on a phone (rule L-types). record is a linked record (a customer, a vendor) with its avatar; person is a teammate; flags is a set of attention badges (“COI missing”, “Licence missing”), not a lifecycle; image is a photo's address, a field the name's thumbnail shows (thumbFrom), never a column.
priority requiredinteger1 is the row's name (exactly one); 2 the key figure, left unused when the list has none; 3 the status; higher numbers leave first as the screen narrows (rule L-priority). Numbers needn't be consecutive.
hiddenbooleanOff by default; offered under Columns.
titlestringName column only: what the first line shows, with {key} fields from columns or fields, e.g. “{client} · {job}”. Separators only, no literal words (“Invoice #”): the header already says what the row is. A missing value drops with its separator. A column folded into the name stays a hidden column if people filter or sort by it, otherwise it becomes a field.
detailstringName column only: the quieter second line, e.g. “{number} · {address}”.
totalbooleanNumeric columns: show a total under the list.
emptystringWhat an empty cell says (“No price yet”, “Unnamed vendor”); a dash otherwise. On the name column it replaces a missing name, not a placeholder one.
leadingavatar · thumbnail · icon · noneName column only: the slot before the name, holding whatever the page needs there, or nothing (D55). avatar takes initials from avatarFrom, for people and companies; thumbnail is a photo of a thing (an item) from thumbFrom, a placeholder where there's none or its link breaks; icon is the record kind's icon.
thumbFromstringName column only, with leading thumbnail: the image field holding the photo. Leave it out when the product has a place for photos but this list has none yet: every row shows the placeholder.
avatarFromstringName column only: the field whose initials and colour the avatar shows (often the customer). Missing value: the record kind's icon instead.
unitstringcount and duration: the unit noun, singular (“estimate”, “day”), for “3 estimates”, “40 days”.
valueslist of objectA kinds column's kinds (rule L-types): what a row can hold, each drawn as its icon in the column. Their order is the order the icons and their words take: the icons share one tooltip that names them together (“Labor and materials”). A category or flags column's values (no icons): every value it holds, from the model, in its order; sample data and the filter's choices use only these.
formatstringid: how the source writes one, as an example (“#0003”, “#29599975514”). Ids keep the source's format exactly (L-source); sample data follows it.

Status value

FieldTakesMeans
value requiredstring
label requiredstring
stage requirednew · draft · waiting · won · underway · done · lost · late · neutralWhere the value sits in the lifecycle; picks its colour and shape.
derivedstringFor a status worked out from other fields rather than set by people: how, in words (“outstanding and past its due date”). A row that meets it shows this status instead of its stored one, and counts only here; list it right after the status it comes from.
paststringWith derived: the date field that, once before today, gives a row of the status before it this one (due).
eventobjectHow reaching this status reads in a record's activity (L-sample): what happened, when (a date field), who (a person field), and a note (a field, such as a decline reason).
fromlist of stringThe statuses a row can reach this one from. Left out: the one listed before it (the main path); endings name theirs (Declined from Awaiting approval).

Field

FieldTakesMeans
key requiredstring
labelstring
type requiredname · text · id · money · number · percent · count · duration · date · datetime · status · person · category · address · contact · record · flags · kinds · imageDecides alignment, format, sorting, the filter picker and where the value sits on a phone (rule L-types). record is a linked record (a customer, a vendor) with its avatar; person is a teammate; flags is a set of attention badges (“COI missing”, “Licence missing”), not a lifecycle; image is a photo's address, a field the name's thumbnail shows (thumbFrom), never a column.
formatstringid: how the source writes one, as an example (“#0003”, “#29599975514”). Ids keep the source's format exactly (L-source); sample data follows it.

Action

FieldTakesMeans
label requiredstring
tonedangerDestructive: last in its list, always confirmed (rule L-risky).
menulist of stringA submenu's choices.

The concept's lists, described

The four lists in the data list concept, written as descriptions and checked when this page was built. A failure here is the concept breaking a rule, which is the point of checking.

FileWhat it isCheck
concept/catalog.json9 columns · no status · rows peekPasses
concept/customers.json9 columns · 3 statuses · rows peekPasses
concept/invoices.json8 columns · 4 statuses · rows peekPasses
concept/jobs.json8 columns · 5 statuses · rows openPasses