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 subfolderindex.mdcarries no frontmatter; the rootindex.mdcarriesokf_version.
3. Headings and tables
| Level | Meaning |
|---|---|
# | 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 — writea / 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
| Token | Meaning |
|---|---|
| 🟡 | 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.proposalfiles 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*£).