The module file

A module is one file in app/src/modules/, named <id>.json, plus one data file in app/data/. The sidebar entry, the page and the Settings page all come from the module file, so adding the file adds the module and deleting it removes the module. This page is the full format. You do not write these files yourself; it helps to read them when you check what Claude made.

Keys

KeyHoldsRequiredExample
idThe module's web address and its name in files; lower case, words joined by -Yes"accounts" → /accounts
titleThe name in the sidebar and in SettingsYes"Accounts"
blockThe block it is built onYes"datatable"
viewFor a datatable only: "table", "task" or "goal"Datatable"table"
iconThe sidebar mark, by name (see Icons below)Yes"users"
fileIts data file in app/data/; the wiki has noneAll but Wiki"accounts.json"
orderIts place in the sidebar, lowest firstYes25
purposeOne line on what it holds, shown in SettingsYes"The customer accounts I look after."
fieldsFor a datatable only: its fields, in the order the sheet shows themDatatablesee below

A module whose block is not installed is left out of the sidebar rather than breaking the app. The kit's orders run from 10 to 70, with About at 70, so a number such as 25 places a module between People and Goals.

Fields

A field is { "key", "label", "kind", "hint"?, "tab"? }.

PartHolds
keyWhere the value sits in a row in the data file, such as "lastContact"
labelThe column header and the label in the sheet
kindThe control you type in and what is stored (table below)
hintGrey placeholder text in the sheet, saying what belongs there
tabThe tab of the sheet the field sits on

Rules:

  • The first field is name. A row without a name is not saved.
  • Fields on the first tab are the table's columns. Fields on later tabs show only in the row's sheet. With no tabs at all, every field is a column.
  • When you save a row on screen, keys that no field names are dropped.

Field kinds

Every value is stored as plain text, except lines, which is a list.

KindControlStored asExample value
lineOne line of textA string; line breaks collapse to spaces"Head of operations"
textSeveral linesA string, kept as typed"Prefers the numbers a day before the meeting."
linesA list, one entry per lineA list of strings; blank lines drop out["Reporting", "Single sign-on"]
dateA date picker"YYYY-MM-DD""2026-10-07"
statusA choice of fourOne status id; anything else becomes backlog"in-progress"
categoryA choice of fiveOne type id; anything else is dropped"followup"
personA choice from PeopleA People row id"maya-okafor"
numberA text boxA string; a comma reads as a decimal point"38.4"
directionHigher is better or Lower is better"up" or "down"; blank reads as up"down"
periodA picker: Day, Month or Quarter"YYYY-MM-DD", "YYYY-MM" or "YYYY-Q1" to "YYYY-Q4""2026-Q4"
ownerA choice of Me, Team or Company"me", "team" or "company"; anything else is dropped"team"

An empty field is left out of the row, not stored as "". Sorting works on every kind except text and lines.

Status values

StoredShownSort orderOpen or closed
in-progressIn progress1stOpen
blockedBlocked2ndOpen
backlogBacklog3rdOpen
doneDone4thClosed

A new row starts at backlog. Blocked is the darkest pill, so it stands out; Done is the lightest.

Type values (category)

StoredShown
projectProject
deliverableDeliverable
taskTask
requestRequest
followupFollowup

A new row starts as task. The set is fixed; a new type means a code change.

Row ids

Every row has an id, made once from its name when it is first saved: lower case, accents removed, anything else turned into -, at most 48 characters. If the id is taken, a number is added: tallow-co-2. The id never changes, even when you rename the row, because other rows point at it: a task's person holds a People row id.

Icons

icon nameMark
bookAn open book with text (Wiki)
chartA bar chart
compassA compass (About)
layersStacked layers (Tasks)
presentationA presentation board
targetA target (Goals)
usersTwo people (People)

Any other name shows a pilcrow mark (¶) instead of failing. To use a new icon, ask Claude to add it to app/src/lib/module-icons.ts; that is a small code change.

Full example: Accounts

Sam's first module. The module file, app/src/modules/accounts.json:

{
  "id": "accounts",
  "title": "Accounts",
  "block": "datatable",
  "view": "table",
  "icon": "layers",
  "file": "accounts.json",
  "order": 25,
  "purpose": "The customer accounts I look after, and where each one stands.",
  "fields": [
    { "key": "name", "label": "Account", "kind": "line", "tab": "Details" },
    { "key": "plan", "label": "Plan", "kind": "line", "tab": "Details" },
    { "key": "renewal", "label": "Renewal date", "kind": "date", "tab": "Details" },
    { "key": "health", "label": "Health", "kind": "line", "hint": "Green, amber or red", "tab": "Details" },
    { "key": "contact", "label": "Main contact", "kind": "person", "tab": "Details" },
    { "key": "note", "label": "Notes", "kind": "text", "hint": "What to know before the next call", "tab": "Note" }
  ]
}

The data file, app/data/accounts.json:

{
  "items": [
    {
      "id": "tallow-co",
      "name": "Tallow & Co",
      "plan": "Business",
      "renewal": "2026-11-30",
      "health": "Amber",
      "contact": "maya-okafor",
      "note": "Wants the renewal proposal a week before the business review."
    },
    {
      "id": "harbourline-logistics",
      "name": "Harbourline Logistics",
      "plan": "Business",
      "renewal": "2027-03-31",
      "health": "Amber",
      "contact": "daniel-reyes"
    },
    {
      "id": "fernwood-studio",
      "name": "Fernwood Studio",
      "plan": "Starter",
      "health": "Green"
    }
  ]
}

Result: Accounts appears in the sidebar between People and Goals, with five columns (the Details tab) and the notes behind each row's click. To have Claude make it, see Build your first module.