Tracker Standard

The rules for Markdown containers that still hold maintained state: registers, notes and the frozen originals. It does not govern generated views — their generator does — and it does not outrank the contract, which owns the architecture and assigns operational state to SQLite (decision 45A).

The point is still containment: anything a script reads must be predictable, and anything Joe writes must still feel like writing notes.


1. Authority

  • Operational task state lives in SQLite. No Markdown file is a task authority.
  • Evidence files are append-only. Correct them by adding, never by rewriting history.
  • Generated views are reading copies. Change the owner, then regenerate.
  • Narrative notes may be edited freely, but must not restate a structured fact that has an owner elsewhere.

2. Files

  • One subject per file.
  • Every file opens with a title and one line saying what it is for.
  • Area entrances live at <area>/index.md: what belongs in the area, and a link to every file currently in it. A subfolder index.md carries no frontmatter; the root index.md carries okf_version.

3. Headings and tables

LevelMeaning
#File title — exactly one, first line
##Section — a group of related rows
###Sub-table or reference detail inside a section
  • One table per ## section. A second table becomes a ### sub-table.
  • Prose above a table: three lines maximum, no bullet lists.
  • No bold or italic inside cells; cells are data.
  • No | inside a cell — write a / b.
  • No trailing empty column, no empty rows, no duplicated rows.
  • Rows are data, not history: a finished row is removed from an operational table. Log sections keep ✅.

4. Status vocabulary

TokenMeaning
🟡Open
🔵In progress
⏳Waiting on someone else; the chase goes in Next Action
❄️Parked or deferred
🔴Blocked or urgent — see the row’s context
⛔No action needed (life-admin process steps)
⬜Planned, not yet scheduled (checklists)
✅Done — log sections and machine-written history only

Green means in progress and nothing else (decision 41A); deferred work is parked. Never write words in Status, never combine tokens.

5. Dates

  • Human-maintained tables: DD Mon YYYY, zero-padded.
  • Machine-written files: ISO YYYY-MM-DD, because a script will write them again.
  • ~ means approximate and is kept. Month-only and yearless facts keep their precision — never invent a day (decision 22A).
  • A timed reminder marker is ⏰ DD Mon, HH:MM, in Details or Next Action.
  • Only an accepted exact deadline can be overdue. A reminder is not a renewal (21B).

6. Money

  • £1,234.56, with symbol, separators, and two decimals when there are pence.
  • Never 1.2k, never £1,234.5.
  • State the measure: balance, payment, order total, instalment, valuation. An order total is not a payment; a receipt is not proof of payment.
  • Unknown stays unknown — never zero, never carried forward as current.

7. Columns

To-do tables use Status | Task | Details | Next Action. Reference tables name their own columns; the first column is the key and is never empty. Column names are Title Case and mean the same thing everywhere: Details is context, Next Action is the verb.

8. Ownership

  • human — edited by Joe. The normaliser may reformat it.
  • machine — written by a generator. The normaliser reports but never rewrites it, because the next run would undo the change.
  • generated — produced from a single owner with a hash manifest; hand edits become .proposal files and are never overwritten silently.

Planned owners must never be presented as existing ones.

9. Tooling

~/.hermes/scripts/system-validate.py                 # 07:00 structure check
~/.hermes/scripts/tracker-normalise.py --check       # report drift, change nothing
~/.hermes/scripts/tracker-normalise.py --write       # apply, after backing up
python3 scripts/wiki-lint.py                         # links, stubs, orphans, index, OKF
cd simple-tasks && python3 publish.py --check        # stale or hand-edited views

10. Forbidden

  • Writing task state into Markdown.
  • Two independent writers for one fact.
  • Bidirectional sync or last-write-wins between Markdown and SQLite.
  • Deleting evidence to make a count agree.
  • Reopening completed or trashed work because an older file still lists it.

Consumers — normalising can break a parser

tracker-normalise.py strips bold from cells, and several scripts used to identify a row by its bold name (| **SIPP (Freetrade)** | ... |). Stripping it made those lookups return None — loudly (a cron exiting 1) or silently (a dedupe set going empty, a cross-check finding nothing).

Rule: every pattern that identifies a row must treat bold as optional — \|\s*(?:\*\*)?Name(?:\*\*)?\s*\|. Never require ** to locate a row.

Known consumers: tracker_lib.py (net cash, Scottish Widows), daily-finance-refresh.py (SIPP, ISA/GIA writes), daily-tick.py (ISA/GIA fallback), system-validate.py (overview vs platform cross-check), tracker-intake-merge.py (dedupe), kidney-chase.py (coordinator email), ring-reminder.py (writes rows — no bold, so new rows cannot reintroduce drift).

Two traps found while fixing those: an unanchored keyword match also hits prose rows (ISA allowance £1,000), so anchor to ^\| and require the second cell to be a name, not a figure; and (?!£) after \s* is defeated by backtracking — write \|(?!\s*£).